SessionAuthentication在Tastypie中支持HTTP POST吗?
我可以在使用SessionAuthentication和Tastypie时,进行GET请求,只需要设置一个头信息,就是content-type
为application/json
。但是,进行HTTP POST请求时就不行了,尽管头信息里有包含会话ID的Cookie。这个请求失败了,返回了401授权错误,但这和授权没有关系。把SessionAuthentication换成BasicAuthentication,并传入用户名和密码就可以正常工作。
有没有人成功用Tastypie实现POST请求的SessionAuthentication?
3 个回答
我在tastypie的源代码中发现了这个问题。基本上是说SessionAuthentication不支持HTTP POST请求。
class SessionAuthentication(Authentication):
"""
An authentication mechanism that piggy-backs on Django sessions.
This is useful when the API is talking to Javascript on the same site.
Relies on the user being logged in through the standard Django login
setup.
Requires a valid CSRF token.
"""
def is_authenticated(self, request, **kwargs):
"""
Checks to make sure the user is logged in & has a Django session.
"""
# Cargo-culted from Django 1.3/1.4's ``django/middleware/csrf.py``.
# We can't just use what's there, since the return values will be
# wrong.
# We also can't risk accessing ``request.POST``, which will break with
# the serialized bodies.
if request.method in ('GET', 'HEAD', 'OPTIONS', 'TRACE'):
return request.user.is_authenticated()
所以我在这里回答我自己的问题,但如果有人能更好地解释一下,并给出一个好的解决办法,那就太好了。
编辑
我目前使用的是一个变通方法,来自于这个链接,它基本上是一个自定义的认证方案,能够从cookie中获取session_id
,然后跟后端检查是否已经认证。虽然这可能不是最完美的解决方案,但效果很好。
这里有一些对Dan回答的补充。如果有什么不对的地方,请纠正我,我自己也还有点困惑。
在继续之前,先了解一下Django中的CSRF保护。一定要仔细阅读。你需要把cookie里的token放到请求头的X-CSRFToken
里。如果cookie设置为Httponly,也就是在settings.py
里设置了CSRF-COOKIE-HTTPONLY = True
,那么这个方法就行不通了。那样的话,你就得把cookie嵌入到文档中,这样会带来更多的安全隐患,降低使用Httponly
带来的保护效果。
据我所知,如果cookie不是Httponly
,jQuery会自动设置X-CSRFToken。如果我说错了请纠正我,但我花了好几个小时在这上面测试,这就是我一直得到的结果。这让我想知道,Django文档里的建议有什么意义?这是jQuery的新特性吗?
进一步讨论:
Tastypie在使用会话认证时会禁用CSRF保护,那里有自定义的代码在authentication.py
里。你必须同时传递csrftoken
cookie和X-CSRFToken
头,才能让认证工作(这是Tastypie的要求)。假设在同一个域名下,浏览器会自动传递cookie。jQuery会为你传递头,除非csrftoken
cookie是Httponly。相反,如果cookie是Httponly,我甚至无法手动在$.ajaxSetup{beforeSend...
里设置头。看起来如果csrftoken
cookie是Httponly,jQuery会自动把X-CSRFToken设置为null
。至少我能把头X-CS_RFToken设置为我想要的值,所以我知道我传递的值是正确的。我使用的是jQuery 1.10。
如果你在测试中使用curl
,你需要传递两个cookie(sessionid
和csrftoken
),设置头X-CSRFToken
,如果使用HTTPS协议,还需要设置Referrer
。
是的,我已经让它工作了。你只需要传递CSRF令牌就可以了:
会话认证
这种认证方式使用Django自带的会话功能来检查用户是否已登录。通常在JavaScript与API在同一个网站上时特别有用。
它要求用户必须已经登录,并且有一个活跃的会话。他们还必须有一个有效的CSRF令牌。
在jQuery中,你可以这样做:
// sending a csrftoken with every ajax request
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
crossDomain: false, // obviates need for sameOrigin test
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type)) {
xhr.setRequestHeader("X-CSRFToken", $.cookie('csrftoken'));
}
}
});
$.ajax({
type: "POST",
// ...
注意那部分写着$.cookie('csrftoken')
。它是从Django设置的cookie中获取CSRF令牌的。
更新:
我在Firefox和Opera上遇到了一些问题,Django没有设置cookie。在你的模板中放入模板标签{% csrf_token %}
可以解决这个问题。更好的解决方案可能是使用装饰器ensure_csrf_cookie()
。