"Токен CSRF отсутствует или неверен" при публикации параметра через AJAX в Django
Я пытаюсь опубликовать параметр как
jQuery.ajax(
{
'type': 'POST',
'url': url,
'contentType': 'application/json',
'data': "{content:'xxx'}",
'dataType': 'json',
'success': rateReviewResult
}
);
Тем не менее, Джанго возвращается Forbidden 403. CSRF verification failed. Request aborted.
я использую 'django.middleware.csrf.CsrfViewMiddleware'
и не мог найти, как я могу предотвратить эту проблему, не ставя под угрозу безопасность.
14 ответов
Вы можете сделать AJAX-запрос двумя разными способами:
Чтобы ваш взгляд не проверял токен csrf. Это можно сделать с помощью декоратора
@csrf_exempt
, как это:from django.views.decorators.csrf import csrf_exempt @csrf_exempt def your_view_name(request): ...
Чтобы встроить токен csrf в каждый запрос AJAX, для jQuery это может быть:
$(function () { $.ajaxSetup({ headers: { "X-CSRFToken": getCookie("csrftoken") } }); });
Где
getCookie
Функция извлекает токен csrf из файлов cookie. Я использую следующую реализацию:function getCookie(c_name) { if (document.cookie.length > 0) { c_start = document.cookie.indexOf(c_name + "="); if (c_start != -1) { c_start = c_start + c_name.length + 1; c_end = document.cookie.indexOf(";", c_start); if (c_end == -1) c_end = document.cookie.length; return unescape(document.cookie.substring(c_start,c_end)); } } return ""; }
Кроме того, в jQuery есть плагин для доступа к файлам cookie, что-то вроде этого:
// set cookie $.cookie('cookiename', 'cookievalue');<br> // read cookie var myCookie = $.cookie('cookiename');<br> // delete cookie $.cookie('cookiename', null);
Самый простой способ, который я нашел, это включить {{csrf_token}}
значение в данных:
jQuery.ajax(
{
'type': 'POST',
'url': url,
'contentType': 'application/json',
'data': {
'content': 'xxx',
'csrfmiddlewaretoken': '{{ csrf_token }}',
},
'dataType': 'json',
'success': rateReviewResult
}
);
Мне потребовалось некоторое время, чтобы понять, что делать с кодом, который опубликовал Дэниел. Но на самом деле все, что вам нужно сделать, это вставить его в начало файла javascript.
Для меня лучшим решением на данный момент является:
Создать
csrf.js
файлВставьте код в
csrf.js
файлСсылка на код в шаблоне вам нужна
<script type="text/javascript" src="{{ STATIC_PREFIX }}js/csrf.js"></script>
Заметить, что STATIC_PREFIX/js/csrf.js
указывает на мой файл. Я на самом деле загружаю STATIC_PREFIX
переменная с {% get_static_prefix as STATIC_PREFIX %}
,
Расширенный совет: если вы используете шаблоны и у вас есть что-то вроде base.html
откуда вы расширяетесь, тогда вы можете просто ссылаться на сценарий оттуда, и вам больше не нужно беспокоиться об остальных ваших файлах. Насколько я понимаю, это также не должно представлять проблему безопасности.
Простой и короткий
$.ajaxSetup({
headers: { "X-CSRFToken": '{{csrf_token}}' }
});
ИЛИ ЖЕ
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", '{{csrf_token}}');
}
}
});
Из-за отсутствия прямого ответа, вам просто нужно добавить заголовок X-CSRFToken
к запросу AJAX, который находится в куки csrftoken
, JQuery не делает куки (по некоторым причинам) без плагина, поэтому:
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-cookie/1.4.1/jquery.cookie.min.js"></script>
и минимальное изменение кода:
$.ajax({
headers: { "X-CSRFToken": $.cookie("csrftoken") },
...
});
Самое быстрое решение без каких-либо плагинов, если вы не встраиваете js в шаблон:
Положил <script type="text/javascript"> window.CSRF_TOKEN = "{{ csrf_token }}"; </script>
перед вашей ссылкой на файл script.js в вашем шаблоне, затем добавьте csrfmiddlewaretoken
в ваш data
толковый словарь:
$.ajax({
type: 'POST',
url: somepathname + "do_it/",
data: {csrfmiddlewaretoken: window.CSRF_TOKEN},
success: function() {
console.log("Success!");
}
})
Если вы встраиваете свой js в шаблон, это так просто: data: {csrfmiddlewaretoken: '{{ csrf_token }}'}
Вчера у меня возникла та же проблема, и я подумал, что она поможет людям, если есть простой способ справиться с этим, поэтому я написал для этого плагин jQuery: jquery.djangocsrf. Вместо добавления токена CSRF в каждый запрос он подключается к событию AjaxSend jQuery и добавляет cookie-файл клиента в заголовок.
Вот как это использовать:
1- включить его:
<script src="path/to/jquery.js"></script>
<script src="path/to/jquery.cookie.js"></script>
<script src="path/to/jquery.djangocsrf.js"></script>
2 - включите его в своем коде:
$.djangocsrf( "enable" );
Django всегда добавляет токен в куки, если ваш шаблон использует {% csrf_token %}
, Чтобы он всегда добавлялся, даже если вы не используете специальный тег в шаблоне, используйте @ensure_csrf_cookie
декоратор:
from django.views.decorators.csrf import ensure_csrf_cookie
@ensure_csrf_cookie
def my_view(request):
return render(request, 'mytemplate.html')
Примечание: я использую Django 1.6.2.
Включают x-csrftoken
заголовок в запросе:
var token = $('input[name="csrfmiddlewaretoken"]').prop('value');
jQuery.ajax({
type: 'POST',
url: url,
beforeSend : function(jqXHR, settings) {
jqXHR.setRequestHeader("x-csrftoken", get_the_csrf_token_from_cookie());
},
data: data,
dataType: 'json',
});
Спасибо всем за все ответы. Я использую Django 1.5.1. Я немного опоздал на вечеринку, но здесь идет.
Я нашел ссылку на проект Django очень полезной, но на самом деле я не хотел включать дополнительный код JavaScript каждый раз, когда хотел сделать Ajax-вызов.
Мне нравится ответ Джеррикэна, так как он очень лаконичен и добавляет только одну строку к обычному Ajax-вызову. В ответ на комментарии ниже своего комментария относительно ситуаций, когда теги шаблонов Django недоступны, как насчет загрузки csrfmiddlewaretoken из DOM?
var token = $('input[name="csrfmiddlewaretoken"]').prop('value');
jQuery.ajax({
type: 'POST',
url: url,
data: { 'csrfmiddlewaretoken': token },
dataType: 'json',
success: function(data) { console.log('Yippee! ' + data); }
});
РЕДАКТИРОВАТЬ март 2016
Мой подход к этому вопросу за последние несколько лет изменился. Я добавляю приведенный ниже код (из документации Django) в файл main.js и загружаю его на каждую страницу. После этого вам больше не нужно беспокоиться о токене CSRF с ajax.
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;
}
var csrftoken = getCookie('csrftoken');
Если после прочтения других ответов кто-то все еще испытывает трудности, попробуйте это:
$.ajax({
type: "POST",
beforeSend: function (request)
{
request.setRequestHeader("X-CSRF-TOKEN", "${_csrf.token}");
},
url: servlet_path,
data : data,
success : function(result) {
console.log("Success!");
}
});
Просто хочу сказать, что если он работает в вашем случае использования, то токен CSRF не потребуется. В моем случае использования
GET
было в порядке.
HTML
<form action="">
{% csrf_token %}
</form>
JS
<script>
const csrftoken = document.querySelector('[name=csrfmiddlewaretoken]').value;
const request = new Request(
'url_here',
{headers: {'X-CSRFToken': csrftoken}}
);
fetch(request, {
method: 'POST',
// mode: 'same-origin' optinal // Do not send CSRF token to another domain.
}).then(function(response) {
console.log(response);
});
</script>
Пожалуйста, обратите внимание, что, делая это таким образом, убедитесь, что у вас нет
{% csrf_token %}
внутри
<form></form>
теги. Затем, как описано здесь, добавьте следующий код в свой javascript
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;
}
const csrftoken = getCookie('csrftoken');
// using js fetch
// https://docs.djangoproject.com/en/3.1/ref/csrf/#setting-the-token-on-the-ajax-request
const request = new Request(
/* URL */,
{headers: {'X-CSRFToken': csrftoken}}
);
fetch(request, {
method: 'POST',
mode: 'same-origin' // Do not send CSRF token to another domain.
}).then(function(response) {
// ...
});
В качестве резюме моих ошибок:
- Не забудьте установить тип содержимого запроса.
- Получите значение csrf либо из
- Шаблон
{{ csrf_token }}
- Файл cookie, используйте фрагмент на сайте django
NB. Имя файла cookie по умолчанию
csrftoken
, но может быть переопределенCSRF_COOKIE_NAME
параметр.- DOM, если вы не можете получить доступ к файлу cookie (вы устанавливаете для CSRF_USE_SESSIONS или CSRF_COOKIE_HTTPONLY значение True)
document.querySelector('[name=csrfmiddlewaretoken]').value;
- Шаблон
- Установите заголовок запроса, я использую XMLHttpRequest
const Http = new XMLHttpRequest();
Http.setRequestHeader("X-CSRFToken", CSRF_VALUE);
Http.setRequestHeader("X_CSRFTOKEN", CSRF_VALUE);
Имя заголовка управляется
CSRF_HEADER_NAME
параметр, который по умолчанию равен .
Но : «Имя заголовка, полученное от сервера, нормализуется путем преобразования всех символов в верхний регистр, замены любых дефисов символами подчеркивания и добавления префикса «HTTP_» к имени»src.
Итак, если вы установитеHTTP_X_CSRFTOKEN
заголовок, Django преобразует его вHTTP_HTTP_X_CSRFTOKEN
который будет называться неправильно и подниметCSRF missed
ошибка.
Http.setRequestHeader("X-CSRFToken", csrftoken); // This worked
Http.setRequestHeader("X-CSRFTOKEN", csrftoken); // Also this
Http.setRequestHeader("HTTP-X-CSRFToken", csrftoken); // Not working
Http.setRequestHeader("HTTP_X_CSRFTOKEN", csrftoken); // Not Working
Http.setRequestHeader("X_CSRFTOKEN", csrftoken); // Not working !!
Не используйте URL-адрес в ajax, который отличается от адреса браузера.
127.0.0.1
&localhost
не одинаковы!!Нет необходимости устанавливать
data.append("csrfmiddlewaretoken", csrftoken);
в теле запроса, насколько я знаю.