![](/img/trans.png)
[英]Forbidden (CSRF token missing.) error when trying to make a POST request. Using React (Axios) and Django
[英]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 方面,我已經閱讀了有關 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')
獲得的值?
有三種方法。 您可以在每個axios調用的標頭中手動包含令牌,您可以在每次調用中設置axios的xsrfHeaderName
,或者設置默認的xsrfHeaderName
。
假設您已將令牌的值存儲在名為csrfToken
的變量中。 在axios調用中設置標題:
// ...
method: 'post',
url: '/api/data',
data: {...},
headers: {"X-CSRFToken": csrfToken},
// ...
xsrfHeaderName
: 添加這個:
// ...
method: 'post',
url: '/api/data',
data: {...},
xsrfHeaderName: "X-CSRFToken",
// ...
然后在settings.py
文件中添加以下行:
CSRF_COOKIE_NAME = "XSRF-TOKEN"
您可以為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
問題:下一部分對任何人都有用嗎? 我想知道只有包含解決方案才能改善這個答案。 如果您有意見,請告訴我。
困惑:
...在每個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文檔 。 它表示您設置包含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
在那些令人困惑的文檔中有一些概念被拋出:
csrftoken
,它位於csrftoken
號的左側。 我發現, 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"
這個配置對我來說沒有問題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
在花了太多時間研究並實施上述答案之后,我發現我的錯誤是這個問題! 我已添加此答案以補充已接受的答案。 我已經按照上述設置了所有內容,但對我來說,問題實際上在於瀏覽器本身!
如果在本地進行測試,請確保您通過127.0.0.1
而不是localhost
訪問 react! localhost
以不同的方式處理請求標頭,並且不會在標頭響應中顯示 CSRF 令牌,而127.0.0.1
會! 所以代替localhost:3000
試試127.0.0.1:3000
!
希望這可以幫助。
“簡單的方式”幾乎對我有用。 這似乎有效:
import axios from 'axios';
axios.defaults.xsrfHeaderName = "X-CSRFTOKEN";
axios.defaults.xsrfCookieName = "XCSRF-TOKEN";
並在settings.py文件中:
CSRF_COOKIE_NAME = "XCSRF-TOKEN"
您可以手動將Django提供的CSRF令牌添加到您的所有帖子請求中,但這很煩人。
來自Django文檔 :
雖然上述方法( 手動設置CSRF令牌 )可用於AJAX POST請求,但它有一些不便之處:您必須記住在每次POST請求時都將CSRF令牌作為POST數據傳遞。 因此,有一種替代方法:在每個XMLHttpRequest上,將自定義X-CSRFToken標頭設置為CSRF標記的值。 這通常更容易,因為許多JavaScript框架提供了允許在每個請求上設置標頭的鈎子。
文檔具有可用於從CSRF令牌cookie中提取CSRF令牌的代碼,然后將其添加到AJAX請求的標頭中。
實際上有一種非常簡單的方法可以做到這一點。
添加axios.defaults.xsrfHeaderName = "X-CSRFToken";
到您的應用配置,然后在settings.py文件中設置CSRF_COOKIE_NAME = "XSRF-TOKEN"
。 奇跡般有效。
對我來說,django沒有聽我發送的標題。 我可以卷入api但無法使用axios訪問它。 查看cors-headers包 ...它可能是你最好的朋友。
我通過安裝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
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.