我可以通过我的AJAX帖子使用一些符合Django CSRF保护机制的帮助。我遵循了这里的指示:
http://docs.djangoproject.com/en/dev/ref/contrib/csrf/
我已经完全复制了他们在该页面上的AJAX示例代码:
http://docs.djangoproject.com/en/dev/ref/contrib/csrf/#ajax
我在getCookie('csrftoken')
调用之前打印了xhr.setRequestHeader
的内容警报,但确实填充了一些数据。我不确定如何验证令牌是否正确,但我鼓励它发现并发送内容。
但是Django仍在拒绝我的AJAX帖子。
这是我的JavaScript:
$.post("/memorize/", data, function (result) {
if (result != "failure") {
get_random_card();
}
else {
alert("Failed to save card data.");
}
});
这是我从Django看到的错误:
[23 / Feb / 2011 22:08:29]“POST / memorize / HTTP / 1.1”403 2332
我确定我错过了一些东西,也许这很简单,但我不知道它是什么。我在SO周围搜索并看到一些关于通过csrf_exempt
装饰器关闭我的视图的CSRF检查的信息,但我发现它没有吸引力。我已经试过了,但它确实有效,但我宁愿让我的POST以Django的设计方式工作,如果可能的话。
以防它有用,这是我的观点的主旨:
def myview(request):
profile = request.user.profile
if request.method == 'POST':
"""
Process the post...
"""
return HttpResponseRedirect('/memorize/')
else: # request.method == 'GET'
ajax = request.GET.has_key('ajax')
"""
Some irrelevent code...
"""
if ajax:
response = HttpResponse()
profile.get_stack_json(response)
return response
else:
"""
Get data to send along with the content of the page.
"""
return render_to_response('memorize/memorize.html',
""" My data """
context_instance=RequestContext(request))
感谢您的回复!
答案 0 :(得分:168)
真正的解决方案
好的,我设法追查问题了。它位于Javascript(我在下面建议)代码中。
你需要的是:
$.ajaxSetup({
beforeSend: function(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;
}
if (!(/^http:.*/.test(settings.url) || /^https:.*/.test(settings.url))) {
// Only send the token to relative URLs i.e. locally.
xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
}
}
});
而不是官方文档中发布的代码: https://docs.djangoproject.com/en/2.2/ref/csrf/
工作代码来自这个Django条目:http://www.djangoproject.com/weblog/2011/feb/08/security/
所以一般的解决方案是:“使用ajaxSetup处理程序而不是ajaxSend处理程序”。我不知道为什么会这样。但它对我有用:)
上一篇文章(没有回答)
我实际上遇到了同样的问题。
在更新到Django 1.2.5之后发生 - 在Django 1.2.4中没有AJAX POST请求的错误(AJAX没有受到任何方式的保护,但它工作正常)。
就像OP一样,我尝试过在Django文档中发布的JavaScript代码段。我正在使用jQuery 1.5。我也在使用“django.middleware.csrf.CsrfViewMiddleware”中间件。
我试图遵循中间件代码,我知道它失败了:
request_csrf_token = request.META.get('HTTP_X_CSRFTOKEN', '')
然后
if request_csrf_token != csrf_token:
return self._reject(request, REASON_BAD_TOKEN)
这个“if”是真的,因为“request_csrf_token”是空的。
基本上它意味着未设置标头。那个JS系列有什么问题:
xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
我希望提供的详细信息有助于我们解决问题:)
答案 1 :(得分:146)
如果您使用$.ajax
功能,只需在数据正文中添加csrf
令牌即可:
$.ajax({
data: {
somedata: 'somedata',
moredata: 'moredata',
csrfmiddlewaretoken: '{{ csrf_token }}'
},
答案 2 :(得分:72)
将此行添加到您的jQuery代码中:
$.ajaxSetup({
data: {csrfmiddlewaretoken: '{{ csrf_token }}' },
});
并完成。
答案 3 :(得分:14)
问题是因为django期望cookie的值作为表单数据的一部分传回。上一个答案中的代码是使用javascript来查找cookie值并将其放入表单数据中。从技术的角度来看,这是一种可爱的方式,但看起来确实有些冗长。
过去,我通过让javascript将令牌值放入帖子数据中来做得更简单。
如果您在模板中使用{%csrf_token%},您将获得一个带有值的隐藏表单字段。但是,如果你使用{{csrf_token}},你将只获得令牌的裸值,所以你可以在这样的javascript中使用它....
csrf_token = "{{ csrf_token }}";
然后,您可以在哈希中包含所需的密钥名称,然后将其作为数据提交给ajax调用。
答案 4 :(得分:13)
{% csrf_token %}
放入<form></form>
转换为:
<input type='hidden' name='csrfmiddlewaretoken' value='Sdgrw2HfynbFgPcZ5sjaoAI5zsMZ4wZR' />
所以为什么不在你的JS中像这样grep它:
token = $("#change_password-form").find('input[name=csrfmiddlewaretoken]').val()
然后传递它,例如做一些POST,如:
$.post( "/panel/change_password/", {foo: bar, csrfmiddlewaretoken: token}, function(data){
console.log(data);
});
答案 5 :(得分:8)
如果您的表单在没有JS的Django中正确发布,您应该能够使用ajax逐步增强它,而不会有任何黑客或杂乱的csrf令牌传递。只需序列化整个表单,这将自动获取所有表单字段包括隐藏的csrf字段:
$('#myForm').submit(function(){
var action = $(this).attr('action');
var that = $(this);
$.ajax({
url: action,
type: 'POST',
data: that.serialize()
,success: function(data){
console.log('Success!');
}
});
return false;
});
我用Django 1.3+和jQuery 1.5+测试了这个。显然,这适用于任何HTML表单,而不仅仅是Django应用程序。
答案 6 :(得分:7)
非jquery回答:
var csrfcookie = function() {
var cookieValue = null,
name = 'csrftoken';
if (document.cookie && document.cookie !== '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = cookies[i].trim();
if (cookie.substring(0, name.length + 1) == (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
};
用法:
var request = new XMLHttpRequest();
request.open('POST', url, true);
request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
request.setRequestHeader('X-CSRFToken', csrfcookie());
request.onload = callback;
request.send(data);
答案 7 :(得分:5)
接受的答案很可能是红鲱鱼。 Django 1.2.4和1.2.5之间的区别是对AJAX请求的CSRF令牌的要求。
我在Django 1.3上遇到过这个问题,并且首先由于没有设置CSRF cookie而导致 。 Django除非必须,否则不会设置cookie。因此,在Django 1.2.4上运行的独占或大量的ajax站点可能永远不会向客户端发送令牌,然后需要令牌的升级将导致403错误。
理想的解决方法是:
http://docs.djangoproject.com/en/dev/ref/contrib/csrf/#page-uses-ajax-without-any-html-form
但你必须等待1.4,除非这只是文档追赶代码
<强> 修改 强>
另请注意,后来的Django文档会注意到jQuery 1.5中的错误,因此请确保使用1.5.1或更高版本的Django建议代码: http://docs.djangoproject.com/en/1.3/ref/contrib/csrf/#ajax
答案 8 :(得分:4)
将Firefox与Firebug一起使用。在触发ajax请求时打开“控制台”选项卡。使用DEBUG=True
,您可以获得漂亮的django错误页面作为响应,您甚至可以在控制台选项卡中看到呈现的ajax响应的html。
然后你会知道错误是什么。
答案 9 :(得分:3)
似乎没有人提到如何使用X-CSRFToken
标头和{{ csrf_token }}
在纯JS中执行此操作,因此这里是一个简单的解决方案,您无需搜索Cookie或DOM:
var xhttp = new XMLHttpRequest();
xhttp.open("POST", url, true);
xhttp.setRequestHeader("X-CSRFToken", "{{ csrf_token }}");
xhttp.send();
答案 10 :(得分:2)
我刚刚遇到了一些不同但相似的情况。不是100%肯定它是否是你的案例的解决方案,但我通过设置一个POST参数'csrfmiddlewaretoken'与正确的cookie值字符串解决了Django 1.3的问题,该字符串通常由您的家庭HTML形式由Django的表单返回带有'{%csrf_token%}'标签的模板系统。我没有尝试使用较旧的Django,只是发生了并在Django1.3上解决了。 我的问题是,从表单中通过Ajax提交的第一个请求已成功完成,但第二次尝试从完全相同的失败,导致403状态,即使标题'X-CSRFToken'正确放置了CSRF令牌值也是如此就像第一次尝试的情况一样。 希望这会有所帮助。
此致
纮
答案 11 :(得分:2)
你可以将这个js粘贴到你的html文件中,记得把它放在其他js函数之前
<script>
// using jQuery
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 csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$(document).ready(function() {
var csrftoken = getCookie('csrftoken');
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
});
</script>
答案 12 :(得分:1)
(2020年10月26日)
在我看来,这比正确的答案要简洁得多。
@login_required
def some_view(request):
"""Returns a json response to an ajax call. (request.user is available in view)"""
# Fetch the attributes from the request body
data_attribute = request.GET.get('some_attribute') # Make sure to use POST/GET correctly
# DO SOMETHING...
return JsonResponse(data={}, status=200)
urlpatterns = [
path('some-view-does-something/', views.some_view, name='doing-something'),
]
ajax调用非常简单,但是在大多数情况下就足够了。您可以获取一些值并将其放入数据对象中,然后在上述视图中通过其名称再次获取它们的值。
您可以在django's documentation中找到csrftoken函数。基本上,只需复制它并确保在您的Ajax调用之前将其渲染,以便定义 csrftoken变量。
$.ajax({
url: "{% url 'doing-something' %}",
headers: {'X-CSRFToken': csrftoken},
data: {'some_attribute': some_value},
type: "GET",
dataType: 'json',
success: function (data) {
if (data) {
console.log(data);
// call function to do something with data
process_data_function(data);
}
}
});
这可能是个话题,但我很少见到使用它,这是一种最小化窗口重定位以及在javascript中手动创建html字符串的好方法。
这与上面的非常相似,但是这次我们从响应中渲染html,而无需重新加载当前窗口。
如果您打算从作为对Ajax调用的响应而接收的数据中呈现某种html,则从视图发送回HttpResponse(而不是JsonResponse)可能会更容易。这样一来,您就可以轻松创建html,然后可以将其插入到元素中。
# The login required part is of course optional
@login_required
def create_some_html(request):
"""In this particular example we are filtering some model by a constraint sent in by
ajax and creating html to send back for those models who match the search"""
# Fetch the attributes from the request body (sent in ajax data)
search_input = request.GET.get('search_input')
# Get some data that we want to render to the template
if search_input:
data = MyModel.objects.filter(name__contains=search_input) # Example
else:
data = []
# Creating an html string using template and some data
html_response = render_to_string('path/to/creation_template.html', context = {'models': data})
return HttpResponse(html_response, status=200)
creation_template.html
{% for model in models %}
<li class="xyz">{{ model.name }}</li>
{% endfor %}
urlpatterns = [
path('get-html/', views.create_some_html, name='get-html'),
]
这是我们要将数据添加到的模板。特别是在此示例中,我们有一个搜索输入和一个按钮,该按钮将搜索输入的值发送到视图。然后该视图发送一个HttpResponse返回,显示与我们可以在元素内呈现的搜索匹配的数据。
{% extends 'base.html' %}
{% load static %}
{% block content %}
<input id="search-input" placeholder="Type something..." value="">
<button id="add-html-button" class="btn btn-primary">Add Html</button>
<ul id="add-html-here">
<!-- This is where we want to render new html -->
</ul>
{% end block %}
{% block extra_js %}
<script>
// When button is pressed fetch inner html of ul
$("#add-html-button").on('click', function (e){
e.preventDefault();
let search_input = $('#search-input').val();
let target_element = $('#add-html-here');
$.ajax({
url: "{% url 'get-html' %}",
headers: {'X-CSRFToken': csrftoken},
data: {'search_input': search_input},
type: "GET",
dataType: 'html',
success: function (data) {
if (data) {
/* You could also use json here to get multiple html to
render in different places */
console.log(data);
// Add the http response to element
target_element.html(data);
}
}
});
})
</script>
{% endblock %}
答案 13 :(得分:1)
如果有人和axios一起努力做这项工作,这对我有所帮助:
import axios from 'axios';
axios.defaults.xsrfCookieName = 'csrftoken'
axios.defaults.xsrfHeaderName = 'X-CSRFToken'
来源:https://cbuelter.wordpress.com/2017/04/10/django-csrf-with-axios/
答案 14 :(得分:1)
1)django csrf检查(假设您正在发送)是here
2)在我的情况下,settings.CSRF_HEADER_NAME
设置为&#39; HTTP_X_CSRFTOKEN&#39;我的AJAX调用发送了一个名为&#39; HTTP_X_CSRF_TOKEN&#39;所以东西不起作用。我可以在AJAX调用或django设置中更改它。
3)如果您选择在服务器端进行更改,请找到django的安装位置并在csrf middleware
中抛出一个断点。如果您正在使用virtualenv
,那么&#39;我会像:~/.envs/my-project/lib/python2.7/site-packages/django/middleware/csrf.py
import ipdb; ipdb.set_trace() # breakpoint!!
if request_csrf_token == "":
# Fall back to X-CSRFToken, to make things easier for AJAX,
# and possible for PUT/DELETE.
request_csrf_token = request.META.get(settings.CSRF_HEADER_NAME, '')
然后,确保{request {USD}
正确地获取csrf
令牌
4)如果您需要更改标题等 - 在设置文件中更改该变量
答案 15 :(得分:1)
为每个会话分配一个CSRF令牌(即每次登录时)。 因此,在您希望获取用户输入的某些数据并将其作为ajax调用发送到受csrf_protect装饰器保护的某个函数之前,请尝试在从用户获取此数据之前查找正在调用的函数。例如。必须呈现某些模板,用户在该模板上输入数据。 该模板由某些函数呈现。 在此函数中,您可以获取csrf令牌,如下所示: csrf = request.COOKIES [&#39; csrftoken&#39;] 现在在上下文字典中传递此csrf值,以便对正在呈现的模板进行渲染。 现在在该模板中写下这一行: 现在在你的javascript函数中,在发出ajax请求之前,写下: var csrf = $(&#39;#csrf&#39;)。val()这将选择传递给模板的令牌值并将其存储在变量csrf中。 现在在进行ajax调用时,在你的帖子数据中,也传递这个值: &#34; csrfmiddlewaretoken&#34;:csrf
即使您没有实现django表单,这也会有效。
事实上,这里的逻辑是:你需要你可以从请求获得的令牌。 所以你只需要在登录后立即找出被调用的函数。一旦你有了这个令牌,要么再做一个ajax调用来获取它,或者把它传递给你的ajax可以访问的模板。
答案 16 :(得分:0)
这是Django提供的一个不那么详细的解决方案:
<script type="text/javascript">
// using jQuery
var csrftoken = jQuery("[name=csrfmiddlewaretoken]").val();
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
// set csrf header
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
// Ajax call here
$.ajax({
url:"{% url 'members:saveAccount' %}",
data: fd,
processData: false,
contentType: false,
type: 'POST',
success: function(data) {
alert(data);
}
});
</script>
答案 17 :(得分:0)
由于当前答案中未在任何地方说明,因此,如果您未将 js嵌入模板中,最快的解决方案是:
将<script type="text/javascript"> window.CSRF_TOKEN = "{{ csrf_token }}"; </script>
放在模板中对script.js文件的引用之前,然后将csrfmiddlewaretoken
添加到js文件的data
字典中:
$.ajax({
type: 'POST',
url: somepathname + "do_it/",
data: {csrfmiddlewaretoken: window.CSRF_TOKEN},
success: function() {
console.log("Success!");
}
})
答案 18 :(得分:0)
与chosen Answer相关,只想添加到所选答案中。
在该答案中,关于使用.ajaxSetup(...)
的解决方案。在您的Django settings.py中,如果有
CSRF_USE_SESSIONS = True
这将导致所选答案完全无效。实施所选答案的解决方案时,删除该行或将其设置为False对我有用。
有趣的是,如果您在Django settings.py中设置了以下内容
CSRF_COOKIE_HTTPONLY = True
此变量不会导致所选答案的解决方案停止运行。
CSRF_USE_SESSIONS
和CSRF_COOKIE_HTTPONLY
都来自这个官方Django文档https://docs.djangoproject.com/en/2.2/ref/csrf/
(我没有足够的代表来评论,所以我将输入的内容发布为答案)
答案 19 :(得分:0)
我有解决办法。在我的JS中,我有两个功能。 首先获得Cookies(即csrftoken):
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;
}
第二个是我的ajax函数。在这种情况下,它是用于登录的,实际上不返回任何东西,只需传递值以设置会话即可:
function LoginAjax() {
//get scrftoken:
const csrftoken = getCookie('csrftoken');
var req = new XMLHttpRequest();
var userName = document.getElementById("Login-Username");
var password = document.getElementById("Login-Password");
req.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
//read response loggedIn JSON show me if user logged in or not
var respond = JSON.parse(this.responseText);
alert(respond.loggedIn);
}
}
req.open("POST", "login", true);
//following header set scrftoken to resolve problem
req.setRequestHeader('X-CSRFToken', csrftoken);
req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
req.send("UserName=" + userName.value + "&Password=" + password.value);
}
答案 20 :(得分:0)
使用Django 3.1.1和我尝试过的所有解决方案均失败。但是,将“ csrfmiddlewaretoken”键添加到我的POST正文中是可行的。这是我打的电话:
$.post(url, {
csrfmiddlewaretoken: window.CSRF_TOKEN,
method: "POST",
data: JSON.stringify(data),
dataType: 'JSON',
});
在HTML模板中:
<script type="text/javascript">
window.CSRF_TOKEN = "{{ csrf_token }}";
</script>
答案 21 :(得分:0)
在我的情况下,问题出在nginx配置中,我已经从主服务器复制到临时的配置,并禁用了https,而在此过程中第二个不需要。
我必须在配置中注释掉这两行以使其再次起作用:
# uwsgi_param UWSGI_SCHEME https;
# uwsgi_pass_header X_FORWARDED_PROTO;