Подтвердить что ты не робот

CSRF с Django, React + Redux с использованием Axios

Это образовательный проект, а не для производства. Я не собирался иметь учетные записи пользователей как часть этого.

Могу ли я сделать POST-вызовы в Django с помощью токена CSRF без входа пользователя? Могу ли я это сделать без использования jQuery? Я здесь не в своей глубине и, конечно, объединяю некоторые понятия.

Для стороны JavaScript я нашел этот redux-csrf. Я не уверен, как объединить его с моим действием POST, используя Axios:

export const addJob = (title, hourly, tax) => {
  console.log("Trying to addJob: ", title, hourly, tax)
  return (dispatch) => {
    dispatch(requestData("addJob"));
    return axios({
      method: 'post',
      url: "/api/jobs",
      data: {
        "title": title,
        "hourly_rate": hourly,
        "tax_rate": tax
      },
      responseType: 'json'
    })
      .then((response) => {
        dispatch(receiveData(response.data, "addJob"));
      })
      .catch((response) => {
        dispatch(receiveError(response.data, "addJob"));
      })
  }
};

На стороне Django я прочитал эту документацию на CSRF и this в целом работает с представлениями на основе классов.

Вот мой взгляд до сих пор:

class JobsHandler(View):

    def get(self, request):
        with open('./data/jobs.json', 'r') as f:
            jobs = json.loads(f.read())

        return HttpResponse(json.dumps(jobs))

    def post(self, request):
        with open('./data/jobs.json', 'r') as f:
            jobs = json.loads(f.read())

        new_job = request.to_dict()
        id = new_job['title']
        jobs[id] = new_job

        with open('./data/jobs.json', 'w') as f:
            f.write(json.dumps(jobs, indent=4, separators=(',', ': ')))

        return HttpResponse(json.dumps(jobs[id]))

Я попытался использовать декоратор csrf_exempt, чтобы не беспокоиться об этом на данный момент, но это не похоже на то, как который работает.

Я добавил {% csrf_token %} к моему шаблону.

Это мой метод getCookie (украден из документов Django):

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 = 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;
}

Я прочитал, что мне нужно изменить информацию Axios CSRF:

var axios = require("axios");
var axiosDefaults = require("axios/lib/defaults");

axiosDefaults.xsrfCookieName = "csrftoken"
axiosDefaults.xsrfHeaderName = "X-CSRFToken"

Где я прикрепляю фактический токен, значение, которое я получаю от вызова getCookie('csrftoken')?

4b9b3361

Ответ 1

Есть три способа. Вы можете вручную включить токен в заголовок каждого вызова xsrfHeaderName, вы можете установить xsrfHeaderName в каждом вызове или установить xsrfHeaderName умолчанию.

1. Добавление его вручную

Допустим, у вас есть значение токена, хранящееся в переменной csrfToken. Установите заголовки в вашем вызове axios:

// ...
method: 'post',
url: '/api/data',
data: {...},
headers: {"X-CSRFToken": csrfToken},
// ...

2. Установка xsrfHeaderName в вызове:

Добавь это:

// ...
method: 'post',
url: '/api/data',
data: {...},
xsrfHeaderName: "X-CSRFToken",
// ...

Затем в вашем файле settings.py добавьте эту строку:

CSRF_COOKIE_NAME = "XSRF-TOKEN"

3. Установка заголовков по умолчанию [1]

Вместо того, чтобы определять заголовок в каждом вызове, вы можете установить заголовки по умолчанию для axios.

В файле, куда вы импортируете axios, чтобы сделать вызов, добавьте это ниже ваших импортов:

axios.defaults.xsrfHeaderName = "X-CSRFToken";

Затем в вашем файле settings.py добавьте эту строку:

CSRF_COOKIE_NAME = "XSRF-TOKEN"

Редактировать (10 июня 2017 г.): Пользователь @yestema говорит, что с Safari он работает немного по-другому [2]

Изменить (17 апреля 2019 г.): Пользователь @GregHolst говорит, что приведенное выше решение Safari не работает для него. Вместо этого он использовал вышеуказанное Решение № 3 для Safari 12.1 на MacOS Mojave. (из комментариев)

Изменить (17 февраля 2019 г.): Вам также может потребоваться установить [3]:

axios.defaults.withCredentials = true

Вопрос: полезен ли этот следующий раздел? Мне интересно, если этот ответ может быть улучшен только путем включения решений. Дайте мне знать, если у вас есть мнение, пожалуйста.

Путаница:

Django Docs

Во-первых, весь отрывок из Django docs, на который ссылался Джеймс Эванс:

... в каждом XMLHttpRequest установите для настраиваемого заголовка X-CSRFToken значение токена CSRF. Это часто проще, потому что многие JavaScript-фреймворки предоставляют хуки, которые позволяют устанавливать заголовки при каждом запросе.

В качестве первого шага вы должны получить сам токен CSRF. Рекомендуемым источником для токена является файл cookie csrftoken, который будет установлен, если вы включили защиту CSRF для своих представлений, как описано выше.

Заметка

Cookie-токен CSRF по умолчанию называется csrftoken, но вы можете управлять именем cookie файла с помощью параметра CSRF_COOKIE_NAME.

Имя заголовка CSRF по умолчанию - HTTP_X_CSRFTOKEN, но вы можете настроить его с помощью параметра CSRF_HEADER_NAME.


Axios Docs

Это из документов Axios. Это указывает на то, что вы csrftoken имя файла cookie, который содержит csrftoken, и имя заголовка здесь:

  // 'xsrfCookieName' is the name of the cookie to use as a value for xsrf token
  xsrfCookieName: 'XSRF-TOKEN', // default

  // 'xsrfHeaderName' is the name of the http header that carries the xsrf token value
  xsrfHeaderName: 'X-XSRF-TOKEN', // default

термины

Как указано в моем вопросе, вы получаете доступ к файлам cookie с помощью document.cookie. Единственный файл cookie, который у меня есть, - это токен CSRF, который я вставил в шаблон Django. Вот пример:

csrftoken=5knNceCUi9nL669hGGsvCi93XfqNhwTwM9Pev7bLYBOMXGbHVrjitlkKi44CtpFU

В этих документах есть несколько концепций, которые сбивают с толку:

  • Имя файла cookie, содержащего токен CSRF. В Django это по умолчанию csrftoken, который находится слева от знака равенства в куки.
  • Фактический токен. Это все с правой стороны знака равенства в куки.
  • Заголовок http, который содержит значение токена.

То, что я пробовал, не сработало: 1, 2

Ответ 2

Я узнал, что axios.defaults.xsrfCookieName = "XCSRF-TOKEN"; и CSRF_COOKIE_NAME = "XCSRF-TOKEN"

НЕ РАБОТАЕТ В APPLE Safari в Mac OS

Решение для MAC Safari легко, просто измените XCSRF-TOKEN на csrftoken

Итак, в js-коде должно быть:

    import axios from 'axios';
    axios.defaults.xsrfHeaderName = "X-CSRFTOKEN";
    axios.defaults.xsrfCookieName = "csrftoken";

В settings.py:

    CSRF_COOKIE_NAME = "csrftoken"

Ответ 3

Эта конфигурация у меня работает без проблем Config Axios CSRF django

import axios from 'axios'

/**
 * Config global for axios/django
 */
axios.defaults.xsrfHeaderName = "X-CSRFToken"
axios.defaults.xsrfCookieName = 'csrftoken'

export default axios

Ответ 4

"Легкий способ" почти сработал у меня. Кажется, что это работает:

import axios from 'axios';
axios.defaults.xsrfHeaderName = "X-CSRFTOKEN";
axios.defaults.xsrfCookieName = "XCSRF-TOKEN";

И в файле settings.py:

CSRF_COOKIE_NAME = "XCSRF-TOKEN"

Ответ 5

Вы можете добавить маркер CSRF, предоставленный Django, вручную во все ваши почтовые запросы, но это раздражает.

Из Django docs:

В то время как вышеупомянутый метод (ручная настройка токена CSRF) может использоваться для запросов POST AJAX, у него есть некоторые неудобства: вы должны помнить, чтобы передать токен CSRF в качестве данных POST с каждым запросом POST. По этой причине существует альтернативный метод: на каждом XMLHttpRequest установите собственный заголовок X-CSRFToken на значение токена CSRF. Это часто бывает проще, потому что многие фреймворки JavaScript предоставляют крючки, которые позволяют устанавливать заголовки для каждого запроса.

В документах есть код, который вы можете использовать, чтобы вытащить токен CSRF из куки файла токена CSRF, а затем добавить его в заголовок вашего запроса AJAX.

Ответ 6

На самом деле есть действительно простой способ сделать это.

Добавьте axios.defaults.xsrfHeaderName = "X-CSRFToken"; в конфигурацию вашего приложения, а затем установите CSRF_COOKIE_NAME = "XSRF-TOKEN" в файле settings.py. Работает как шарм.

Ответ 7

Для меня django не слушал заголовки, которые я отправлял. Я мог свернуться в API, но не мог получить к нему доступ с помощью axios. Проверьте пакет cors-headers... это может быть ваш новый лучший друг.

Я исправил это, установив django-cors-headers

pip install django-cors-headers

И затем добавление

INSTALLED_APPS = (
    ...
    'corsheaders',
    ...
)

а также

MIDDLEWARE = [  # Or MIDDLEWARE_CLASSES on Django < 1.10
    ...
    'corsheaders.middleware.CorsMiddleware',
    'django.middleware.common.CommonMiddleware',
    ...
]

в мои settings.py

У меня тоже было

ALLOWED_HOSTS = ['*']
CORS_ORIGIN_ALLOW_ALL = True
CORS_ALLOW_CREDENTIALS = True
CORS_EXPOSE_HEADERS = (
    'Access-Control-Allow-Origin: *',
)

в моем settings.py, хотя это, вероятно, излишним

Ответ 8

В дополнение к тому, что сказал yestema (и повторяется krescruz, cran_man, Dave Merwin et al.), Вам также необходимо:

axios.defaults.withCredentials = true

Ответ 9

Потратив слишком много часов на изучение и реализацию приведенного выше ответа, я нашел свою ошибку для этой проблемы! Я добавил этот ответ, чтобы дополнить принятый ответ. Я настроил все как указано, но получил для меня на самом деле в самом браузере!

Если вы проводите локальное тестирование, убедитесь, что вы получаете доступ к реакции через 127.0.0.1 вместо localhost! localhost обрабатывает заголовки запроса по-разному и не показывает токены CSRF в ответе заголовка, где 127.0.0.1 будет! Поэтому вместо localhost:3000 попробуйте 127.0.0.1:3000!

Надеюсь это поможет.