【问题标题】:jQuery AJAX POST skips Django CSRF token header on first calljQuery AJAX POST 在第一次调用时跳过 Django CSRF 令牌标头
【发布时间】:2011-10-19 07:23:34
【问题描述】:

使用 jQuery AJAX POST 调用 Django 以获取更新的 JSON 集。

在我的 JS 文件中包含 Django 应用程序的“django.middleware.csrf.CsrfViewMiddleware”和“django.middleware.csrf.CsrfResponseMiddleware”以及来自https://docs.djangoproject.com/en/dev/ref/contrib/csrf/#ajax 的代码。

$(document).ajaxSend(function(event, xhr, settings) {
    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;
    }
    function sameOrigin(url) {
    // url could be relative or scheme relative or absolute
    var host = document.location.host; // host + port
    var protocol = document.location.protocol;
    var sr_origin = '//' + host;
    var origin = protocol + sr_origin;
    // Allow absolute or scheme relative URLs to same origin
    return (url == origin || url.slice(0, origin.length + 1) == origin + '/') ||
        (url == sr_origin || url.slice(0, sr_origin.length + 1) == sr_origin + '/') ||
        // or any other URL that isn't scheme relative or absolute i.e relative.
        !(/^(\/\/|http:|https:).*/.test(url));
    }
    function safeMethod(method) {
    return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
    }

    if (!safeMethod(settings.type) && sameOrigin(settings.url)) {
    xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
    }
});

会发生什么

  1. 清除浏览器 cookie 并加载页面。
  2. Django 以 403 禁止响应 AJAX POST 请求,然后使用 CSRF 设置一个 cookie。但是没有 JSON 数据。
  3. 再次触发 AJAX POST 请求
  4. Django 从 X-CSRFToken 标头接收 CSRF 令牌,并使用 JSON 集进行适当的响应。

产生 ajax 请求的 html 和 js 文件不是 django 产生的。 js 通过 jquery $.post() 与 django 交互。所以获取 csrf 令牌的唯一方法是在发送 post 数据之前调用 django。

在我看来,Django 文档中给出的 jQuery 函数解决方案还没有本地 cookie,并且在提交 POST 请求之前没有等待从服务器取回 cookie。但是当它与 403 一起使用时,它使用(现在本地存储的)cookie 进行后续尝试并且工作正常。

如何解决此问题,以便提前设置 cookie 并在 AJAX POST 请求中第一次使用该 cookie?我是否错误地认为 django csrf 可以严格从源自同一站点的 ajax 调用工作?


*此处与 CSRF 有关的其他问题根本不起作用,建议使用 ajax.Setup 而不是 ajaxSend,但根据此 https://code.djangoproject.com/ticket/15284?cversion=0&cnum_hist=6 ,这可能会导致其他 JS 出现问题,因此选择了 ajaxSend。*

【问题讨论】:

    标签: jquery ajax django csrf


    【解决方案1】:

    看起来中间件类的放置顺序错误。要获得正确答案需要更多信息,请在 settings.py 中提供 MIDDLEWARE_CLASSES。

    将中间件“django.middleware.csrf.CsrfViewMiddleware”添加到您的 中间件类列表,MIDDLEWARE_CLASSES。 (它应该来了 在任何假设 CSRF 攻击已经被攻击的视图中间件之前 处理。)

    或者,您可以使用装饰器 csrf_protect() 您想要保护的视图(见下文)。

    来源:How to use CSRF protection

    【讨论】:

    • MIDDLEWARE_CLASSES = ('django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.middleware.csrf.CsrfResponseMiddleware', 'django.contrib.sessions.middleware. SessionMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', )
    • 从 django 的角度来看,csrf 中间件似乎工作正常,因为第一个 ajax 请求从 django 获取令牌,第二个请求使用令牌并且 django 很高兴。
    • 实际上CSRF cookie必须在第一步设置,当页面加载时
    • 包含 AJAX 的页面不是在 Django 中构建的。它是直接的 HTML(尽管我在 Django 中重建它来处理这个问题和其他问题)虽然据我了解,这不重要。 AJAX 调用应该能够在没有任何整页请求的情况下获取和读取 Django 提供的 cookie。似乎脚本在发送发布数据之前没有从远程获取 cookie。它只是在寻找一个在第一次请求时还不存在的本地 cookie。似乎需要的是在将 POST 数据发送到 Django 之前使 CSRF 值成为必需
    • 这是有道理的,因为 Django 正是从那种请求中提供了安全性。当 Django 返回使用 ajax 调用的页面的响应时,必须设置 cookie。虽然您可以使用@csrf_exempt 来禁用对处理 AJAX 的视图的 csrf 检查,或者将直接的 HTML 页面实现到视图中。
    猜你喜欢
    • 1970-01-01
    • 2017-12-05
    • 2018-08-09
    • 1970-01-01
    • 2019-01-13
    • 2018-02-10
    • 2012-01-26
    • 2018-01-26
    • 2018-07-10
    相关资源
    最近更新 更多