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

视图使用@csrf_protect时/api/token/refresh/报CSRF令牌缺失/无效错误

问题成因

给视图添加@csrf_protect装饰器后触发403 CSRF校验错误,核心原因有两点:

  • 装饰器顺序错误:Django原生CSRF装饰器和DRF的@api_view装饰器执行顺序是从下往上(离函数定义越近越先包装请求),你当前的写法把@csrf_protect放在最外层,会导致CSRF校验逻辑被DRF的请求处理流程干扰,触发异常拦截。
  • 请求未携带有效CSRF令牌:Django对POST等非安全方法的请求做CSRF校验时,要求请求必须携带和Cookie中csrftoken匹配的有效令牌,当前前端请求没有按规则传递这个令牌,自然会被拦截。

补充:你给这个接口加CSRF防护的思路是完全正确的——只要认证凭证(你这里是存在Cookie里的refresh_token)放在Cookie中,就必须做CSRF防护,不要为了省事直接关闭防护造成安全漏洞。另外你贴的代码里还有一处逻辑bug:if not user.is_active的判断缩进写在了if user is None的代码块内部,当user为None时会直接抛出用户不存在的异常,用户活跃状态的校验永远不会执行,修复时需要把这段判断的缩进调整到和上一个if同级。

修复方案

1. 调整装饰器顺序

把@csrf_protect放到离视图函数最近的位置(最内层装饰器),修正后的代码结构如下:

@api_view(['POST'])
@renderer_classes([CustomizeJSONRenderer])
@csrf_protect
def refresh_token_view(request):
    refresh_token = request.COOKIES.get('refreshtoken')
    # 校验refresh token存在
    if refresh_token is None:
       raise exceptions.AuthenticationFailed('Authentication credentials were not provided, please login.')
    # 校验refresh token有效性
    try:
        payload = jwt.decode(refresh_token, settings.REFRESH_TOKEN_SECRET, algorithms=['HS256'])
    except jwt.ExpiredSignatureError:
           raise exceptions.AuthenticationFailed('expired refresh token, please login again.')

    user = User.objects.filter(id=payload.get('user_id')).first()
    # 校验用户存在
    if user is None:
       raise exceptions.AuthenticationFailed('user not found.')
    # 校验用户活跃状态(修复原有缩进错误)
    if not user.is_active:
        raise exceptions.AuthenticationFailed('user is inactive.')

    access_token = generate_access_token(user)
    return Response({'access_token': access_token})

2. 配置CSRF令牌传递规则

前后端分离场景下按以下规则配置即可通过校验:

  • Django侧配置:
    • 确保django.middleware.csrf.CsrfViewMiddleware在settings的MIDDLEWARE列表中开启
    • 设置CSRF_COOKIE_HTTPONLY = False,允许前端JS读取Cookie中的csrftoken值(CSRF令牌本身无泄露风险,不需要开启HttpOnly)
    • 把前端服务的域名添加到CSRF_TRUSTED_ORIGINS配置项中,避免跨源请求被拦截
  • 前端侧配置:
    所有POST/PUT/DELETE等非GET请求,从Cookie中读取csrftoken字段的值,放到请求头X-CSRFToken中发送即可。如果使用axios作为请求库,可以通过请求拦截器全局统一注入:
    import Cookies from 'js-cookie'
    axios.interceptors.request.use(config => {
      const csrfToken = Cookies.get('csrftoken')
      if (csrfToken && config.method.toLowerCase() !== 'get') {
        config.headers['X-CSRFToken'] = csrfToken
      }
      return config
    })
    

3. 多端场景适配

如果这个接口同时要给无Cookie环境的移动端App调用,不要直接给整个视图加@csrf_exempt关闭CSRF防护,可以拆分两个接口:

  • 给网页端调用的刷新接口保留@csrf_protect,走Cookie+CSRF令牌的校验逻辑
  • 给移动端调用的刷新接口单独编写,要求客户端把refresh_token放在请求头(比如Authorization头)中传递,不要存在Cookie里,这种场景下不需要CSRF防护,可以正常加@csrf_exempt。

严禁直接给存Cookie认证凭证的接口加@csrf_exempt关闭CSRF校验,否则任意恶意站点都可以诱导已登录用户发起跨站请求盗用账号,属于严重的安全漏洞。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 23:18:47