DRF结合Simple JWT的Token自动刷新中间件失效问题求助
问题:Access Token过期后首次请求返回401,二次刷新才正常?
我使用Django(dj-rest-auth)实现认证功能,将access token和refresh token存储在httpOnly cookie中,前端无法直接访问这些令牌,因此需要在后端处理令牌刷新逻辑。编写了如下中间件用于自动刷新过期的access token:
from datetime import datetime, timezone from rest_framework_simplejwt.tokens import RefreshToken from django.http import HttpResponse from django.utils.deprecation import MiddlewareMixin class TokenRefreshMiddleware(MiddlewareMixin): def process_response(self, request, response): if response.status_code == 200: refresh_token = request.COOKIES.get('refresh_token') if refresh_token: try: decoded = RefreshToken(refresh_token, verify=False) if decoded: if decoded.access_token['exp'] < datetime.now(timezone.utc).timestamp(): refresh = RefreshToken(refresh_token) response.set_cookie('access_token', refresh.access_token) response.set_cookie('refresh_token', refresh) except: pass if response.status_code == 401: refresh_token = request.COOKIES.get('refresh_token') if refresh_token: print("refresh_token = = = = = ", refresh_token) try: decoded = RefreshToken(refresh_token, verify=False) if decoded: refresh = RefreshToken(refresh_token) response.set_cookie('access_token', refresh.access_token) response.set_cookie('refresh_token', refresh) response.status_code = 200 except: pass return response
但当access token过期时,会收到如下错误响应:
{ "detail": "Given token not valid for any token type", "code": "token_not_valid", "messages": [ { "token_class": "AccessToken", "token_type": "access", "message": "Token is invalid or expired" } ] }
只有第二次刷新页面后,已认证的用户信息才会正常显示,请问该如何解决?
解决方案
核心问题:中间件执行时机错误
你当前的中间件用了process_response,这个方法是在视图处理完成、已经生成响应之后才执行的。也就是说,第一次请求时,视图已经因为无效的access token返回了401,你才去刷新token并修改响应状态码,但此时视图返回的错误内容已经无法改变,所以前端还是会收到401。第二次请求时,新的token已经存在cookie里,所以能正常认证。
正确的做法是在视图处理之前检查token有效性并刷新,也就是用process_request方法。
修复后的中间件代码
from datetime import datetime, timezone from rest_framework_simplejwt.tokens import RefreshToken, AccessToken from django.utils.deprecation import MiddlewareMixin from rest_framework_simplejwt.exceptions import TokenError class TokenRefreshMiddleware(MiddlewareMixin): def process_request(self, request): # 尝试获取当前的access token access_token = request.COOKIES.get('access_token') refresh_token = request.COOKIES.get('refresh_token') if access_token: try: # 验证access token是否有效(自动检查过期) AccessToken(access_token) # token有效,直接跳过后续逻辑 return None except TokenError: # access token无效/过期,进入刷新流程 pass # 用refresh token尝试刷新 if refresh_token: try: refresh = RefreshToken(refresh_token) # 生成新的access和refresh token new_access_token = str(refresh.access_token) new_refresh_token = str(refresh) # 更新请求的Authorization头,让后续认证流程能拿到有效token request.META['HTTP_AUTHORIZATION'] = f'Bearer {new_access_token}' # 把新token暂存到请求对象,后续在响应阶段设置cookie request._new_tokens = (new_access_token, new_refresh_token) except TokenError: # refresh token也无效,标记需要清除cookie request._clear_tokens = True return None def process_response(self, request, response): # 在响应阶段设置新的cookie if hasattr(request, '_new_tokens'): access_token, refresh_token = request._new_tokens # 根据你的实际需求配置cookie属性(httponly、secure、samesite等) response.set_cookie( 'access_token', access_token, httponly=True, secure=True, samesite='Lax', max_age=3600 # 对应access token的过期时间(1小时) ) response.set_cookie( 'refresh_token', refresh_token, httponly=True, secure=True, samesite='Lax', max_age=7*24*3600 # 对应refresh token的过期时间(7天) ) # 如果refresh token无效,清除cookie if hasattr(request, '_clear_tokens'): response.delete_cookie('access_token') response.delete_cookie('refresh_token') return response
关键改进点
- 提前处理token刷新:在
process_request中先检查access token有效性,无效则立即用refresh token刷新,并把新token注入到请求头中,确保后续视图处理时能通过认证。 - 安全验证token:不再用
verify=False跳过验证,而是通过捕获TokenError异常来判断token是否有效,避免安全风险。 - 分离逻辑:把token刷新逻辑放在请求阶段,cookie设置放在响应阶段,符合Django中间件的执行流程。
- 处理refresh token无效场景:当refresh token也过期时,自动清除cookie,避免无效请求循环。
额外注意事项
- 确保你的认证类(比如
JWTAuthentication)是从Authorization头获取token的,这样修改request.META['HTTP_AUTHORIZATION']才会生效。 - 生产环境中务必开启
secure=True(仅HTTPS下传输cookie),并根据需求设置samesite属性,提升安全性。 - 调整
max_age值,使其与你在simplejwt配置中设置的token过期时间一致。
内容的提问来源于stack exchange,提问作者Bif_Titan
相关产品推荐
相关产品推荐

