Django生产环境X-CSRFToken设置错误及CSRF令牌获取问题求助
解决Django+React生产环境CSRF验证失败问题及令牌获取方案
问题背景
- 本地环境:Django后端通过
ensure_csrf_cookie装饰器的接口返回CSRF令牌,React前端存入Cookie后,POST请求携带X-CSRFToken,Cookie与请求头令牌匹配,请求正常。 - 生产环境:请求头的Cookie字段被转为小写,出现
CSRF verification failed错误,提示X-Csrftoken HTTP头令牌不正确。 - 需求补充:明确浏览器Application面板无CSRF令牌时的正确获取流程。
现有代码
后端Django代码
from django.views.decorators.csrf import ensure_csrf_cookie from django.utils.decorators import method_decorator from django.http import JsonResponse from django.middleware.csrf import get_token class CSRFTokenView(View): @method_decorator(ensure_csrf_cookie) def get(self, request, format=None): return JsonResponse({'csrftoken':get_token(request)})
前端React代码
import axios from 'axios'; import Cookies from 'js-cookie'; import { useEffect, useState } from 'react'; import { redirect } from 'react-router-dom'; const Signup = () => { const [signup, setSignup] = useState({}); const [disabled, setDisabled] = useState(false); const [isUp, setIsUp] = useState(false); const [error, setError] = useState({}); useEffect(()=>{ axios.get('http://127.0.0.1:8000/csrf/').then((res)=>{ console.log(res.data) Cookies.set('csrftoken',res.data.csrftoken) }) },[]) let handleSubmit = (e) => { e.preventDefault(); axios .post("http://127.0.0.1:8000/signup/", signup, { withCredentials: true, headers: { "X-CSRFToken": Cookies.get("csrftoken") }, }) .then((res) => { setDisabled(true) setIsUp(true) setTimeout(()=>{ setIsUp(false) redirect('/') },3000) }) .catch((e) => { console.log(e.response.data) let err = e.response.data setError(err); }); }; return ( // 组件渲染内容 ) }
解决方案
1. 生产环境CSRF验证失败修复
核心原因
生产环境中部分代理服务器(如Nginx)会自动将请求头字段转为小写,而Django默认优先识别X-CSRFToken驼峰格式;同时手动通过接口返回并设置Cookie的方式,可能与ensure_csrf_cookie自动生成的Cookie产生冲突,或Cookie的安全属性不符合生产环境要求。
修复步骤
后端调整:移除手动返回令牌,依赖装饰器自动设置Cookie
ensure_csrf_cookie装饰器会自动在响应头中设置csrftokenCookie,无需手动返回令牌,避免重复设置导致的冲突:class CSRFTokenView(View): @method_decorator(ensure_csrf_cookie) def get(self, request, format=None): return JsonResponse({'status': 'csrf_cookie_set'})前端调整:自动提取Cookie,兼容大小写格式
不再手动存储Cookie,直接从浏览器Cookie中提取令牌,同时兼容大小写的请求头字段:// 封装CSRF令牌获取函数 const getCsrfToken = () => { let token = null; if (document.cookie) { const cookies = document.cookie.split(';').map(c => c.trim()); for (const cookie of cookies) { if (cookie.startsWith('csrftoken=') || cookie.startsWith('CSRFToken=')) { token = cookie.split('=')[1]; break; } } } return token; }; useEffect(() => { // 仅触发后端设置Cookie,无需处理返回数据 axios.get('/csrf/', { withCredentials: true }); }, []); let handleSubmit = (e) => { e.preventDefault(); const csrfToken = getCsrfToken(); axios .post("/signup/", signup, { withCredentials: true, headers: { "X-CSRFToken": csrfToken, "x-csrftoken": csrfToken // 兼容小写请求头环境 }, }) .then((res) => { setDisabled(true); setIsUp(true); setTimeout(() => { setIsUp(false); redirect('/'); }, 3000); }) .catch((e) => { console.log(e.response.data); setError(e.response.data); }); };生产环境Cookie属性配置
在Django的settings.py中配置符合生产环境的Cookie安全属性:CSRF_COOKIE_SECURE = True # 仅在HTTPS下传输Cookie CSRF_COOKIE_SAMESITE = 'Lax' # 根据跨域需求选择Strict/Lax/None CSRF_COOKIE_HTTPONLY = False # 允许前端读取Cookie(默认值)
2. 浏览器无CSRF令牌时的正确获取流程
当浏览器Application面板中无csrftoken Cookie时,按以下步骤操作:
- 前端发起不带CSRF令牌的GET请求到后端带有
ensure_csrf_cookie装饰器的接口(如/csrf/); - 后端通过装饰器自动生成并设置
csrftokenCookie到浏览器; - 前端后续的POST/PUT/DELETE请求,调用
getCsrfToken()函数从Cookie提取令牌,携带X-CSRFToken请求头; - 必须确保所有请求配置
withCredentials: true,否则Cookie无法被正确传递和存储。
内容的提问来源于stack exchange,提问作者Ghazi
相关产品推荐
相关产品推荐

