Django自定义JWT认证类未遵循AllowAny权限,过期Token拦截问题求助
问题根源分析
你的问题出在Django REST Framework的执行顺序上:DRF会先执行认证类的authenticate方法,完成认证逻辑后才会检查视图的权限类。也就是说,哪怕你的GoogleLogin视图设置了AllowAny,只要认证类在处理过期token时抛出了异常,DRF就会直接返回这个异常的响应,根本不会走到权限检查的环节。
你的JWTAuthentication类在捕获到ExpiredSignatureError时,直接抛出了ParseError('Token has expired'),这就导致带过期token的请求在认证阶段就被拦截,无法进入视图处理登录逻辑。
解决方案
我们需要调整认证类的逻辑:当遇到过期的auth_token时,不要抛出异常,而是返回None(表示认证失败)。这样DRF会继续执行到权限检查环节,AllowAny权限类会允许请求进入视图,而其他需要认证的视图(比如使用IsAuthenticated的视图)则会因为认证失败被拒绝访问,符合预期。
修改后的认证类代码
def get_authorization_header(request): raw_token = request.COOKIES.get('auth_token', ) or None auth = request.META.get('HTTP_AUTHORIZATION', ) if isinstance(auth, str): auth = auth.encode(HTTP_HEADER_ENCODING) return auth class JWTAuthentication(BaseAuthentication): keyword = 'auth_token' def authenticate(self, request): raw_token = request.COOKIES.get('auth_token', ) or None if raw_token is None: return None try: return self.authenticate_credentials(raw_token) except jwt.ExpiredSignatureError: # 过期token返回None,让权限类处理后续逻辑 return None def authenticate_credentials(self, key): user_model = get_user_model() try: payload = jwt.decode(key, settings.SECRET_KEY, algorithms="HS256") user = user_model.objects.get(email=payload['email']) except jwt.DecodeError: raise exceptions.ParseError('Invalid token') except user_model.DoesNotExist: raise exceptions.ParseError('Invalid token') if not user.is_active: raise exceptions.AuthenticationFailed('User inactive or deleted') return (user, payload) def authenticate_header(self, request): return self.keyword
关键修改点说明
- 在
authenticate方法中,新增对jwt.ExpiredSignatureError的捕获,遇到过期token时直接返回None,跳过异常抛出。 - 把原
authenticate_credentials中的ExpiredSignatureError捕获逻辑移到了authenticate方法中,这样过期token不会触发错误响应,而是进入权限检查流程。 - 保留了对无效token(解码失败、用户不存在)的异常抛出,确保这些情况仍然会返回错误提示。
这样调整后,当请求携带过期的auth_token访问GoogleLogin视图时,认证类返回None,AllowAny权限类会允许请求进入视图,你的登录逻辑就能正常执行,重新生成有效的JWT token并设置到cookie中。
内容的提问来源于stack exchange,提问作者Kadiem Alqazzaz




