使用 Django 的 CSRF,使用 Axios 的 React+Redux

IT技术 django reactjs redux django-csrf axios
2021-03-24 01:07:59

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

我可以在没有用户登录的情况下使用 CSRF 令牌对 Django 进行 POST 调用吗?我可以在不使用 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,以及对一般与基于类视图工作。

到目前为止,这是我的看法:

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"

我在哪里贴实际令牌,我从调用中获得的valuegetCookie('csrftoken')

6个回答

有三种方式。您可以在每个 axios 调用的标头中手动包含令牌,您可以xsrfHeaderName在每个调用中设置 axios ,或者您设置一个默认的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"

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

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

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

axios.defaults.withCredentials = true

问题:下一节对任何人都有用吗?我想知道是否可以通过仅包含解决方案来改进此答案。如果您有意见,请告诉我。

困惑:

Django 文档

首先,James Evans引用Django 文档的整个段落

...在每个 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 文档

这是来自Axios 文档它表示您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

条款

如我的问题所示,您使用document.cookie. 我拥有的唯一 cookie 是我放在 Django 模板中的 CSRF 令牌。下面是一个例子:

csrftoken=5knNceCUi9nL669hGGsvCi93XfqNhwTwM9Pev7bLYBOMXGbHVrjitlkKi44CtpFU

这些文档中有一些概念令人困惑:

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

我尝试过但不起作用的事情:1

选项 3 对我有用,但不适用于下面介绍的 Safari 选项。
2021-06-03 01:07:59
几年前,我正在研究这个,当我发现方法#2 时。从那以后我就没有使用过 Django。如果我的答案中有过时的信息,请告诉我,我会更新。
2021-06-04 01:07:59
@ReedDunkle 是的,它在 MacOS Mojave 上的 Safari 12.1 上看起来也不错。起初我没有尝试你的选项3,因为我认为我直接去yestema的答案,但这对我来说显然是错误的方式。
2021-06-08 01:07:59
@ReedDunkle .. 以为我已经解决了这个问题。但它已经重新浮出水面。参见stackoverflow.com/questions/55842141/...
2021-06-14 01:07:59
有趣的是,我发现 axios 中的“X-CSRFTOKEN”在服务器请求标头上转换为“HTTP_X_CSRFTOKEN”。
2021-06-20 01:07:59

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

在 Mac OS 上的 Apple Safari 中不起作用

MAC Safari 的解决方案很简单,只需更改XCSRF-TOKENcsrftoken

所以,在js代码中应该是:

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

在 settings.py 中:

    CSRF_COOKIE_NAME = "csrftoken"
CSRF_COOKIE_NAME默认为'csrftoken',因此您无需编辑settings.py
2021-05-30 01:07:59

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

“简单的方法”几乎对我有用。这似乎有效:

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

在 settings.py 文件中:

CSRF_COOKIE_NAME = "XCSRF-TOKEN"
欢迎来到 SO!请访问如何撰写答案您已经提供了一个旧问题的答案,该问题已经有非常详细的答案,并得到了许多人的认可和支持。几乎可以工作的答案是否为其他人提供了尚未详细说明的有用信息?
2021-05-22 01:07:59

在花了太多时间研究并实施上述答案后,我发现了这个问题的错误!我已添加此答案作为已接受答案的补充。我已经按照上面提到的方式设置了所有内容,但问题实际上在于浏览器本身!

如果在本地测试,请确保您访问的是 react through127.0.0.1而不是localhost! localhost以不同的方式处理请求标头,并且不会在标头响应中显示 CSRF 令牌,在那里127.0.0.1所以,而不是localhost:3000尝试127.0.0.1:3000

希望这可以帮助。