Django Simple-JWT中自动刷新Access Token及Token存储问题
Django Simple-JWT 自动刷新Access Token及Refresh Token请求头配置
一、Refresh Token的请求头配置
默认情况下simple-jwt从请求体获取Refresh Token,要改为从请求头传递,需自定义刷新序列化器:
1. 自定义TokenRefreshSerializer
from rest_framework_simplejwt.serializers import TokenRefreshSerializer from rest_framework_simplejwt.exceptions import InvalidToken class CustomTokenRefreshSerializer(TokenRefreshSerializer): def validate(self, attrs): # 优先从自定义请求头X-Refresh-Token获取Refresh Token refresh_token = self.context['request'].headers.get('X-Refresh-Token') if not refresh_token: # 兼容原有请求体传参的方式 refresh_token = attrs.get('refresh') if not refresh_token: raise InvalidToken("Refresh token not provided") attrs['refresh'] = refresh_token return super().validate(attrs)
2. 替换默认刷新端点
在项目urls.py中更新刷新Token的路由,使用自定义序列化器:
from rest_framework_simplejwt.views import TokenRefreshView from .serializers import CustomTokenRefreshSerializer urlpatterns = [ # 其他路由... path('api/token/refresh/', TokenRefreshView.as_view(serializer_class=CustomTokenRefreshSerializer), name='token_refresh'), ]
此后前端发送刷新请求时,可将Refresh Token放在X-Refresh-Token请求头中,无需再放在请求体。
二、实现Access Token过期自动刷新并执行原请求
通过自定义中间件拦截认证失败的请求,尝试用Refresh Token刷新Access Token,再重新执行原请求:
1. 编写自动刷新中间件
from django.http import JsonResponse from rest_framework_simplejwt.authentication import JWTAuthentication from rest_framework_simplejwt.exceptions import AuthenticationFailed, ExpiredSignatureError from rest_framework_simplejwt.serializers import TokenRefreshSerializer class JWTAutoRefreshMiddleware: def __init__(self, get_response): self.get_response = get_response def __call__(self, request): # 排除刷新Token的端点,避免循环调用 if request.path == '/api/token/refresh/': return self.get_response(request) auth_header = request.headers.get('Authorization') refresh_token = request.headers.get('X-Refresh-Token') # 仅当同时携带Access Token和Refresh Token时处理 if auth_header and auth_header.startswith('Bearer ') and refresh_token: try: # 尝试用原Access Token认证 auth = JWTAuthentication() user, token = auth.authenticate(request) except ExpiredSignatureError: # Access Token过期,尝试刷新 try: refresh_serializer = TokenRefreshSerializer( data={'refresh': refresh_token}, context={'request': request} ) refresh_serializer.is_valid(raise_exception=True) new_access_token = refresh_serializer.validated_data['access'] # 更新请求头中的Access Token request.META['HTTP_AUTHORIZATION'] = f'Bearer {new_access_token}' # 重新认证并绑定用户到请求对象 auth = JWTAuthentication() user, token = auth.authenticate(request) request.user = user except AuthenticationFailed: # Refresh Token无效,返回401 return JsonResponse({'detail': '无效或过期的Refresh Token'}, status=401) except AuthenticationFailed: # Access Token无效(非过期),返回401 return JsonResponse({'detail': '无效的Access Token'}, status=401) # 执行原请求 response = self.get_response(request) # 将新的Access Token通过响应头返回给前端,方便前端更新存储 if 'new_access_token' in locals(): response['X-New-Access-Token'] = new_access_token return response
2. 注册中间件
在settings.py的MIDDLEWARE列表中添加自定义中间件,注意要放在AuthenticationMiddleware之后:
MIDDLEWARE = [ # 其他中间件... 'django.contrib.auth.middleware.AuthenticationMiddleware', 'your_app_name.middleware.JWTAutoRefreshMiddleware', # 替换为你的应用路径 # 其他中间件... ]
三、关键注意事项
- 前端需监听响应头的
X-New-Access-Token,更新本地存储的Access Token,避免后续请求仍使用过期Token。 - 确保Refresh Token的有效期配置合理(在
settings.py的SIMPLE_JWT中设置REFRESH_TOKEN_LIFETIME),同时可启用Refresh Token旋转(ROTATE_REFRESH_TOKENS = True),避免重复使用同一Refresh Token。 - 若Refresh Token过期或被撤销,后端会返回401,前端需引导用户重新登录。
内容的提问来源于stack exchange,提问作者chocho
相关产品推荐
相关产品推荐

