CSRF与Django,React + Redux使用Axios

问题描述 投票:37回答:8

这是一个教育项目,不适用于生产。我不打算将用户登录作为其中的一部分。

我可以使用CSRF令牌对Django进行POST调用而无需用户登录吗?我可以不使用jQuery吗?我在这里超出了我的深度,肯定会混淆一些概念。

对于JavaScript方面,我发现了这个redux-csrf包。我不确定如何将它与我使用Axios的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"));
      })
  }
};

在Django方面,我已经阅读了CSRF上的this documentation,以及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 docs中窃取):

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

I've read,我需要更改Axios CSRF信息:

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

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

我在哪里坚持实际的令牌,我从调用getCookie('csrftoken')得到的价值?

django reactjs redux django-csrf axios
8个回答
33
投票

有三种方法。您可以在每个axios调用的标题中手动包含令牌,您可以在每次调用中设置axios的xsrfHeaderName,或者设置默认的xsrfHeaderName

1. Adding it manually

假设您已将令牌的值存储在名为csrfToken的变量中。在axios调用中设置标题:

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

2. Setting xsrfHeaderName in the call:

添加这个:

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

然后在你的settings.py文件中添加以下行:

CSRF_COOKIE_NAME = "XSRF-TOKEN"

3. Setting default headers[1]

您可以为axios设置默认标头,而不是在每次调用中定义标头。

在您要导入axios以进行调用的文件中,在导入下方添加以下内容:

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

然后在你的settings.py文件中添加以下行:

CSRF_COOKIE_NAME = "XSRF-TOKEN"

编辑(2017年6月10日):用户@yestema说它与Safari[2]略有不同

编辑(2019年4月17日):用户@GregHolst说上面的Safari解决方案对他不起作用。相反,他在MacOS Mojave上使用上述解决方案#3 for Safari 12.1。 (来自评论)

编辑(2019年2月17日):您可能还需要设置[3]

axios.defaults.withCredentials = true

问题:下一部分对任何人都有用吗?我想知道只有包含解决方案才能改善这个答案。如果您有意见,请告诉我。

困惑:

Django Docs

首先,詹姆斯·埃文斯Django docsreferenced的整个段落:

...在每个XMLHttpRequest上,将自定义X-CSRFToken标头设置为CSRF标记的值。这通常更容易,因为许多JavaScript框架提供了允许在每个请求上设置标头的钩子。

作为第一步,您必须自己获取CSRF令牌。该令牌的推荐来源是csrftoken cookie,如果您已按照上述方式为视图启用了CSRF保护,则会设置该cookie。

注意

默认情况下,CSRF令牌cookie名为csrftoken,但您可以通过CSRF_COOKIE_NAME设置控制cookie名称。

默认情况下,CSRF标头名称为HTTP_X_CSRFTOKEN,但您可以使用CSRF_HEADER_NAME设置对其进行自定义。


Axios Docs

这是来自Axios docs。它表示您设置包含csrftoken的cookie的名称,以及此处标题的名称:

  // `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

Terms

如我的问题所示,您可以使用document.cookie访问cookie。我唯一的cookie是我放在Django模板中的CSRF令牌。这是一个例子:

csrftoken=5knNceCUi9nL669hGGsvCi93XfqNhwTwM9Pev7bLYBOMXGbHVrjitlkKi44CtpFU

在那些令人困惑的文档中有一些概念被抛出:

  • 包含CSRF令牌的cookie的名称。在Django中,这是默认的csrftoken,它位于cookie中等号的左侧。
  • 实际的令牌。这是cookie中等号右侧的所有内容。
  • 带有标记值的http标头。

我试过的东西不起作用:12


14
投票

我发现了,axios.defaults.xsrfCookieName = "XCSRF-TOKEN";CSRF_COOKIE_NAME = "XCSRF-TOKEN"

在Mac OS上不适用于APPLE Safari

MAC Safari的解决方案很简单,只需将XCSRF-TOKEN更改为csrftoken即可

所以,在js-code中应该是:

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

在settings.py中:

    CSRF_COOKIE_NAME = "csrftoken"

8
投票

这个配置对我没有问题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"

2
投票

对我来说,django没有听我发送的标题。我可以卷入api但无法使用axios访问它。看看cors-headers package ......它可能是你最好的朋友。

我通过安装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虽然这可能是矫枉过正


1
投票

您可以手动将Django提供的CSRF令牌添加到您的所有帖子请求中,但这很烦人。

来自Django docs

虽然上述方法(手动设置CSRF令牌)可用于AJAX POST请求,但它有一些不便之处:您必须记住在每次POST请求时都将CSRF令牌作为POST数据传递。因此,有一种替代方法:在每个XMLHttpRequest上,将自定义X-CSRFToken标头设置为CSRF标记的值。这通常更容易,因为许多JavaScript框架提供了允许在每个请求上设置标头的钩子。

文档具有可用于从CSRF令牌cookie中提取CSRF令牌的代码,然后将其添加到AJAX请求的标头中。


1
投票

实际上有一种非常简单的方法可以做到这一点。

axios.defaults.xsrfHeaderName = "X-CSRFToken";添加到您的应用程序配置,然后在settings.py文件中设置CSRF_COOKIE_NAME = "XSRF-TOKEN"。奇迹般有效。


0
投票

除了yestema所说的(并且由krescruz,cran_man,Dave Merwin等人回应),您还需要:

axios.defaults.withCredentials = true
© www.soinside.com 2019 - 2024. All rights reserved.