基于用户无活动的Token过期实现咨询(DRF+Simple JWT)
Django REST Framework + Simple JWT 实现无活动自动过期Token方案
核心实现思路
JWT本身是无状态令牌,服务端无法主动触发过期通知,只能通过客户端操作触发API请求时校验,结合自定义Payload和认证逻辑来实现无活动过期。以下是具体步骤:
1. 自定义JWT Payload,添加活动时间字段
在生成Token时,给Payload加入last_activity字段记录用户最后操作时间,每次用户发起请求或刷新Token时更新该字段。
自定义Token序列化器
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer, TokenRefreshSerializer from rest_framework_simplejwt.tokens import RefreshToken from datetime import datetime class CustomTokenObtainPairSerializer(TokenObtainPairSerializer): @classmethod def get_token(cls, user): token = super().get_token(user) # 初始化为当前时间 token['last_activity'] = datetime.now().isoformat() return token class CustomTokenRefreshSerializer(TokenRefreshSerializer): def validate(self, attrs): data = super().validate(attrs) # 刷新Token时更新last_activity refresh = RefreshToken(attrs['refresh']) refresh['last_activity'] = datetime.now().isoformat() data['refresh'] = str(refresh) # 同步更新Access Token的活动时间 access = refresh.access_token access['last_activity'] = datetime.now().isoformat() data['access'] = str(access) return data
在Settings中配置自定义序列化器
SIMPLE_JWT = { 'TOKEN_OBTAIN_SERIALIZER': 'your_app.serializers.CustomTokenObtainPairSerializer', 'TOKEN_REFRESH_SERIALIZER': 'your_app.serializers.CustomTokenRefreshSerializer', }
2. 自定义认证逻辑,校验无活动过期
继承JWTAuthentication,在认证过程中检查last_activity与当前时间的差值,超过30分钟则拒绝请求。
自定义认证类
from rest_framework_simplejwt.authentication import JWTAuthentication from rest_framework.exceptions import AuthenticationFailed from datetime import datetime, timedelta class ActivityBasedJWTAuthentication(JWTAuthentication): def authenticate(self, request): user, token = super().authenticate(request) last_activity_str = token.payload.get('last_activity') if not last_activity_str: raise AuthenticationFailed("无效Token:缺少活动时间戳") last_activity = datetime.fromisoformat(last_activity_str) # 校验是否超过30分钟无活动 if datetime.now() - last_activity > timedelta(minutes=30): raise AuthenticationFailed("Token因长时间无操作过期") # 更新活动时间并生成新的Access Token,返回给客户端更新 token.payload['last_activity'] = datetime.now().isoformat() new_access_token = token.encode() # 通过请求头传递新Token,客户端需监听并更新本地存储 request.META['HTTP_AUTHORIZATION'] = f'Bearer {new_access_token}' return user, token
在Settings中配置自定义认证类
REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': [ 'your_app.authentication.ActivityBasedJWTAuthentication', ], }
3. 客户端配合处理
因为服务端无法主动推送过期通知,客户端需要做以下配合:
- 每次发起API请求时携带最新的Access Token
- 收到
401 Unauthorized响应时,自动跳转到登录页面 - 可选:设置客户端定时器,每25分钟发起一次轻量API请求(如获取用户基本信息),主动刷新Token的活动时间;若用户30分钟无操作,定时器不触发,下次请求会被服务端拒绝。
额外安全方案建议
- 缩短Access Token有效期:将Access Token默认有效期设为5分钟,Refresh Token设为7天,结合活动校验,降低Token泄露后的风险。配置示例:
SIMPLE_JWT = { 'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5), 'REFRESH_TOKEN_LIFETIME': timedelta(days=7), } - 启用Token黑名单:利用Simple JWT内置的黑名单功能,将过期或登出的Refresh Token加入黑名单,防止复用。需依赖Redis缓存:
SIMPLE_JWT = { 'BLACKLIST_AFTER_ROTATION': True, } # 配置Redis缓存 CACHES = { 'default': { 'BACKEND': 'django_redis.cache.RedisCache', 'LOCATION': 'redis://127.0.0.1:6379/1', 'OPTIONS': { 'CLIENT_CLASS': 'django_redis.client.DefaultClient', } } } - 安全存储Token:用HttpOnly、Secure的Cookie存储Token,替代localStorage,避免XSS攻击窃取Token。配置示例:
SIMPLE_JWT = { 'AUTH_COOKIE': 'access_token', 'AUTH_COOKIE_REFRESH': 'refresh_token', 'AUTH_COOKIE_SECURE': True, # 生产环境开启,仅HTTPS下传输 'AUTH_COOKIE_HTTPONLY': True, 'AUTH_COOKIE_SAMESITE': 'Lax', } - 启用CSRF保护:如果使用Cookie传输Token,需开启DRF的CSRF保护,防止CSRF攻击。
- 定期强制重登录:设置全局规则,比如每7天强制用户重新验证身份,即使持续有活动,也提升账户安全性。
内容的提问来源于stack exchange,提问作者Yaghi
相关产品推荐
相关产品推荐

