jQuery Ajax API 兼容 Django csrf 策略配置

关于 Django csrf 的旧的理解

关于 Django 的 csrf 问题,早就已经碰到过了,以往的了解是,假如对 Django 请求一个 POST 请求的时候,Django 的 view 会要求验证一个 csrf 的令牌,而这个令牌在 cookie 中就放了有。

例如:

# views.py
def update(request):
    return HTTPResponse(json.dumps(request.POST))

假如定义了一个这样的视图,配置 url 为:http://example.com/update/

那么当我们发送一个 post AJAX 请求过去,或者直接提交一个 POST 表单:

$.post('http://example.com/update', {data: 'mydata'});

这样的话会得到一个 403 的错误码响应,提示没有 csrf 令牌。

于是我们可以将 csrf_token 拿到,放在 payload 里面,名称为 csrfmiddlewatetoken,服务器就会接受:

$.post('http://example.com/update', {
    data: 'mydata', 
    csrfmiddlewatetoken: Cookie.get('csrftoken')
});

又或者我们可以通过 csrf_exempt 设定这个 view 不做 csrf 校验(但这明显是掩耳盗铃,不是正确解决问题的方式):

# views.py
@csrf_exempt
def update(request):
    return HTTPResponse(json.dumps(request.POST))

一般而言,只要在 POST 的时候把 csrfmiddlewatetoken 加进 payload 就万事大吉了。

那么问题来了

但是由于现在用 RESTful 架构,发现在使用 PUT 方法的时候,即使在 payload 里面放了 csrftoken,但是照样返回 403。

然后到 stackoverflow 里面找答案,找到这个问题:

http://stackoverflow.com/q/22497492/2544762

貌似是 Django REST Framework 的作者回答的,还是查文档,原来这篇官方 django 文档写得很清楚:

https://docs.djangoproject.com/en/dev/ref/csrf/#ajax

简而言之,其实 csrftoken 使可以通过 HTTP 头的 X-CSRFToken 传过去的(那就不用再在 payload 里面加了)。

于是有官方这段脚本:

function csrfSafeMethod(method) {
    // these HTTP methods do not require CSRF protection
    return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}

jQuery.ajaxSetup({
    beforeSend: function(xhr, settings) {
        if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
            xhr.setRequestHeader("X-CSRFToken", csrftoken);
        }
    }
});

完美解决问题!我可以把手动往 payload 里面加 token 的逻辑全部删掉了!

注意上面 Cookie 这个库是需要引入一个库的:https://github.com/js-cookie/js-cookie/

OK,大功告成,Good Luck!


后记 2015-09-27

实际操作时发现,如果使用 ajaxSetup 修改 beforeSend 设置,这种设置方式在 jQuery 中称为:Local Events

这样会导致一个问题,如果在 $.ajax 调用的时候指定了一个新的 beforeSend 设置值,这将覆盖掉 $.ajaxSetup 中预先设定的那一个。

为此,我还专门在 StackOverflow 研究了这个问题:http://stackoverflow.com/a/32804309/2544762

所以,更为合理的办法是使用 $(document).ajaxSendGlobal Event 中修改 xhr 头:

$(document).ajaxSend(function(event, xhr, settings) {
    if (!/^(GET|HEAD|OPTIONS|TRACE)$/.test(settings.type) 
            && !settings.crossDomain) {
        xhr.setRequestHeader("X-CSRFToken", Cookies.get('csrftoken'));
    }
}

【转载请附】愿以此功德,回向 >>

原文链接:https://www.huangwenchao.com.cn/2015/09/django-ajax-csrf.html【jQuery Ajax API 兼容 Django csrf 策略配置】

发表评论

电子邮件地址不会被公开。 必填项已用*标注