我可以通过我的 AJAX 帖子使用一些帮助来遵守 Django 的 CSRF 保护机制。我已按照此处的说明进行操作:
http://docs.djangoproject.com/en/dev/ref/contrib/csrf/
我已经完全复制了他们在该页面上的 AJAX 示例代码:
http://docs.djangoproject.com/en/dev/ref/contrib/csrf/#ajax
我在 xhr.setRequestHeader
调用之前放置了一个打印 getCookie('csrftoken')
内容的警报,它确实填充了一些数据。我不确定如何验证令牌是否正确,但我很高兴它正在查找和发送一些东西。
但是 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))
感谢您的回复!
如果您使用 $.ajax
函数,您只需在数据正文中添加 csrf
标记即可:
$.ajax({
data: {
somedata: 'somedata',
moredata: 'moredata',
csrfmiddlewaretoken: '{{ csrf_token }}'
},
真正的解决方案
好的,我设法追查问题。它位于 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)
这个“如果”是真的,因为“request_csrf_token”是空的。
基本上这意味着没有设置标题。那么这条JS线有什么问题吗:
xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
?
我希望提供的详细信息将帮助我们解决问题:)
将此行添加到您的 jQuery 代码中:
$.ajaxSetup({
data: {csrfmiddlewaretoken: '{{ csrf_token }}' },
});
并做了。
CSRF Failed: CSRF token missing or incorrect.
{% csrf_token %}
,即使没有表单标签。没有它,至少对我来说,会发生 403 错误。有关更多信息,请参阅:stackoverflow.com/a/65757544/7076615,我还建议在 Django 中使用 X-editable,它非常好,并且在该线程中是一个完整的示例。
问题是因为 django 期望 cookie 中的值作为表单数据的一部分传回。上一个答案中的代码是让 javascript 找出 cookie 值并将其放入表单数据中。从技术角度来看,这是一种可爱的方式,但它看起来确实有点冗长。
过去,我通过让 javascript 将令牌值放入 post 数据来更简单地完成此操作。
如果您在模板中使用 {% csrf_token %},您将获得一个隐藏的表单字段,其中包含该值。但是,如果你使用 {{ csrf_token }} 你只会得到令牌的裸值,所以你可以像这样在javascript中使用它......
csrf_token = "{{ csrf_token }}";
然后你可以在哈希中包含所需的键名,然后将其作为数据提交给 ajax 调用。
window
对象中设置了 register django variables,以便之后可以访问它们。即使在静态文件中。
{% csrf_token %}
在 <form></form>
中放入 html 模板
翻译成类似的东西:
<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);
});
非 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);
似乎没有人提到如何在纯 JS 中使用 X-CSRFToken
标头和 {{ csrf_token }}
来做到这一点,所以这里有一个简单的解决方案,您不需要搜索 cookie 或 DOM:
var xhttp = new XMLHttpRequest();
xhttp.open("POST", url, true);
xhttp.setRequestHeader("X-CSRFToken", "{{ csrf_token }}");
xhttp.send();
如果您的表单在没有 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 应用程序。
公认的答案很可能是红鲱鱼。 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 建议的代码:https://docs.djangoproject.com/en/dev/ref/csrf/#ajax
ensure_csrf_cookie
的装饰器,您可以包裹视图以确保它发送 cookie。
csrftoken
cookie,谢谢!
将 Firefox 与 Firebug 一起使用。在触发 ajax 请求时打开“控制台”选项卡。使用 DEBUG=True
,您可以获得漂亮的 django 错误页面作为响应,您甚至可以在控制台选项卡中看到呈现的 ajax 响应的 html。
然后你就会知道错误是什么。
由于当前答案中的任何地方都没有说明,因此如果您没有将 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!");
}
})
我刚刚遇到了一些不同但相似的情况。不是 100% 确定它是否能解决您的问题,但我通过使用正确的 cookie 值字符串设置 POST 参数“csrfmiddlewaretoken”解决了 Django 1.3 的问题,该字符串通常由 Django 以您的家庭 HTML 的形式返回带有“{% csrf_token %}”标签的模板系统。我没有尝试使用较旧的 Django,只是在 Django1.3 上发生并解决了。我的问题是,通过 Ajax 从表单提交的第一个请求已成功完成,但来自完全相同的第二次尝试失败,导致 403 状态,即使标题“X-CSRFToken”也正确放置了 CSRF 令牌值就像第一次尝试的情况一样。希望这可以帮助。
问候,
广
你可以把这个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>
<script type="text/javascript"> window.CSRF_TOKEN = "{{ csrf_token }}"; </script>
有效
使用 Django 轻松调用 ajax
(26.10.2020) 在我看来,这比正确答案更清晰、更简单。
风景
@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)
网址.py
urlpatterns = [
path('some-view-does-something/', views.some_view, name='doing-something'),
]
ajax 调用
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);
}
}
});
使用ajax将HTML添加到当前页面
这可能有点离题,但我很少看到它被使用,它是最小化窗口重定位以及在 javascript 中手动创建 html 字符串的好方法。
这与上面的非常相似,但这次我们从响应中渲染 html 而不重新加载当前窗口。
如果您打算从您将收到的数据中呈现某种 html 作为对 ajax 调用的响应,则从视图发送回 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)
视图的html创建模板
创建模板.html
{% for model in models %}
<li class="xyz">{{ model.name }}</li>
{% endfor %}
网址.py
urlpatterns = [
path('get-html/', views.create_some_html, name='get-html'),
]
主模板和ajax调用
这是我们要向其中添加数据的模板。在这个例子中,我们有一个搜索输入和一个将搜索输入的值发送到视图的按钮。然后视图发送一个 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 %}
在我的情况下,问题出在我从主服务器复制到临时服务器的 nginx 配置上,其中禁用了进程中第二个不需要的 https。
我不得不在配置中注释掉这两行以使其再次工作:
# uwsgi_param UWSGI_SCHEME https;
# uwsgi_pass_header X_FORWARDED_PROTO;
一个 CSRF 令牌分配给每个会话(即每次您登录时)。因此,在您希望获取用户输入的一些数据并将其作为 ajax 调用发送到受 csrf_protect 装饰器保护的某个函数之前,请尝试在从用户获取此数据之前找到正在调用的函数。例如,必须呈现一些模板,您的用户正在其上输入数据。该模板正在由某个函数呈现。在此函数中,您可以获得 csrf 令牌,如下所示: csrf = request.COOKIES['csrftoken'] 现在将这个 csrf 值传递到上下文字典中,针对哪个模板正在呈现问题。现在在该模板中写下这一行: 现在在你的 javascript 函数中,在发出 ajax 请求之前,写下: var csrf = $('#csrf').val() 这将选择传递给模板的令牌值并将其存储在变量中csrf.现在,在进行 ajax 调用时,在您的帖子数据中,也传递此值:“csrfmiddlewaretoken”:csrf
即使您没有实现 django 表单,这也将起作用。
实际上,这里的逻辑是:您需要可以从请求中获取的令牌。所以你只需要弄清楚登录后立即调用的函数。一旦你有了这个令牌,要么进行另一个 ajax 调用来获取它,要么将它传递给你的 ajax 可以访问的某个模板。
csrftoken: csrftoken
,而不是 csrfmiddlwaretoken: csrftoken
。更改后,它起作用了。谢谢
对于遇到此问题并尝试调试的人:
1)django csrf 检查(假设您发送一个)是 here
2) 在我的情况下,settings.CSRF_HEADER_NAME
设置为“HTTP_X_CSRFTOKEN”,而我的 AJAX 调用发送了一个名为“HTTP_X_CSRF_TOKEN”的标头,因此无法正常工作。我可以在 AJAX 调用或 django 设置中更改它。
3) 如果您选择在服务器端更改它,请找到您的 django 安装位置并在 csrf middleware
中设置一个断点。如果您使用的是 virtualenv
,它将类似于:~/.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, '')
然后,确保 csrf
令牌正确来自 request.META
4)如果您需要更改标题等 - 在设置文件中更改该变量
如果有人正在努力使用 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/
这是 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>
来源:https://docs.djangoproject.com/en/1.11/ref/csrf/
var csrftoken = jQuery("[name=csrfmiddlewaretoken]").val();
工作的元素。另一种获取 csrftoken 的方法:var csrftoken = '{{ csrf_token }}';
使用 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>
2022 年更新
在 CSRF 攻击中,无辜的最终用户被攻击者欺骗,提交了他们无意的 Web 请求
选项1
from django.views.decorators.csrf import csrf_exempt
from django.http.response import JsonResponse
@csrf_exempt
def commentDeletePost(request):
if request.is_ajax() and request.method == 'POST':
try:
comment = Comment.objects.get(pk=request.POST['pk'])
if comment.author != request.user:
return JsonResponse({'e': 'Forbidden'}, status=403)
comment.delete()
return JsonResponse({}, status=200)
execpt Comment.DoesNotExist:
return JsonResponse({'e': 'Not Found'}, status=404)
选项 2
<div id="csrf">
{% csrf_token %}
</div>
<script type="text/javascript">
window.crud = {
commentDelete: function(
pk,
success,
error,
){
$.ajax({
headers: {'X-CSRFToken': document.getElementById('csrf').querySelector('input').value},
type: "POST",
url: "{% url 'comment-delete-post' %}",
data: {
pk: pk,
},
success: success,
error: error,
})
},
}
</script>
两种选择各有优势。第一个选项将丢弃 csrf 令牌,这不会保护您的站点免受 csrf 攻击,但它会允许用户发送多个具有相同 Ajax 功能的请求。第二个选项将限制用户仅发送一个 Ajax 请求,因为 csrf 令牌只能使用一次,但它更安全。我个人更喜欢选项 1,因为像 like, star, unlike
这样的 Ajax 函数需要多次 Ajax 调用,并且允许用户多次调用不是一个有风险的函数。
与 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/
(我没有足够的代表发表评论,所以我将我的输入发布为答案)
我有一个解决方案。在我的 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);
}
<script type="text/javascript"> window.CSRF_TOKEN = "{{ csrf_token }}"; </script>