2013-03-30 71 views
0

我一直从Django获得403错误。Django/JQuery CSRF:403错误

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

这里是JS文件,我只是HTML头之后包括: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应用那样发送POST标头。或者我错过了什么?

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

+0

你可能想看看csrf_exempt [这里](https://docs.djangoproject.com/en/1.2/ref/contrib/csrf/) –

+0

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

回答

0

谢谢你的帮助。答案是非常讨厌的,我通过一个简单的例子来测试ajaxSetup + ajax,然后增加了更多的复杂度来匹配我的原始代码。

这是因为jquery工具。当我开始使用bootstrap-twitter时,我看到他们建议将JavaScript包含在页面末尾。所以我把jquery-tools.js包括在内。我不知道在这个脚本文件中调用了$ .ajaxSetup是否超出了我自己的范围。

解决的办法是把jquery工具包括在顶层。然而在这一点上,我不确定它会与我的代码有多大的冲突。因为我需要为每个Ajax请求设置ajaxSetup。

我花了一天的时间才知道,我在#jquery和#django上,很多人试图找出解决方案。如果你有一个复杂的代码库,你不能共享,并且你想解决一个问题,这里是我的建议:试着让最简单的例子工作,并改变它,直到它匹配你失败的设置。它会节省每个人的时间,尤其是你自己的时间。

0

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

+0

csrf代码位于cookie中。但根据JS代码,它应该在请求头部,它不是。所以我会说CSRF代码不会发送到服务器,这是我怀疑的,但我不知道为什么。 – Aki

+0

您是否验证cookie'csrftoken'是否存在? – f4der

+0

它存在于网页和cookies中。但不在POST请求的HTTP标头中。我用一些额外的信息更新了我的问题。 – Aki

2

这是代码的片段,我认为你正在寻找

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

这是我为了用什么可以做的jQuery AJAX调用一个Django应用程序。 希望它的作品!

+0

我很感激。我可以使用getCookie()而不是你的方法来获得csrfToken吗?但是,仍然困惑于为什么xfer.setRequestHeader()在beforeSend()中不起作用。如果我让它工作,那么我不需要手动添加csrfmiddlewaretoken。 – Aki

+0

你可以用它来获得csrf标记,但是xhr.beforesend设置对我来说也不起作用。我相信这是因为使用动态加载的表单或dom元素时,csrf_token没有正确生成。我会在今天晚些时候看到它,并为您提供更新:) –

+0

看起来它不能很好地工作,虽然它在官方的django文档上。我想我找到了一个解决方案,使其工作,但我不知道我解决了每个人的问题,因为我的具体情况。 – Aki

3
myFunction: function() { 
    $.ajax({ 
    type: 'POST', 
    data: { 
    'csrfmiddlewaretoken': '{{csrf_token}}' 
    }, 
    url: '/myajaxview/', 
    async: false 
    }); 
} 
+0

这就像limelights的答案,它的工作原理我猜并且会帮助其他人,但我试图按照文档告诉它的方式来做:在每个XMLHttpRequest上,将自定义X-CSRFToken标头设置为CSRF标记的值。这就是为什么我与beforeSend()..奋斗 – Aki

+0

它现在的作品。我认为最难的部分是确保beforeSend()不被库重写,特别是在使用异步包含时。但现在它适用于我(请参阅我的答案)。感谢您的输入,但我仍然可能在某些情况下使用它! – Aki

0

我解决了这个问题,只要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'), 
+0

做了所有事情,但错误((Forbidden(403) CSRF验证失败。请求中止。“GET/main HTTP/1.1”200 1111 “POST/main HTTP/1.1”403 2294 – Olga