在Django中通过AJAX发布参数时,“ CSRF令牌丢失或不正确”


71

我尝试发布参数像

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

但是,Forbidden 403. CSRF verification failed. Request aborted. 我正在使用Django返回,'django.middleware.csrf.CsrfViewMiddleware'并且找不到如何在不损害安全性的情况下防止此问题。


Answers:


107

您可以通过两种不同的方式发出AJAX发布请求:

  1. 告诉您的视图不要检查csrf令牌。这可以通过使用decorator来完成@csrf_exempt,如下所示:

    from django.views.decorators.csrf import csrf_exempt
    
    @csrf_exempt
    def your_view_name(request):
        ...
    
  2. 要将csrf令牌嵌入每个AJAX请求中,对于jQuery,它可能是:

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

    getCookie函数从cookie检索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

3
使用csrf_exempt装饰器可能会导致安全问题,因为将禁用中间件保护。
J punto Marcos

由于js位于单独的文件中并且无法呈现,{{ csrf_token }}因此第一个选项解决了我的问题。
Alex Jolig

51

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

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

5
如果Django不处理您的JavaScript,该怎么办?猜猜你真的会成为小河。
Naftuli Kay 2011年

4
最初的问题表明,他们正在使用“ django.middleware.csrf.CsrfViewMiddleware”,而Django返回了错误,因此我认为假定Django正在处理ajax请求是非常安全的。
jerrykan 2011年

7
问题在于Django不会对JS进行模板化,而不会对HTML视图进行模板化。
Naftuli Kay 2011年

4
然后在base.htmlwindow.csrftoken="{{csrftoken}}";
airtonix 2013年

2
如果django不处理js,他可以在html中添加一个csrf令牌输入,并使用jquery获取该令牌。添加{{ csrf_token }}表格并获取值,csrf_token = $('input[name="csrfmiddlewaretoken"]').val();并将其与数据一起传递data = {'para1': 'para1_value', csrfmiddlewaretoken: csrf_token};
vikas devde

23

我花了一段时间才了解如何处理Daniel发布的代码。但是实际上您要做的就是将其粘贴到javascript文件的开头。

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

  1. 建立csrf.js档案

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

  3. 引用您需要的模板中的代码

    <script type="text/javascript" src="{{ STATIC_PREFIX }}js/csrf.js"></script>
    

请注意,这STATIC_PREFIX/js/csrf.js指向我的文件。我实际上是用加载STATIC_PREFIX变量{% get_static_prefix as STATIC_PREFIX %}


高级提示:如果您正在使用模板,并且base.html从那里进行扩展,那么您可以从那里引用脚本,而不必担心其余文件。据我了解,这也不代表任何安全问题。


3
这节省了我几个小时的时间。直接从Django文档中获取的简单Pythonic方法。
Pratyush

1
通过the code你的意思是绿色的背景究竟里面的每一个人物?我复制了粘贴内容并按照您的指示进行了操作,但仍然收到403禁止的错误。也许情况已经变了?
Philip007

@ Philip007,是的,绿色背景。他们更改了Django 1.5的文档,但是我看不出结果代码有什么真正的区别。他们只是给出了更长的解释以及使用jQuery的选项。
toto_tico

@ Phillip007,确定您指向正确的js文件src="{{ STATIC_PREFIX }}js/csrft.js"。考虑到STATIC_PREFIX是一个变量。我用设置了这个变量{% get_static_prefix as STATIC_PREFIX %}。但是,请确保src指向正确的位置。
toto_tico

1
哈哈谢谢。我从一开始就注意到了。我不是罪魁祸首:)我通过使用jQuery插件“ jQuery-cookie”解决了这个问题。对我来说,理解起来要容易得多。
Philip007

12

简单而简短

$.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}}');
    }
  }
});

docs


简单而美丽
Shashishekhar Hasabnis,

8

由于缺乏直接答案,您只需将标头添加X-CSRFToken到cookie中的ajax请求中即可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") },
  ...
});

2
千票人.....我总是想知道为什么人们不能像这样简单地回答!
NoobEditor

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总是将令牌添加到cookie中{% csrf_token %}。为了确保即使您不在模板中使用特殊标签,它也总是添加它,请使用@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。


4

谢谢大家的所有答案。我正在使用Django 1.5.1。我参加聚会有点晚了,但是来了。

我找到了Django项目的链接非常有用,但是我真的不想每次要进行Ajax调用时都必须包含额外的JavaScript代码。

我喜欢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 docs)添加到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

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',

});

3

如果不将js嵌入模板,则没有任何插件的最快解决方案是:

<script type="text/javascript"> window.CSRF_TOKEN = "{{ csrf_token }}"; </script>您对script.js文件的引用放在模板中,然后添加csrfmiddlewaretokendata字典中:

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

如果您确实将js嵌入模板,则非常简单: data: {csrfmiddlewaretoken: '{{ csrf_token }}'}


0

如果在阅读其他答案后仍然有人在挣扎,请尝试以下操作:

   $.ajax({
            type: "POST",
            beforeSend: function (request)
            {
                request.setRequestHeader("X-CSRF-TOKEN", "${_csrf.token}");
            },
            url: servlet_path,
            data : data,
            success : function(result) {
            console.log("Success!");
   }
});

我无法按此处所述操作它。有任何想法吗?似乎是因为beforeSend属性未正确获取令牌...?
twknab

如果仍然有人在想,那就是X-CSRFTOKEN,而不是X-CSRF-TOKEN。注意连字符。
Endre双方

0

请不要以这种方式进行操作,请确保没有标签的{% csrf_token %}内部<form></form>。然后按照此处的说明将以下代码添加到您的javascript中

    function getCookie(name) {
    let cookieValue = null;
    if (document.cookie && document.cookie !== '') {
        const cookies = document.cookie.split(';');
        for (let i = 0; i < cookies.length; i++) {
            const cookie = cookies[i].trim();
            // 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;
    }
    const csrftoken = getCookie('csrftoken');

// using js fetch
// https://docs.djangoproject.com/en/3.1/ref/csrf/#setting-the-token-on-the-ajax-request
    const request = new Request(
    /* URL */,
    {headers: {'X-CSRFToken': csrftoken}}
);
fetch(request, {
    method: 'POST',
    mode: 'same-origin'  // Do not send CSRF token to another domain.
}).then(function(response) {
    // ...
});
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.