在Django中通过AJAX提交参数时出现“CSRF token missing or incorrect”错误

85

我试图像这样提交参数:

 jQuery.ajax(
        {
            'type': 'POST',
            'url': url,
            'contentType': 'application/json',
            'data': "{content:'xxx'}",
            'dataType': 'json',
            'success': rateReviewResult 
        }
    );

然而,Django返回Forbidden 403. CSRF verification failed. Request aborted.我正在使用'django.middleware.csrf.CsrfViewMiddleware',但找不到如何在不牺牲安全性的情况下解决此问题。


https://dev59.com/UG435IYBdhLWcg3w-1V_ - Ciro Santilli OurBigBook.com
14个回答

123

你可以通过两种不同的方式进行AJAX post请求:

  1. 告诉你的视图不要检查csrf令牌。这可以通过使用装饰器@csrf_exempt来实现,像这样:

    from django.views.decorators.csrf import csrf_exempt
    
    @csrf_exempt
    def your_view_name(request):
        ...
    
  2. 为了在每个 AJAX 请求中嵌入 CSRF 令牌,对于 jQuery 可以这样做:

  3. $(function () {
        $.ajaxSetup({
            headers: { "X-CSRFToken": getCookie("csrftoken") }
        });
    });
    

    getCookie函数从cookies中获取csrf令牌。我使用以下实现:

    function getCookie(c_name)
    {
        if (document.cookie.length > 0)
        {
            c_start = document.cookie.indexOf(c_name + "=");
            if (c_start != -1)
            {
                c_start = c_start + c_name.length + 1;
                c_end = document.cookie.indexOf(";", c_start);
                if (c_end == -1) c_end = document.cookie.length;
                return unescape(document.cookie.substring(c_start,c_end));
            }
        }
        return "";
     }
    

    此外,jQuery有一个插件可以访问cookie,类似于这样:

    // set cookie
    $.cookie('cookiename', 'cookievalue');
    // read cookie
    var myCookie = $.cookie('cookiename');
    // delete cookie
    $.cookie('cookiename', null);
    

7
需要注意的是,常用的是 X-CSRFToken 而不是 X-CSRF-Token - shangxiao
4
使用csrf_exempt装饰器可能会带来安全问题,因为中间件保护将被禁用。 - J punto Marcos
由于js代码位于单独的文件中,无法渲染 {{ csrf_token }} ,因此第一个选项解决了我的问题。 - Ghasem

59

我发现最简单的方法是在数据中包含{{csrf_token}}值:

jQuery.ajax(
    {
        'type': 'POST',
        'url': url,
        'contentType': 'application/json',
        'data': {
            'content': 'xxx',
            'csrfmiddlewaretoken': '{{ csrf_token }}',
        },
        'dataType': 'json',
        'success': rateReviewResult 
    }
);

5
如果您的JavaScript没有被Django处理,那该怎么办?猜测你真的会陷入困境。 - Naftuli Kay
4
原始问题说明他们正在使用 'django.middleware.csrf.CsrfViewMiddleware',而Django返回了错误,因此我认为可以非常安全地假设Django正在处理ajax请求。 - jerrykan
7
问题在于Django没有将JS转换为模板格式,仅仅对HTML视图进行了模板化处理。 - Naftuli Kay
4
然后在base.html中执行以下操作:window.csrftoken="{{csrftoken}}"; - airtonix
当我尝试这样做时,我会得到一个jquery.js:8423未捕获的RangeError:超出最大调用堆栈大小的错误! - Laurent
2
他可以在HTML中添加一个CSRF令牌输入,并使用jQuery获取该令牌(如果JS未被Django处理)。在表单中添加{{ csrf_token }},并通过csrf_token = $('input[name="csrfmiddlewaretoken"]').val();获取其值,然后将其与数据一起传递data = {'para1': 'para1_value', csrfmiddlewaretoken: csrf_token}; - vikas devde

26

我花了一些时间才理解如何处理丹尼尔发布的代码。但实际上,你只需要将它粘贴到javascript文件的开头即可。

对我来说,目前最好的解决方案是:

  1. 创建一个csrf.js文件。

  2. 代码粘贴到csrf.js文件中。

  3. 在需要使用的模板中引用该代码。

<script type="text/javascript" src="{{ STATIC_PREFIX }}js/csrf.js"></script>
注意 STATIC_PREFIX/js/csrf.js 指向我的文件。我实际上是使用 {% get_static_prefix as STATIC_PREFIX %} 加载了 STATIC_PREFIX 变量。
高级技巧:如果您正在使用模板并且有类似于 base.html 的模板,那么您可以直接从那里引用脚本,就不必再在其他文件中担心这个问题了。据我所知,这应该也不会造成任何安全问题。

1
你所说的“代码”,是指绿色背景内的每个字符吗?我复制粘贴了它,并按照您的指示操作,但仍然出现403禁止错误。也许事情已经改变了? - Philip007
@Phillip007,你确定你指向了正确的js文件src="{{ STATIC_PREFIX }}js/csrft.js"吗?请注意,STATIC_PREFIX是一个变量。我使用{% get_static_prefix as STATIC_PREFIX %}设置了这个变量。但是请确保src指向正确的位置。 - toto_tico
@Philip007,我刚刚意识到src行中有一个错误。它写成了csrft.js而不是csrf.js。如果这就是错误的话,那会很有趣,因为这个答案已经得到了一些赞 :P。 - toto_tico
1
哈哈,谢谢。我一开始就注意到了。这不是我的问题的罪魁祸首 :) 我通过使用jQuery插件“jQuery-cookie”解决了这个问题。对我来说更容易理解。 - Philip007
@Phillip007,太好了!如果你仍在使用csrf.js,请随意编辑我的答案。我猜最好将整个代码粘贴到Pastebin上,因为自Django 1.5以来,方法已经改变了。 - toto_tico
显示剩余2条评论

21

简单易懂

$.ajaxSetup({
  headers: { "X-CSRFToken": '{{csrf_token}}' }
});
function csrfSafeMethod(method) {
  // these HTTP methods do not require CSRF protection
  return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
  beforeSend: function(xhr, settings) {
    if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
      xhr.setRequestHeader("X-CSRFToken", '{{csrf_token}}');
    }
  }
});

文档


9

缺乏直接的答案,您只需将头文件X-CSRFToken添加到ajax请求中,该请求位于cookie csrftoken中。 JQuery没有处理cookie(出于某种原因),除非使用插件,因此:

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-cookie/1.4.1/jquery.cookie.min.js"></script>

最小的代码更改如下:

$.ajax({
  headers: { "X-CSRFToken": $.cookie("csrftoken") },
  ...
});

6

如果您不将js嵌入模板中,则无需使用插件即可快速解决问题:

在模板中引用script.js文件之前添加<script type="text/javascript"> window.CSRF_TOKEN = "{{ csrf_token }}"; </script>,然后将csrfmiddlewaretoken添加到您的data字典中。

$.ajax({
            type: 'POST',
            url: somepathname + "do_it/",
            data: {csrfmiddlewaretoken: window.CSRF_TOKEN},
            success: function() {
                console.log("Success!");
            }
        })

如果你将js嵌入模板中,只需简单地这样做:data:{csrfmiddlewaretoken:'{{ csrf_token }}'}

4

在请求中包含x-csrftoken头:

var token = $('input[name="csrfmiddlewaretoken"]').prop('value');
jQuery.ajax({
    type: 'POST',
    url: url,
    beforeSend : function(jqXHR, settings) {
        jqXHR.setRequestHeader("x-csrftoken", get_the_csrf_token_from_cookie());
    },
    data: data,
    dataType: 'json',

});

4
感谢大家的回答。我使用的是Django 1.5.1版本。虽然有些晚,但还是来说说我的看法。
我发现Django项目对我很有用,但我不想每次都要加入额外的JavaScript代码来进行Ajax调用。
我喜欢jerrykan的回答,因为它非常简洁,只在普通的Ajax调用中添加了一行代码。针对他下面评论中提到的当Django模板标签不可用时的情况,可以从DOM中加载csrfmiddlewaretoken。
var token = $('input[name="csrfmiddlewaretoken"]').prop('value');
jQuery.ajax({
    type: 'POST',
    url: url,
    data: { 'csrfmiddlewaretoken': token },
    dataType: 'json',
    success: function(data) { console.log('Yippee! ' + data); } 
});

2016年3月更新

我在过去几年中对这个问题的处理方式已经改变。我将下面的代码(来自Django文档)添加到一个main.js文件中,并在每个页面上加载它。一旦完成,您就不需要再次担心使用ajax时的CSRF令牌问题。

function getCookie(name) {
    var cookieValue = null;
    if (document.cookie && document.cookie != '') {
        var cookies = document.cookie.split(';');
        for (var i = 0; i < cookies.length; i++) {
            var cookie = jQuery.trim(cookies[i]);
            // Does this cookie string begin with the name we want?
            if (cookie.substring(0, name.length + 1) == (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}
var csrftoken = getCookie('csrftoken');


4

昨天我遇到了同样的问题,想着如果有一种简单的方法来处理它会对人们有所帮助,所以我写了一个jQuery插件: jquery.djangocsrf. 它不需要在每个请求中添加CSRF令牌,而是将自身挂钩在AjaxSend jQuery事件上并将客户端cookie添加到标头中。

以下是使用方法:

1- 引入它:

<script src="path/to/jquery.js"></script>
<script src="path/to/jquery.cookie.js"></script>
<script src="path/to/jquery.djangocsrf.js"></script>

2- 在你的代码中启用它:

$.djangocsrf( "enable" );

Django会在使用{% csrf_token %}的模板中添加令牌到cookie中。为了确保即使您的模板中没有使用特殊标签也始终添加令牌,请使用@ensure_csrf_cookie修饰符:

from django.views.decorators.csrf import ensure_csrf_cookie

@ensure_csrf_cookie
def my_view(request):
    return render(request, 'mytemplate.html')

注意:我正在使用Django 1.6.2。

0
HTML
<form action="">
        {% csrf_token %}
    </form>

JS

 <script>   
     const csrftoken = document.querySelector('[name=csrfmiddlewaretoken]').value;
              const request = new Request(
                   'url_here',
                  {headers: {'X-CSRFToken': csrftoken}}
              );
              fetch(request, {
                  method: 'POST',
                  // mode: 'same-origin' optinal // Do not send CSRF token to another domain.
              }).then(function(response) {
                  console.log(response);
              });
    
    </script>

更多详细信息的参考链接


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接