Ошибка проверки Django CSRF с запросом Ajax POST
я мог бы использовать некоторую помощь в соответствии с механизмом защиты CSRF Django через мой пост AJAX. Я следовал указаниям здесь:
http://docs.djangoproject.com/en/dev/ref/contrib/csrf/
Я скопировал пример кода AJAX, который они имеют на этой странице точно:
http://docs.djangoproject.com/en/dev/ref/contrib/csrf/#ajax
я поставил предупреждение печать содержимого getCookie('csrftoken') до xhr.setRequestHeader звонка и его действительно заполняется некоторыми данными. Я не уверен, как проверить правильность маркера, но я рад, что он находит и отправляет что-то.
но Джанго все еще отвергает мой пост 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
Я уверен, что я что-то упускаю, и, может быть, это просто, но я не знаю, что это такое. Я искал вокруг так и увидел некоторую информацию о выключении проверки CSRF для моего просмотра через csrf_exempt декоратор, но я нахожу это непривлекательным. Я пробовал это, и это работает, но я бы предпочел, чтобы мой пост работал так, как Джанго был разработан, чтобы ожидать его, если это возможно.
на всякий случай это полезно, вот суть того, что делает мой взгляд:
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))
Спасибо за ваши ответы!
18 ответов:
реальное решение
хорошо, мне удалось отследить проблему. Он лежит в 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')); } } });вместо кода, опубликованного в официальных документах: http://docs.djangoproject.com/en/1.2/ref/contrib/csrf/#ajax
рабочий код, происходит от этой записи Django: http://www.djangoproject.com/weblog/2011/feb/08/security/
таким образом, общее решение: "используйте обработчик ajaxSetup вместо обработчика ajaxSend". Я не знаю, почему это работает. Но это работает для меня :)
предыдущий пост (без ответа)
Я испытываю ту же проблему на самом деле.
это происходит после обновления до Django 1.2.5 - не было ошибок с запросами AJAX POST в Django 1.2.4 (AJAX не был защищен в в любом случае, но это сработало просто отлично).
как и OP, я попробовал фрагмент JavaScript, опубликованный в документации Django. Я использую jQuery 1.5. Я также использую "Джанго.промежуточное программное обеспечение.CSRF-атаку.CsrfViewMiddleware " middleware.
Я попытался следовать коду промежуточного программного обеспечения, и я знаю, что он не работает на этом:
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'));?
Я надеюсь, что предоставленные детали помогут нам в решении этого вопроса:)
Если вы используете
$.ajaxфункция, вы можете просто добавитьcsrfтокен в теле данных:$.ajax({ data: { somedata: 'somedata', moredata: 'moredata', csrfmiddlewaretoken: '{{ csrf_token }}' },
добавьте эту строку в свой код jQuery:
$.ajaxSetup({ data: {csrfmiddlewaretoken: '{{ csrf_token }}' }, });и сделал.
проблема в том, что django ожидает, что значение из файла cookie будет передано обратно как часть данных формы. Код из предыдущего ответа получает javascript, чтобы выследить значение cookie и поместить его в данные формы. Это прекрасный способ сделать это с технической точки зрения, но это выглядит немного многословен.
в прошлом я делал это более просто, получая javascript, чтобы поместить значение токена в данные post.
Если вы используете {% csrf_token %} в вашем шаблоне вы получите скрытое поле формы, которое несет значение. Но, если вы используете {{ csrf_token }}, вы просто получите голое значение токена, поэтому вы можете использовать это в javascript следующим образом....
csrf_token = "{{ csrf_token }}";затем вы можете включить это, с требуемым именем ключа в хэш, который вы затем отправляете в качестве данных для вызова ajax.
The
{% csrf_token %}поместите в HTML шаблоны внутри<form></form>переводится примерно так:
<input type='hidden' name='csrfmiddlewaretoken' value='Sdgrw2HfynbFgPcZ5sjaoAI5zsMZ4wZR' />так почему бы просто не grep его в вашем JS, как это:
token = $("#change_password-form").find('input[name=csrfmiddlewaretoken]').val()а затем передать его, например, делать какой-то пост, например:
$.post( "/panel/change_password/", {foo: bar, csrfmiddlewaretoken: token}, function(data){ console.log(data); });
Если ваша форма публикуется правильно в Django без JS, вы должны иметь возможность постепенно улучшать ее с помощью 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.
не-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);
принятый ответ, скорее всего, отвлекающий маневр. Разница между Django 1.2.4 и 1.2.5 была требованием для токена CSRF для запросов AJAX.
Я столкнулся с этой проблемой на Django 1.3, и это было вызвано тем, что файл cookie CSRF не установлен в первую очередь. Django не будет устанавливать куки, если это не нужно. Таким образом, исключительно или сильно ajax-сайт, работающий на Django 1.2.4, потенциально никогда не отправил бы токен клиенту, а затем обновление требование маркера вызвало бы 403 ошибки.
идеальное решение здесь: http://docs.djangoproject.com/en/dev/ref/contrib/csrf/#page-uses-ajax-without-any-html-form
но вам придется подождать 1.4, если это не просто документация догоняет кодEdit
обратите внимание также, что более поздние документы Django отмечают ошибку в jQuery 1.5, поэтому убедитесь, что вы используете 1.5.1 или более позднюю версию с предложенным Django код: http://docs.djangoproject.com/en/1.3/ref/contrib/csrf/#ajax
используйте Firefox с Firebug. Откройте вкладку "консоль" при запуске запроса ajax. С
DEBUG=Trueвы получаете хорошую страницу ошибок django в качестве ответа, и вы даже можете увидеть отображаемый html ответа ajax на вкладке консоли.тогда вы будете знать, в чем ошибка.
Я только что столкнулся с немного другой, но похожей ситуацией. Не на 100% уверен, что это будет решение для вашего случая, но я решил проблему для Django 1.3, установив параметр POST "csrfmiddlewaretoken" с правильной строкой значения cookie, которая обычно возвращается в виде вашего домашнего HTML системой шаблонов Django с тегом " {%csrf_token%}". Я не пробовал на старом Django, просто произошло и решилось на Django1.3. Моя проблема заключалась в том, что первый запрос был отправлен через Ajax из формы было успешно сделано, но вторая попытка из того же самого из failed, привела к состоянию 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>
кажется, никто не упоминал, как это сделать в чистом JS с помощью и
{{ csrf_token }}, Так вот простое решение, где вам не нужно искать через куки или дом:var xhttp = new XMLHttpRequest(); xhttp.open("POST", url, true); xhttp.setRequestHeader("X-CSRFToken", "{{ csrf_token }}"); xhttp.send();
один токен CSRF присваивается каждому сеансу (т. е. каждый раз, когда вы входите в систему). Поэтому, прежде чем вы захотите получить некоторые данные, введенные пользователем, и отправить их как вызов ajax некоторой функции, защищенной csrf_protect decorator, попробуйте найти функции, которые вызываются, прежде чем вы получите эти данные от пользователя. Например, какой-то шаблон должен быть визуализирован, на котором ваш пользователь вводит данные. Этот шаблон отображается некоторой функцией. В этой функции вы можете получить токен csrf в виде следует: csrf = запрос.COOKIES ['csrftoken'] Теперь передайте это значение csrf в контекстный словарь, против которого данный шаблон отображается. Теперь в этом шаблоне напишите эту строку: Теперь в вашей функции javascript, прежде чем сделать запрос ajax, напишите это: var csrf = $('#csrf').val () это выберет значение токена, переданного шаблону, и сохранит его в переменной csrf. Теперь при вызове ajax в ваших данных post также передайте это значение : "csrfmiddlewaretoken": csrf
Это работайте, даже если вы не реализуете формы django.
на самом деле, логика здесь такова : вам нужен токен, который вы можете получить из запроса. Поэтому вам просто нужно выяснить, что функция вызывается сразу после входа в систему. Как только у вас есть этот токен, либо сделайте еще один вызов ajax, чтобы получить его, либо передайте его в какой-либо шаблон, доступный вашему ajax.
для тех, кто сталкивается с этим и пытается отладить:
1) проверка django csrf (предполагая, что вы отправляете один) является здесь
2) в моем случае,
settings.CSRF_HEADER_NAMEбыло установлено значение ' HTTP_X_CSRFTOKEN 'и мой AJAX-вызов отправлял заголовок с именем' HTTP_X_CSRF_TOKEN', так что материал не работал. Я могу изменить его в AJAX-вызов, или установка Джанго.3) Если вы решите изменить его на стороне сервера, найдите место установки django и бросьте точка останова в
csrf middleware.если вы используетеvirtualenv, это будет что-то вроде:~/.envs/my-project/lib/python2.7/site-packages/django/middleware/csrf.pyimport 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токен правильно получен из запроса.Мета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/
в моем случае проблема была с конфигурацией nginx, которую я скопировал с основного сервера на временный с отключением https, который не нужен на втором в этом процессе.
Я должен был прокомментировать эти две строки в конфигурации, чтобы заставить его работать снова:
# uwsgi_param UWSGI_SCHEME https; # uwsgi_pass_header X_FORWARDED_PROTO;
вот менее подробное решение, предоставленное 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>
как это не указано нигде в текущих ответах, самое быстрое решение, если вы не врезать js в вашем шаблоне:
поставить
<script type="text/javascript"> window.CSRF_TOKEN = "{{ csrf_token }}"; </script>перед вашей ссылкой на скрипт.js файл в вашем шаблоне, а затем добавитьcsrfmiddlewaretokenв своемdataсловарь в вашем js-файле:$.ajax({ type: 'POST', url: somepathname + "do_it/", data: {csrfmiddlewaretoken: window.CSRF_TOKEN}, success: function() { console.log("Success!"); } })
Comments