使用 Django 的 CSRF,使用 Axios 的 React+Redux
CSRF with Django, React+Redux using 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 方面,我阅读了 this documentation on CSRF, and this 关于一般使用基于 class 的视图的内容。
这是我目前的看法:
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;
}
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-provided CSRF 令牌手动添加到所有 post 请求中,但这很烦人。
来自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.
文档中有代码可用于从 CSRF 令牌 cookie 中提取 CSRF 令牌,然后将其添加到 AJAX 请求的 header 中。
这个问答是 2016 年的,不出所料,我相信情况已经发生了变化。答案继续收到赞成票,所以我将添加其他答案的新信息,但也保留原始答案。
在评论中让我知道哪种解决方案适合您。
选项 1. 设置默认 headers
在导入 Axios 的文件中,设置默认值 headers:
import axios from 'axios';
axios.defaults.xsrfHeaderName = "X-CSRFTOKEN";
axios.defaults.xsrfCookieName = "csrftoken";
方案2.手动添加到axios调用
假设您已将令牌的值存储在名为 csrfToken
的变量中。在您的 axios 调用中设置 headers:
// ...
method: 'post',
url: '/api/data',
data: {...},
headers: {"X-CSRFToken": csrfToken},
// ...
选项3.在调用中设置xsrfHeaderName
:
添加这个:
// ...
method: 'post',
url: '/api/data',
data: {...},
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
我尝试过但没有用的东西:1
实际上有一种非常简单的方法可以做到这一点。
将 axios.defaults.xsrfHeaderName = "X-CSRFToken";
添加到您的应用程序配置中,然后在您的 settings.py 文件中设置 CSRF_COOKIE_NAME = "XSRF-TOKEN"
。就像一个魅力。
"easy way" 几乎对我有用。这似乎有效:
import axios from 'axios';
axios.defaults.xsrfHeaderName = "X-CSRFTOKEN";
axios.defaults.xsrfCookieName = "XCSRF-TOKEN";
并且在 settings.py 文件中:
CSRF_COOKIE_NAME = "XCSRF-TOKEN"
我发现 axios.defaults.xsrfCookieName = "XCSRF-TOKEN";
和 CSRF_COOKIE_NAME = "XCSRF-TOKEN"
在 Mac OS
上的 APPLE Safari 中不工作
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"
这个配置对我来说没有问题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
对我来说,django 没有听我发送的 headers。我可以卷入 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 中,虽然这可能有点矫枉过正
除了 yestema 所说的(并得到 krescruz、cran_man、Dave Merwin 等人的回应),您还需要:
axios.defaults.withCredentials = true
在花了太多时间研究和实施上述答案后,我发现了我对这个问题的错误!我添加了此答案以补充已接受的答案。我已经按照提到的设置了所有内容,但对我来说 gotcha 实际上在浏览器本身!
如果在本地测试,请确保您通过 127.0.0.1
而不是 localhost
访问 React! localhost
以不同方式处理请求 header,并且不会在 header 响应中显示 CSRF 令牌,而 127.0.0.1
会!所以不要 localhost:3000
试试 127.0.0.1:3000
!
希望这对您有所帮助。
这是一个教育项目,不用于生产。我不打算将用户登录作为其中的一部分。
我可以在没有用户登录的情况下使用 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 方面,我阅读了 this documentation on CSRF, and this 关于一般使用基于 class 的视图的内容。
这是我目前的看法:
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;
}
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-provided CSRF 令牌手动添加到所有 post 请求中,但这很烦人。
来自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.
文档中有代码可用于从 CSRF 令牌 cookie 中提取 CSRF 令牌,然后将其添加到 AJAX 请求的 header 中。
这个问答是 2016 年的,不出所料,我相信情况已经发生了变化。答案继续收到赞成票,所以我将添加其他答案的新信息,但也保留原始答案。
在评论中让我知道哪种解决方案适合您。
选项 1. 设置默认 headers
在导入 Axios 的文件中,设置默认值 headers:
import axios from 'axios';
axios.defaults.xsrfHeaderName = "X-CSRFTOKEN";
axios.defaults.xsrfCookieName = "csrftoken";
方案2.手动添加到axios调用
假设您已将令牌的值存储在名为 csrfToken
的变量中。在您的 axios 调用中设置 headers:
// ...
method: 'post',
url: '/api/data',
data: {...},
headers: {"X-CSRFToken": csrfToken},
// ...
选项3.在调用中设置xsrfHeaderName
:
添加这个:
// ...
method: 'post',
url: '/api/data',
data: {...},
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
我尝试过但没有用的东西:1
实际上有一种非常简单的方法可以做到这一点。
将 axios.defaults.xsrfHeaderName = "X-CSRFToken";
添加到您的应用程序配置中,然后在您的 settings.py 文件中设置 CSRF_COOKIE_NAME = "XSRF-TOKEN"
。就像一个魅力。
"easy way" 几乎对我有用。这似乎有效:
import axios from 'axios';
axios.defaults.xsrfHeaderName = "X-CSRFTOKEN";
axios.defaults.xsrfCookieName = "XCSRF-TOKEN";
并且在 settings.py 文件中:
CSRF_COOKIE_NAME = "XCSRF-TOKEN"
我发现 axios.defaults.xsrfCookieName = "XCSRF-TOKEN";
和 CSRF_COOKIE_NAME = "XCSRF-TOKEN"
在 Mac OS
上的 APPLE Safari 中不工作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"
这个配置对我来说没有问题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
对我来说,django 没有听我发送的 headers。我可以卷入 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 中,虽然这可能有点矫枉过正
除了 yestema 所说的(并得到 krescruz、cran_man、Dave Merwin 等人的回应),您还需要:
axios.defaults.withCredentials = true
在花了太多时间研究和实施上述答案后,我发现了我对这个问题的错误!我添加了此答案以补充已接受的答案。我已经按照提到的设置了所有内容,但对我来说 gotcha 实际上在浏览器本身!
如果在本地测试,请确保您通过 127.0.0.1
而不是 localhost
访问 React! localhost
以不同方式处理请求 header,并且不会在 header 响应中显示 CSRF 令牌,而 127.0.0.1
会!所以不要 localhost:3000
试试 127.0.0.1:3000
!
希望这对您有所帮助。