Django REST Framework登录状态下POST请求出现CSRF Token缺失错误的解决咨询
Django REST Framework登录状态下POST请求出现CSRF Token缺失错误的解决咨询
我之前在使用Django 5.x和DRF开发时也碰到过一模一样的问题,结合你给出的代码,先帮你理清问题根源,再分场景给你对应解决方案:
问题根源
Django的SessionAuthentication(DRF默认认证类之一)会对已登录用户的POST/PUT/DELETE等修改类请求强制检查CSRF Token,而未登录时因为没有建立会话(session),所以会跳过这个检查——这就是为什么你未登录时请求正常,登录后就报Token缺失的原因。
分场景解决方案
场景1:你用DRF自带的可浏览API(Browsable API)测试
如果你是在浏览器里用DRF的自带页面测试请求,其实页面已经自动包含了CSRF Token的隐藏字段,可能是你手动构造请求时没带上。解决方式很简单:
- 直接在DRF可浏览API页面填写表单提交,页面会自动携带CSRF Token;
- 如果你是用页面的"Raw data"模式提交JSON,需要手动添加请求头:
- 从浏览器的Cookie中找到
csrftoken的值(可以在开发者工具的Application→Cookies里查看); - 在请求头里添加
X-CSRFToken: 你的csrftoken值。
- 从浏览器的Cookie中找到
场景2:你做前后端分离的API(比如用前端框架/axios/fetch请求)
这种场景下,推荐两种处理方式:
方式A:继续使用SessionAuthentication,前端携带CSRF Token
前端需要从Cookie中读取CSRF Token,然后在每次修改类请求(POST/PUT/DELETE)的请求头里带上:
- 举个Fetch API的例子:
// 从Cookie中提取CSRF Token function getCookie(name) { let cookieValue = null; if (document.cookie && document.cookie !== '') { const cookies = document.cookie.split(';'); for (let i = 0; i < cookies.length; i++) { const cookie = cookies[i].trim(); if (cookie.substring(0, name.length + 1) === (name + '=')) { cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); break; } } } return cookieValue; } // 发送POST请求 const csrftoken = getCookie('csrftoken'); fetch('/api/notes/', { method: 'POST', headers: { 'X-CSRFToken': csrftoken, 'Content-Type': 'application/json', }, body: JSON.stringify({ title: '新笔记标题' }), credentials: 'include' // 必须带这个,确保会话Cookie被发送 })
方式B:改用Token/JWT认证(更适合前后端分离)
Session认证更适合同域的传统Web应用,前后端分离场景下用Token或JWT认证会更灵活,也不需要处理CSRF问题:
- 先安装依赖(以常用的Simple JWT为例):
pip install djangorestframework-simplejwt - 在
settings.py中配置DRF的认证类:REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': [ 'rest_framework_simplejwt.authentication.JWTAuthentication', # 保留SessionAuth以便兼容可浏览API,可选 'rest_framework.authentication.SessionAuthentication', ], } # 可选:配置JWT的过期时间等参数 from datetime import timedelta SIMPLE_JWT = { 'ACCESS_TOKEN_LIFETIME': timedelta(minutes=60), 'REFRESH_TOKEN_LIFETIME': timedelta(days=1), } - 在
urls.py中添加JWT的获取和刷新端点:from rest_framework_simplejwt.views import ( TokenObtainPairView, TokenRefreshView, ) urlpatterns = [ # 其他路由... path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'), path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'), ] - 前端登录时请求
/api/token/拿到access_token,之后每次API请求在请求头里带上:Authorization: Bearer 你的access_token值
额外的代码优化建议
看你的NoteListView的post方法,手动修改serializer.initial_data['author']的方式有点不太规范,可以改成在序列化器中自动填充当前用户:
- 在
NoteSerializer中把author字段设为read_only=True; - 然后在视图中调整保存逻辑:
def post(self, request): serializer = NoteSerializer(data=request.data) if serializer.is_valid(): # 直接传入author参数保存 serializer.save(author=request.user) return Response(serializer.data) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
这样代码更符合DRF的规范,也避免手动修改initial_data可能带来的问题。
内容来源于stack exchange
相关产品推荐
相关产品推荐

