Django Rest Framework跨域场景下防XSS/CSRF的认证方案咨询
跨域DRF+React的API安全方案与实践
一、JWT的XSS防护与http-only Cookie适配方案
把JWT存到http-only cookie里确实是防XSS的最优解,不用纠结前端拿不到token没法加Authorization头——后端可以直接从cookie中读取token做认证,浏览器会自动在跨域请求中携带符合条件的cookie,只要做好跨域和CSRF配置就行。
后端DRF配置步骤
CORS与CSRF基础配置
安装django-cors-headers,在settings.py中添加:INSTALLED_APPS = [ # ...其他应用 'corsheaders', ] MIDDLEWARE = [ 'corsheaders.middleware.CorsMiddleware', # 放在CommonMiddleware之前 'django.middleware.common.CommonMiddleware', # ...其他中间件 ] # 允许的前端域名,禁止使用* CORS_ALLOWED_ORIGINS = [ "https://your-frontend-domain.com", "http://localhost:3000", # 开发环境 ] CORS_ALLOW_CREDENTIALS = True # 允许跨域携带cookie # 信任前端域名,支持CSRF验证 CSRF_TRUSTED_ORIGINS = [ "https://your-frontend-domain.com", "http://localhost:3000", ]自定义JWT认证类
让DRF从cookie中读取access token,替代默认的Authorization头读取逻辑:from rest_framework_simplejwt.authentication import JWTAuthentication from django.http import HttpRequest class CookieJWTAuthentication(JWTAuthentication): def authenticate(self, request: HttpRequest): token = request.COOKIES.get('access_token') if not token: return None try: validated_token = self.get_validated_token(token) user = self.get_user(validated_token) return (user, validated_token) except Exception: return None在
settings.py中设置默认认证类:REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': [ 'your_app.path.to.CookieJWTAuthentication', ], }登录/刷新视图修改
登录成功后把token写入http-only cookie,而非返回给前端存储:from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView from rest_framework.response import Response class CustomTokenObtainPairView(TokenObtainPairView): def post(self, request, *args, **kwargs): response = super().post(request, *args, **kwargs) # 设置access token cookie response.set_cookie( key='access_token', value=response.data['access'], httponly=True, secure=True, # 生产环境开启HTTPS后启用 samesite='Strict', # 限制跨站携带,降低CSRF风险 max_age=3600 # 和token有效期保持一致,比如1小时 ) # 设置refresh token cookie response.set_cookie( key='refresh_token', value=response.data['refresh'], httponly=True, secure=True, samesite='Strict', max_age=7*24*3600 # 7天有效期 ) # 删除响应体中的token,避免前端存储 del response.data['access'] del response.data['refresh'] return response class CustomTokenRefreshView(TokenRefreshView): def post(self, request, *args, **kwargs): # 从cookie中取refresh token request.data['refresh'] = request.COOKIES.get('refresh_token') response = super().post(request, *args, **kwargs) # 更新access token cookie response.set_cookie( key='access_token', value=response.data['access'], httponly=True, secure=True, samesite='Strict', max_age=3600 ) del response.data['access'] return response最后把这两个视图添加到项目的
urls.py中。
前端React处理
全局配置请求携带cookie
用axios的话,在项目入口文件设置:import axios from 'axios'; axios.defaults.withCredentials = true;CSRF Token处理
对POST/PUT/DELETE等状态修改请求,必须携带X-CSRFToken头:// 从cookie中提取CSRF token function getCsrfToken() { const value = `; ${document.cookie}`; const parts = value.split(`; csrftoken=`); if (parts.length === 2) return parts.pop().split(';').shift(); } // 发送请求示例 async function submitData(data) { try { const response = await axios.post('/api/your-endpoint/', data, { headers: { 'X-CSRFToken': getCsrfToken() } }); return response.data; } catch (error) { console.error(error); } }
二、API安全最佳实践
XSS防护
- React默认会转义用户输入,禁止使用
dangerouslySetInnerHTML渲染不可信内容 - 敏感数据(token、用户隐私信息)必须存带有http-only、secure、same-site属性的cookie
- 配置内容安全策略(CSP),限制页面加载的资源来源,比如在后端返回
Content-Security-Policy响应头
- React默认会转义用户输入,禁止使用
CSRF防护
- 所有状态修改请求必须验证CSRF token,GET请求不做任何状态修改操作
- 给cookie设置
SameSite=Strict或Lax,减少跨站请求携带cookie的风险 - 确保CSRF token和用户会话绑定,避免复用
JWT相关
- 缩短access token有效期(1小时以内),用refresh token定期刷新
- refresh token同样存http-only cookie,并且支持用户注销时即时失效(可将refresh token存入数据库,刷新时验证有效性)
- JWT payload不要存敏感信息,因为payload是base64编码,可直接解码查看
跨域与传输安全
- 严格限制
CORS_ALLOWED_ORIGINS,绝不使用* - 生产环境强制启用HTTPS,防止数据明文传输
- 配置HSTS头,强制浏览器用HTTPS访问站点
- 严格限制
其他防护
- 给API添加速率限制,防止暴力破解或批量请求攻击
- 定期更新DRF、React及依赖包,修复已知安全漏洞
- 对用户输入做服务器端校验,不要只依赖前端验证
三、dj-rest-auth使用经验
核心配置要点
安装依赖:
dj-rest-auth依赖django-allauth,需同时安装,然后在INSTALLED_APPS中添加:INSTALLED_APPS = [ # ...其他应用 'rest_framework', 'rest_framework.authtoken', 'dj_rest_auth', 'django.contrib.sites', 'allauth', 'allauth.account', 'allauth.socialaccount', ] SITE_ID = 1跨域适配:和前面JWT的配置一致,开启
CORS_ALLOW_CREDENTIALS,配置CSRF_TRUSTED_ORIGINS,前端请求需携带withCredentials和X-CSRFToken
常见问题解决
- 邮箱验证失效:检查
EMAIL_BACKEND配置,开发环境可用django.core.mail.backends.console.EmailBackend输出到控制台,生产环境配置SMTP服务;确保ACCOUNT_EMAIL_VERIFICATION设置正确(比如mandatory强制验证) - 跨域请求被拒:确认
CORS_ALLOWED_ORIGINS包含前端准确域名,CSRF_TRUSTED_ORIGINS也要同步添加 - 自定义用户模型适配:如果用了自定义User,必须在
settings.py中设置AUTH_USER_MODEL = 'your_app.YourUser',必要时需自定义allauth的适配器来适配用户字段
优势与注意事项
- 自带登录、注册、密码重置、邮箱验证、第三方OAuth登录等功能,节省开发时间
- 如果要把token存http-only cookie,需要自定义登录视图,逻辑和前面的JWT处理类似,将响应体中的token写入cookie并删除响应体中的内容
- 文档虽全面,但部分配置需结合
django-allauth的文档一起查看,初期可能会有混淆
内容的提问来源于stack exchange,提问作者webdeveloper
相关产品推荐
相关产品推荐

