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

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

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

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

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"));
      })
  }
};

на стороне Джанго, я прочитал документация на CSRF, и этой о работе с представлениями на основе классов.

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

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')?

7 ответов


есть три способа. Вы можете вручную включить токен в заголовок каждого вызова axios, вы можете установить axios 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"

редактировать: по-видимому, он работает немного иначе с Сафари[2]

[1] от Дэйва Мервина комментарий


растерянность:

Django Docs

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

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

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

Примечание

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

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


Axios Docs

это Axios docs. Это означает, что вы задаете имя файла 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

условия

как указано в моем вопросе, вы получаете доступ к cookies с document.cookie. Только печенье у меня есть CSRF токен я вставил в шаблон Django. Вот пример:

csrftoken=5knNceCUi9nL669hGGsvCi93XfqNhwTwM9Pev7bLYBOMXGbHVrjitlkKi44CtpFU

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

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

вещи, которые я пробовал, что не работает: 1, 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";

In settings.py:

    CSRF_COOKIE_NAME = "csrftoken"

"легкий путь" почти сработал для меня. Это, кажется, работает:

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

и в settings.py файл:

CSRF_COOKIE_NAME = "XCSRF-TOKEN"

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

import axios from 'axios'

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

export default axios

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

с Django docs:

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

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


есть очень простой способ сделать это.

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


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

я исправил это, установив 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 хотя это вероятно, это перебор