You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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,需要手动添加请求头:
    1. 从浏览器的Cookie中找到csrftoken的值(可以在开发者工具的Application→Cookies里查看);
    2. 在请求头里添加X-CSRFToken: 你的csrftoken值。

场景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问题:

  1. 先安装依赖(以常用的Simple JWT为例):
    pip install djangorestframework-simplejwt
    
  2. 在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),
    }
    
  3. 在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'),
    ]
    
  4. 前端登录时请求/api/token/拿到access_token,之后每次API请求在请求头里带上:
    Authorization: Bearer 你的access_token值
    

额外的代码优化建议

看你的NoteListView的post方法,手动修改serializer.initial_data['author']的方式有点不太规范,可以改成在序列化器中自动填充当前用户:

  1. 在NoteSerializer中把author字段设为read_only=True;
  2. 然后在视图中调整保存逻辑:
    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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.07 09:23:06