我正在使用httpie来玩我在django 1.7和django rest framework 2.4中编写的api。 今天我试图删除一个对象:
$ http DELETE :8000/api/items/8/ --verbose
DELETE /api/items/8/ HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate, compress
Content-Length: 0
Host: 127.0.0.1:8000
User-Agent: HTTPie/0.8.0
HTTP/1.0 204 NO CONTENT
Allow: GET, PUT, PATCH, DELETE, HEAD, OPTIONS
Content-Language: cs
Content-Length: 0
Date: Wed, 07 Jan 2015 21:47:06 GMT
Server: WSGIServer/0.1 Python/2.7.6
Vary: Accept, Accept-Language, Cookie
即使它需要CSRF令牌,但哪个成功。当我尝试使用以下代码从Chrome中删除对象时:
$.ajax({
type: "DELETE",
url: "http://127.0.0.1:8000/api/items/6/"
});
我收到了以下请求:
DELETE /api/items/6/ HTTP/1.1
Host: 127.0.0.1:8000
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
Accept: */*
Origin: http://127.0.0.1:8000
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36
DNT: 1
Referer: http://127.0.0.1:8000/inventory
Accept-Encoding: gzip, deflate, sdch
Accept-Language: cs,en-US;q=0.8,en;q=0.6,es;q=0.4,pt;q=0.2,sk;q=0.2
Cookie: cc_csrf=bd9fbbc8f75cffa2e1e3d2c95c2185c5; _ga=GA1.1.2038400685.1386436341; __utma=96992031.2038400685.1386436341.1417173095.1417428975.79; __utmz=96992031.1409752584.3.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); __zlcmid=MpdRtV3vZuf3D9; djdt=hide; sessionid=kiihjh6m77jm8v9ol7xrryip89sny55i; csrftoken=FtnnEWPLhMh0CAGMRMH77nB0AAno93uW
响应:
HTTP/1.0 403 FORBIDDEN
Date: Wed, 07 Jan 2015 21:57:40 GMT
Server: WSGIServer/0.1 Python/2.7.6
Vary: Accept, Accept-Language, Cookie
Content-Type: application/json
Content-Language: en
Allow: GET, PUT, PATCH, DELETE, HEAD, OPTIONS
{"detail": "CSRF Failed: CSRF token missing or incorrect."}
我的设置:
REST_FRAMEWORK = {
# Use hyperlinked styles by default.
# Only used if the `serializer_class` attribute is not set on a view.
'DEFAULT_MODEL_SERIALIZER_CLASS': 'rest_framework.serializers.HyperlinkedModelSerializer',
# Use Django's standard `django.contrib.auth` permissions,
# or allow read-only access for unauthenticated users.
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'
],
'DEFAULT_FILTER_BACKENDS': ('rest_framework.filters.DjangoFilterBackend',),
'DATETIME_FORMAT': "%B %d, %Y"
}
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.locale.LocaleMiddleware',
'debug_toolbar.middleware.DebugToolbarMiddleware',
)
所以我的问题是:用JS ajax发送DELETE请求和用http发送请求有什么区别?
答案 0 :(得分:5)
这是因为CSRF检查仅在使用SessionAuthentication
进行身份验证时执行,(即使用sessionid
设置的django.contrib.auth
Cookie):
如果您正在使用
SessionAuthentication
,则需要为任何POST
,PUT
,PATCH
或{{1}添加有效的CSRF令牌操作。 (source)
我假设你为HTTPie发送的请求使用另一个auth方法,因此CSRF检查不会在那里应用。
https://github.com/tomchristie/django-rest-framework/blob/master/tests/test_authentication.py
答案 1 :(得分:2)
当通过浏览器发出请求时,它会在sessionid
标题中包含Cookie
标记。 This header is automatically set by the browser,并包含已设置的其他Cookie(如Django调试工具栏的djdt=hide
)。
Cookie: ...; sessionid=kiihjh6m77jm8v9ol7xrryip89sny55i; ...
因此,Django is authenticating the request automatically(就像通常那样),这是Django REST框架提供的triggering the SessionAuthentication
。 SessionAuthentication
要求验证CSRF令牌,该令牌包含在csrftoken
Cookie和X-CSRFToken
标头中,以确保不会发生任何可疑行为。
这意味着您必须设置X-CSRFToken
标题when making your request in the browser。 Django在their documentation on CSRF中为热门库提供了一些有用的代码片段。
现在,在通过HTTPie发出请求时,您通常使用不同形式的身份验证such as basic authentication。默认情况下,Django REST框架支持BasicAuthentication
和SessionAuhentication
,unless you override them,大多数文档都希望您使用基本身份验证。
HTTPie通过-a username:password
参数支持基本身份验证。这可以解释为什么在发出DELETE
请求时没有收到任何权限问题,因为如果没有身份验证,您应该收到403
错误。 The DjangoModelPermissionsOrAnonReadOnly
should not allow您无需经过身份验证即可提出您提供的请求。
答案 2 :(得分:1)
除了其他人已经提到的解释之外,我们可以总结出Httpie允许你的DELETE而不是Javascript的原因:
1)由于您实际上已经禁用了您的身份验证,理论上所有METHODS都将允许来自单独的HTTP调用,因此您的Httpie可以工作(就像您使用Curl时一样),因为Restframework不需要您。
2)来自Javascript的Ajax调用,但稍有不同,因为您使用浏览器控制台进行调用,实际上是在浏览器会话中。此外,您的cookie已存储您之前的GET的CSRF令牌,之后当您执行Ajax调用时,CSRF令牌已从Django / Restframework中提取而不是MATCH(因为CSRF令牌将在每个请求时自动重新生成所以这是 INCORRECT 的问题,而不是* MISSING **令牌。
因此,就像在上面的评论中一样,删除浏览器的cookie /使用私有会话确实解决了问题,并成功地允许您执行Ajax样式DELETE。
希望这会有所帮助,并感谢每个人的指导和提示,使我得出这个结论。