CSRF与Django,React + Redux使用Axios

时间:2016-08-31 16:21:27

标签: django reactjs redux django-csrf axios

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

我是否可以使用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')得到的值?

9 个答案:

答案 0 :(得分:32)

有三种方法。您可以在每个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"

修改 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 docs referenced Axios docs的全部段落

  

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

     

作为第一步,您必须自己获取CSRF令牌。推荐   令牌的来源是csrftoken cookie,如果设置为   如上所述,您已为自己的观点启用了CSRF保护。

     

请注意

     

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

     

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

Axios Docs

这来自1。它表示您在此处设置包含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。我唯一的cookie是我放在Django模板中的CSRF令牌。这是一个例子:

csrftoken=5knNceCUi9nL669hGGsvCi93XfqNhwTwM9Pev7bLYBOMXGbHVrjitlkKi44CtpFU

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

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

我尝试过的不起作用的事情:2,{{3}}

答案 1 :(得分: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"

答案 2 :(得分:7)

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

答案 3 :(得分:3)

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

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

在settings.py文件中:

CSRF_COOKIE_NAME = "XCSRF-TOKEN"

答案 4 :(得分: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中,虽然这可能是矫枉过正的

答案 5 :(得分:1)

You could add the Django-provided CSRF token manually into all of your post requests, but that's annoying.

From the Django docs:

While the above method (manually setting CSRF token) can be used for AJAX POST requests, it has some inconveniences: you have to remember to pass the CSRF token in as POST data with every POST request. For this reason, there is an alternative method: on each XMLHttpRequest, set a custom X-CSRFToken header to the value of the CSRF token. This is often easier, because many JavaScript frameworks provide hooks that allow headers to be set on every request.

The docs have code you can use to pull the CSRF token from the CSRF token cookie and then add it to the header of your AJAX request.

答案 6 :(得分:1)

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

axios.defaults.xsrfHeaderName = "X-CSRFToken";添加到您的应用配置中,然后在settings.py文件中设置CSRF_COOKIE_NAME = "XSRF-TOKEN"。像魅力一样。

答案 7 :(得分:0)

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

axios.defaults.withCredentials = true

答案 8 :(得分:0)

花了很多时间研究并实现以上答案后,我发现了这个问题的错误!我添加了此答案,作为已接受答案的补充。如上所述,我已经完成了所有设置,但是对我来说 gotcha 实际上是在浏览器中!

如果在本地进行测试,请确保您通过127.0.0.1而非localhost访问react! localhost以不同的方式处理请求标头,并且没有在标头响应中显示CSRF令牌,就像127.0.0.1那样!因此,请尝试使用localhost:3000而不是127.0.0.1:3000

希望这会有所帮助。