"Токен 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-запрос двумя разными способами:

  1. Чтобы ваш взгляд не проверял токен csrf. Это можно сделать с помощью декоратора @csrf_exempt, как это:

    from django.views.decorators.csrf import csrf_exempt
    
    @csrf_exempt
    def your_view_name(request):
        ...
    
  2. Чтобы встроить токен 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.

Для меня лучшим решением на данный момент является:

  1. Создать csrf.js файл

  2. Вставьте код в csrf.js файл

  3. Ссылка на код в шаблоне вам нужна

    <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) {
    // ...
});

В качестве резюме моих ошибок:

  1. Не забудьте установить тип содержимого запроса.
  2. Получите значение csrf либо из

    NB. Имя файла cookie по умолчаниюcsrftoken, но может быть переопределен CSRF_COOKIE_NAMEпараметр.

    • DOM, если вы не можете получить доступ к файлу cookie (вы устанавливаете для CSRF_USE_SESSIONS или CSRF_COOKIE_HTTPONLY значение True)

    document.querySelector('[name=csrfmiddlewaretoken]').value;

  3. Установите заголовок запроса, я использую 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 !!
  1. Не используйте URL-адрес в ajax, который отличается от адреса браузера.127.0.0.1&localhostне одинаковы!!

  2. Нет необходимости устанавливатьdata.append("csrfmiddlewaretoken", csrftoken);в теле запроса, насколько я знаю.

Другие вопросы по тегам