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

Django+React前端请求报CSRF token missing错误排查

问题基本信息
  • 报错内容:CSRF Failed: CSRF token missing
  • 技术栈:Django + React
  • 核心现象:前端控制台可正常打印获取到的csrftoken值,但后端无法识别前端请求携带的CSRF token;后端本地CSRF校验逻辑无异常,问题仅出现在前端发起的跨域请求场景。
现有实现代码

后端视图(views.py)

class CheckAuthenticated(views.APIView):
    def get(self, request):
        if request.user.is_authenticated:
            return Response("Authenticated")
        else:
            return Response("Not Authenticated",status=401)
    
class PostView(viewsets.ModelViewSet):
    serializer_class = serializer.PostSerializer

    def get_queryset(self):
        queryset = models.Post.objects.all()
        return queryset
    
    @method_decorator(ensure_csrf_cookie,csrf_protect)
    def create(self,request):
        authentication_classes = [SessionAuthentication]
        permissions_classes = [IsAuthenticated]
        post = serializer.PostSerializer(data=request.data)
        if post.is_valid():
            title = post.data['title']
  
            description = post.data['description']
    
            models.Post.objects.create(title=title,description=description,user=User.objects.first())
            return Response("post created successfully.")
        return Response("post creation failed.")

CSRF Token获取接口

class GetCSRFToken(views.APIView):
    permission_classes = [AllowAny, ]

    @method_decorator(ensure_csrf_cookie)
    def get(self, request, format=None):
        return Response("Success")

路由配置(urls.py)

urlpatterns = [
   path('csrf/',views.GetCSRFToken.as_view(),name='csrf'),
   path('isauthenticated/',views.CheckAuthenticated.as_view(),name='authenticated'),
]

前端React实现代码

let handleSubmit = (e)=>{
        e.preventDefault()
        console.log(Cookies.get('csrftoken'))
        axios.post('http://127.0.0.1:8000/posts/',post,{withCredentials:true},{headers:{'X-CSRFToken':Cookies.get('csrftoken')}}).then((res)=>{
            console.log(res.data)
        }).catch((e)=>{
            console.log(e.response.data)
            console.log(Cookies.get('csrftoken'))
        })
    }
    useEffect(()=>{
        axios.get('http://127.0.0.1:8000/posts/').then((res)=>{
            setPostList(res.data)
        })
        axios.get('http://127.0.0.1:8000/csrf/',{headers:{Authorization:null},withCredentials:true})

    },[])

Django CSRF & 跨域配置

ALLOWED_HOSTS = ['*']
ACCESS_CONTROL_ALLOW_ORIGIN = '*'
CORS_ORIGIN_ALLOW_ALL = True
CORS_ALLOW_CREDENTIALS = True
ACCESS_CONTROL_ALLOW_CREDENTIALS = True
ACCESS_CONTROL_ALLOW_METHODS = '*'
ACCESS_CONTROL_ALLOW_HEADERS = '*'

'''
SESSION_COOKIE_SECURE = True

CSRF_COOKIE_PATH = '/'
'''
CSRF_COOKIE_SAMESITE = 'Strict'  

CSRF_TRUSTED_ORIGINS = [ "http://127.0.0.1:3000",'http://127.0.0.1:8000']

DRF认证配置

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.SessionAuthentication',
    ]
}
已排查进展
  • 前端调用/csrf/接口后可正常读取到csrftoken的cookie值,cookie获取流程无异常
  • 确认问题与DRF认证配置相关:使用SessionAuthentication时触发CSRF missing报错,切换为BasicAuthentication后会出现已登录用户被判定为未授权的问题,暂未定位完整根因
根因分析与修复方案

问题由4处配置/代码写法错误共同导致,按以下顺序修复即可:

  1. 修正axios请求参数写法错误
    axios.post方法仅支持3个参数:请求地址、请求体、请求配置。当前代码把withCredentials和headers拆成两个独立对象作为第3、第4个参数传入,实际post请求根本没有携带X-CSRFToken请求头,也没有正确开启跨域凭证携带。
    错误写法:

    axios.post('http://127.0.0.1:8000/posts/',post,{withCredentials:true},{headers:{'X-CSRFToken':Cookies.get('csrftoken')}})
    

    修正后写法:

    axios.post('http://127.0.0.1:8000/posts/', post, {
      withCredentials: true,
      headers: {
        'X-CSRFToken': Cookies.get('csrftoken')
      }
    })
    
  2. 调整CSRF Cookie的SameSite配置
    当前设置CSRF_COOKIE_SAMESITE = 'Strict'时,跨端口(3000→8000)的跨站请求不会携带CSRF Cookie,后端无法从请求Cookie中读取到csrftoken值,自然无法和请求头中的token做比对校验。开发环境将该配置修改为Lax即可:

    CSRF_COOKIE_SAMESITE = 'Lax'
    # 生产环境如果部署在不同二级域名下,可设置为None,此时必须同时开启CSRF_COOKIE_SECURE = True(仅HTTPS环境生效)
    
  3. 修正视图类的认证、权限写法
    当前在create方法内部定义authentication_classes、permissions_classes是无效写法,类级别的配置需要定义在方法外层,否则csrf_protect装饰器无法获取正确的认证上下文,会判定token无效。同时序列化器校验通过后要从validated_data取值,关联用户直接取当前登录用户,不要硬取第一个用户。修正后的PostView代码:

    class PostView(viewsets.ModelViewSet):
        serializer_class = serializer.PostSerializer
        # 类属性直接定义在方法外层
        authentication_classes = [SessionAuthentication]
        permission_classes = [IsAuthenticated]
    
        def get_queryset(self):
            return models.Post.objects.all()
        
        # csrf接口已经负责下发csrftoken cookie,这里不需要重复加ensure_csrf_cookie装饰器
        @method_decorator(csrf_protect)
        def create(self,request):
            post = serializer.PostSerializer(data=request.data)
            if post.is_valid():
                title = post.validated_data['title']
                description = post.validated_data['description']
                models.Post.objects.create(title=title, description=description, user=request.user)
                return Response("post created successfully.")
            return Response(post.errors, status=400)
    
  4. 清理冲突的CORS配置
    浏览器安全规则禁止跨域携带凭证(withCredentials=true)时将允许源设置为通配符*,当前手写的ACCESS_CONTROL_*开头的配置和django-cors-headers的配置存在冲突,直接删除所有ACCESS_CONTROL_*开头的自定义配置,调整CORS配置为明确指定允许源:

    # 删除所有ACCESS_CONTROL_ALLOW_*开头的配置
    ALLOWED_HOSTS = ['*']
    # 关闭全源允许,和凭证模式冲突
    CORS_ORIGIN_ALLOW_ALL = False
    CORS_ALLOWED_ORIGINS = [
        "http://127.0.0.1:3000",
        "http://127.0.0.1:8000",
    ]
    CORS_ALLOW_CREDENTIALS = True
    CORS_ALLOW_HEADERS = [
        "accept",
        "accept-encoding",
        "authorization",
        "content-type",
        "dnt",
        "origin",
        "user-agent",
        "x-csrftoken",
        "x-requested-with",
    ]
    
    CSRF_COOKIE_SAMESITE = 'Lax'
    CSRF_TRUSTED_ORIGINS = [ "http://127.0.0.1:3000",'http://127.0.0.1:8000']
    

修复完成后重启Django服务,清空浏览器原有Cookie后重新访问页面,先调用/csrf/接口确认csrftoken Cookie的SameSite属性为Lax,再发起post请求即可正常通过CSRF校验,同时SessionAuthentication也能正常识别登录用户状态,不会出现未授权报错。


内容的提问来源于stack exchange,提问作者Ghazi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 23:33:21