【问题标题】:Django/JQuery CSRF: 403 ErrorDjango/JQuery CSRF: 403 错误
【发布时间】:2013-03-21 05:13:03
【问题描述】:

我不断收到来自 Django 的 403 错误。

我设置了我的 settings.py 以使用 CSRF 保护,并在我的模板中使用了 csrf_token 令牌。

这是我在 HTML 标头之后包含的 JS 文件:http://bpaste.net/show/87791/

使用 Firebug 我可以检查 CSRF cookie 是否存在。稍后在页面上,用户单击触发此代码的按钮:

myFunction: function() {
  $.ajax({
  type: 'POST',
  url: window.localtion.href + 'myajaxview',
  async: false
  });
}

我正在使用从 TemplateView 继承的基于类的简单视图来显示此页面。 'myajaxview' 继承自 View 和 JSON Mixin。但是它的代码永远不会执行,因为 django 无法验证 CSRF 令牌。

在我看来,ajax 并没有按照应有的方式发送带有 POST 标头的令牌。还是我错过了什么?

编辑:我在调用 $.ajax() POST 函数之前移动了 $.ajaxSetup 调用,它起作用了。我试图将它移到其他地方,但失败了。我认为这个问题与 Ajax 的关系比与 Django 的关系更大。 所以,我的问题仍然存在,我不想把 $.ajaxSetup 调用放在每个 $.ajax 调用之前,我不认为这是事情的处理方式,我不想重复自己。所以这只是一种解决方法,我正在寻求解决方案。

【问题讨论】:

  • 你可能想看看 csrf_exempt here
  • 已经看到了,如果你在 bpaste 上查看我的代码,它几乎是一样的。不同之处在于我将其包含为外部 .js 文件。因为我想共享该功能,并且每次在每个模板中执行 ajax 请求时都不包含相同的代码。也许这就是问题?使用 ajax 时将 csrf_token 放在哪里?以及将 getCookie 代码放在哪里(即:仅当我做错时)?

标签: jquery django csrf django-csrf


【解决方案1】:

我通过在 DOM 准备好并重新加载 AJAX 时计算令牌的值来解决问题。

javascript 代码使用 reload_ajax() 函数来处理令牌,以及在初始页面加载和后续 AJAX 调用时我需要刷新的任何其他内容。

<script type="text/javascript">

function reload_ajax() {
    // Useful on initial page load, and after calling AJAX.
    $("input#token").val("{{ csrf_token }}");
}

$(document).ready(function() {
    $("form#stats").unbind("submit");      // Prevents calling document-ready multiple times
    $("form#stats").submit(function(event) {
        event.preventDefault();
        $.ajax({
            type:"POST",
            url: $(this).attr("action"),
            data: $(this).serialize(),     // Serialize the form
            dataType: "json",
            success: function(response){
                $("#stats_ajax").html(response.html);  // Object to refresh after AJAX 
                reload_ajax();
            }
        });
        return false;
    });

    reload_ajax();
});
</script>

我正在使用两个 HTML 文件, 在 main.html 中,我有上面的 javascript 代码,以及以下内容:

<div id="stats_ajax">
    {% include "stats_ajax.html" %}
</div>

在 stats_ajax.html 中,我有实际的表单(加上我需要刷新的其他内容)

<form id="stats" action="/main/" method="post">
    <!-- The value will be inserted when the DOM reloads. -->
    <input id="token" type="hidden" name="csrfmiddlewaretoken" value="" />
    <!-- Other input elements -->
    <button type="submit">Submit</button>
</form>

在我的views.py文件中,

# Not all imports may be needed for this example
from django.http import HttpResponse
from django.shortcuts import render, redirect
from django.template.loader import render_to_string
import json

def main(request):
    # This request should only be posting AJAX
    if request.is_ajax():
        data = {}
        data["message"] = "Awesome"
        # This is the template to load on the DOM object to update
        html = render_to_string("stats_ajax.html", data)
        res = {"html": html}
        return HttpResponse(json.dumps(res), mimetype='application/json')

    # Handle GET to this view
    return redirect("/main")

最后,我的 urls.py 有了

# Replace "application_name" with your own value
url(r'^main', 'application_name.views.main'),

【讨论】:

  • 一切都像你一样,但是错误((禁止(403)CSRF验证失败。请求中止。“GET /main HTTP/1.1”200 1111“POST /main HTTP/1.1”403 2294跨度>
【解决方案2】:

感谢您的帮助。答案真的很讨厌,我从一个简单的例子开始测试 ajaxSetup + ajax 就发现了,然后我添加了更多的复杂性来匹配我的原始代码。

这是因为 jquery-tools。当我开始使用 bootstrap-twitter 时,我看到他们建议将 javascript 包含在页面末尾。所以我把 jquery-tools.js 也包括在内。我几乎不知道在这个脚本文件中对 $.ajaxSetup 的调用覆盖了我自己的。

解决方案是将 jquery 工具包含在顶层。但是在这一点上,我不确定它会与我的代码有多少冲突。因为我需要为每个 ajax 请求设置 ajaxSetup。

我花了一天的时间才发现,我在 #jquery 和 #django 上,很多人不遗余力地试图找出解决方案。如果您遇到无法共享的复杂代码库以及想要解决的问题,我的建议是:尝试使最简单的示例正常工作并更改它,直到它与您失败的设置相匹配。这将节省每个人的时间,尤其是您自己的时间。

【讨论】:

    【解决方案3】:
    myFunction: function() {
      $.ajax({
      type: 'POST',
      data: { 
        'csrfmiddlewaretoken': '{{csrf_token}}'
      }, 
      url: '/myajaxview/',
      async: false
      });
    }
    

    【讨论】:

    • 这就像聚光灯的答案,我猜它可以工作并且会帮助其他人,但我尝试按照文档告诉它的方式进行操作:在每个 XMLHttpRequest 上,将自定义 X-CSRFToken 标头设置为值CSRF 令牌。这就是为什么我与 beforeSend()..
    • 现在可以了。我认为最难的部分是确保 beforeSend() 不会被库覆盖,尤其是在使用异步包含时。然而它现在对我有用(见我的回答)。不过感谢您的意见,我可能仍会在某些情况下使用它!
    【解决方案4】:

    这是我认为您正在寻找的代码的 sn-p

    var csrfToken = $('input[name="csrfmiddlewaretoken"]').val();
    $.ajax({
        url: 'blah.com/',
        //snipped for brevity
        csrfmiddlewaretoken: csrfToken,
        success: function(data){ doStuff(); }
    });
    

    这是我为了能够对 django 应用程序进行 jquery ajax 调用而使用的。 希望它有效!

    【讨论】:

    • 我很感激。我可以使用 getCookie() 代替您的方法来获取 csrfToken 吗?但是,我仍然对为什么 xfer.setRequestHeader() 在 beforeSend() 中不起作用感到困惑。如果我让它工作,那么我不需要手动添加 csrfmiddlewaretoken。
    • 您可以使用它来获取 csrf 令牌,但 xhr.beforesend 设置对我也不起作用。我相信这是因为使用动态加载的表单或 dom 元素时没有正确生成 csrf_token。我会在今天晚些时候研究它并为您提供更新:)
    • 看起来效果不太好,虽然它在官方 django 文档中。我想我找到了一个解决方案让它发挥作用,但我不确定我是否解决了每个人的问题,因为我的问题很具体。
    【解决方案5】:

    首先,您是否检查了 CSRF 代码是否实际发送到服务器?如果是,服务器是否希望散列在 post 值或 header 中?现在您设置请求标头。 如果两者都不能解决问题,请验证服务器是否知道正确的哈希。当客户端发送正确的哈希并且服务器将 NULL 作为哈希时,验证当然会失败。

    【讨论】:

    • csrf 代码在 cookie 中。但是根据 JS 代码,它应该在请求头中,它不是。所以我会说CSRF代码没有发送到服务器,这是我怀疑但我不知道为什么。
    • 您确认 cookie 'csrftoken' 存在吗?
    • 它存在于网页和 cookie 中。但不在 POST 请求的 HTTP 标头中。我用一些额外的信息更新了我的问题。
    猜你喜欢
    • 2015-03-31
    • 2021-02-13
    • 2018-04-24
    • 2011-09-06
    • 2011-12-10
    • 2013-05-25
    • 2014-08-31
    • 2011-11-30
    • 2016-07-01
    相关资源
    最近更新 更多