在Django中使用JWT认证实现登出:解决token仍可用的问题
DRF + Simple JWT 登出后黑名单Token仍生效的解决办法
1. 检查核心配置是否正确
首先确认Simple JWT的黑名单模块已正确配置:
- 在
INSTALLED_APPS中添加rest_framework_simplejwt.token_blacklist - 在项目配置中开启黑名单功能:
SIMPLE_JWT = { 'BLACKLIST_AFTER_ROTATION': True, # 其他JWT配置... } - 确保DRF的认证类配置包含Simple JWT的认证器:
REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': [ 'rest_framework_simplejwt.authentication.JWTAuthentication', # 其他认证类... ], }
2. 修正登出接口逻辑
不要手动创建BlacklistedToken记录,必须使用Simple JWT内置的blacklist()方法处理Refresh Token:
from rest_framework_simplejwt.tokens import RefreshToken from rest_framework.response import Response from rest_framework.decorators import api_view, permission_classes from rest_framework.permissions import IsAuthenticated @api_view(['POST']) @permission_classes([IsAuthenticated]) def logout(request): try: # 从请求体获取refresh token refresh_token = request.data.get("refresh") if not refresh_token: return Response({"error": "Refresh token required"}, status=400) token = RefreshToken(refresh_token) # 调用内置方法加入黑名单 token.blacklist() return Response(status=205) except Exception as e: return Response({"error": str(e)}, status=400)
该方法会自动关联OutstandingToken并生成有效的黑名单记录,避免手动操作导致的关联缺失。
3. 验证黑名单记录是否正确生成
通过Django Shell确认Token已被加入黑名单:
from rest_framework_simplejwt.token_blacklist.models import BlacklistedToken, OutstandingToken # 查询目标refresh token对应的OutstandingToken outstanding_token = OutstandingToken.objects.filter(token__exact="你的refresh_token").first() if outstanding_token: # 检查是否已加入黑名单 is_blacklisted = BlacklistedToken.objects.filter(token=outstanding_token).exists() print(f"Token是否在黑名单: {is_blacklisted}") else: print("未找到对应的OutstandingToken,可能token已过期或未被正确记录")
如果OutstandingToken不存在,说明该Refresh Token已过期或从未被系统记录,此时无法加入黑名单。
4. 处理Access Token的即时失效问题
注意:Simple JWT的黑名单默认只限制Refresh Token和后续生成的Access Token,已签发的Access Token在过期前仍会生效(出于性能考虑,默认不实时校验Access Token的黑名单)。
若需要让Access Token立即失效,可通过以下方式实现:
- 在用户模型中添加
last_logout字段:from django.db import models from django.contrib.auth.models import AbstractUser from django.utils import timezone class CustomUser(AbstractUser): last_logout = models.DateTimeField(null=True, blank=True) - 自定义认证类,校验Access Token的签发时间是否早于用户的登出时间:
from rest_framework_simplejwt.authentication import JWTAuthentication from rest_framework.exceptions import AuthenticationFailed class CustomJWTAuthentication(JWTAuthentication): def authenticate(self, request): user, token = super().authenticate(request) if user.last_logout and token.payload['iat'] < user.last_logout.timestamp(): raise AuthenticationFailed("Token已失效") return user, token - 在DRF配置中替换默认认证类为自定义类,并在登出接口更新
last_logout:# 登出接口中添加 user = request.user user.last_logout = timezone.now() user.save()
5. 排查缓存影响
如果项目启用了缓存,可能导致Token校验结果被缓存,可在JWT配置中关闭缓存:
SIMPLE_JWT = { # 其他配置... 'USE_CACHE': False, }
或者手动清理缓存,确保新的黑名单规则生效。
内容的提问来源于stack exchange,提问作者Ricardo Arenas
相关产品推荐
相关产品推荐

