Dan*_*gen 5 testing django django-csrf django-rest-framework
我正在使用Django Rest Framework 3并想测试CSRF验证.
首先,我初始化DRF APIClient
:
client = APIClient(enforce_csrf_checks=True)
Run Code Online (Sandbox Code Playgroud)
然后我在用户上设置密码,这样我就可以登录并获得一个会话:
superuser.set_password('1234')
superuser.save()
client.login(email=superuser.email, password='1234')
Run Code Online (Sandbox Code Playgroud)
现在我们需要一个CSRF令牌.为此,我只需创建一个请求并从cookie中检索令牌.
response = client.request()
csrftoken = client.cookies['csrftoken'].value
Run Code Online (Sandbox Code Playgroud)
在检查代码时,这似乎有效,我找回了一个有效的CSRF令牌.然后我做POST请求,传入csrfmiddlewartoken
参数:
data = {'name': 'My fancy test report', 'csrfmiddlewaretoken': csrftoken}
response = client.post(API_BASE + '/reports', data=data, format='json')
assert response.status_code == status.HTTP_201_CREATED, response.content
Run Code Online (Sandbox Code Playgroud)
问题是,这失败了:
tests/api/test_api.py:156: in test_csrf_success
assert response.status_code == status.HTTP_201_CREATED, response.content
E AssertionError: {"detail":"CSRF Failed: CSRF token missing or incorrect."}
E assert 403 == 201
E + where 403 = <rest_framework.response.Response object at 0x7f7bd6453bd0>.status_code
E + and 201 = status.HTTP_201_CREATED
Run Code Online (Sandbox Code Playgroud)
使用DRF测试CSRF验证的正确方法是什么?
编辑
所以,在研究了一下之后,我发现了以下内容:
Django不一定在标头中设置CSRF令牌,除非它呈现明确包含csrf_token
模板标签的模板.这意味着您需要请求一个使用csrf令牌呈现表单的页面,或者您需要创建一个装饰的令牌请求视图ensure_csrf_cookie
.
由于csrf令牌在每个会话中是唯一的,因此可以创建类似于以下内容的通用令牌设置视图:
from django.views.decorators.csrf import ensure_csrf_cookie
@ensure_csrf_cookie
def token_security(request):
return HttpResponse() # json or whatever
Run Code Online (Sandbox Code Playgroud)
然后,只要您希望POST到CSRF保护的端点并且cookie中没有CSRF令牌,就针对此视图发出GET,并且应该设置cookie,然后可以将其用于POST.
原答案如下:
以下工作在我的测试中(我使用工厂创建User对象,但您可以手动创建它们):
class TestLoginApi(APITestCase):
def setUp(self):
self.client = APIClient(enforce_csrf_checks=True)
self.path = reverse("registration:login")
self.user = UserFactory()
def tearDown(self):
self.client.logout()
def _get_token(self, url, data):
resp = self.client.get(url)
data['csrfmiddlewaretoken'] = resp.cookies['csrftoken'].value
return data
def test_login(self):
data = {'username': self.user.username,
'password': PASSWORD}
data = self._get_token(self.path, data)
# This should log us in.
# The client should re-use its cookies, but if we're using the
# `requests` library or something, we'd have to re-use cookies manually.
resp = self.client.post(self.path, data=data)
self.assertEqual(resp.status_code, 200)
etc.
Run Code Online (Sandbox Code Playgroud)
如果这一切都是动态完成的,你还必须确保你的视图在GET上设置一个cookie,因为根据Django文档(参见警告),如果你没有从模板中回发,它将不会被自动设置.有这个{% csrf_token %}
集合.
如果你需要设置它,看起来像这样(在你的DRF views.py中):
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import ensure_csrf_cookie
@method_decorator(ensure_csrf_cookie)
def get(self, request, *args, **kwargs):
return SomeJson...
Run Code Online (Sandbox Code Playgroud)
最后,对于我的Django Rest Framework视图,我必须确保POST也受csrf保护(但这看起来不像你遇到的问题):
from django.views.decorators.csrf import csrf_protect
@method_decorator(csrf_protect)
def post(self, request, *args, **kwargs):
return SomeJson...
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
2043 次 |
最近记录: |