视图使用@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
相关产品推荐
相关产品推荐

