init medical training project
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
from rest_framework.permissions import AllowAny
|
||||
from rest_framework_simplejwt.views import TokenRefreshView
|
||||
from rest_framework_simplejwt.tokens import RefreshToken
|
||||
from rest_framework_simplejwt.exceptions import TokenError
|
||||
from drf_spectacular.utils import extend_schema
|
||||
|
||||
from config.exceptions import AppError
|
||||
from apps.user.utils.jwt_redis import revoke_token, is_token_revoked, get_user_invalid_before
|
||||
|
||||
|
||||
@extend_schema(tags=['认证'])
|
||||
class CustomTokenRefreshView(TokenRefreshView):
|
||||
"""U8 刷新 Token — 在 simplejwt 旋转前后加入 Redis 黑名单检查 + 旧 token 吊销"""
|
||||
permission_classes = [AllowAny]
|
||||
authentication_classes = ()
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
refresh_raw = request.data.get('refresh', '')
|
||||
if not refresh_raw:
|
||||
raise AppError('AUTH_TOKEN_INVALID', '请提供 refresh token', status_code=401)
|
||||
|
||||
# ── 解析旧 token(必须在 super().post() 之前,因为 simplejwt 会 mutate) ──
|
||||
try:
|
||||
old_token = RefreshToken(refresh_raw)
|
||||
except TokenError:
|
||||
raise AppError('AUTH_TOKEN_INVALID', 'refresh token 无效或已过期', status_code=401)
|
||||
|
||||
old_jti = old_token.payload.get('jti')
|
||||
old_exp = old_token.payload.get('exp')
|
||||
uid = old_token.payload.get('user_id')
|
||||
iat = old_token.payload.get('iat')
|
||||
|
||||
# ── Redis 黑名单检查 ──
|
||||
if old_jti and is_token_revoked(old_jti):
|
||||
raise AppError('AUTH_TOKEN_INVALID', 'refresh token 已被吊销', status_code=401)
|
||||
|
||||
# ── 用户级失效截止检查 ──
|
||||
if uid and iat is not None:
|
||||
invalid_before = get_user_invalid_before(uid)
|
||||
if invalid_before is not None and iat < invalid_before:
|
||||
raise AppError('AUTH_TOKEN_INVALID', 'token 已失效,请重新登录', status_code=401)
|
||||
|
||||
# ── 交给 simplejwt 处理旋转 ──
|
||||
response = super().post(request, *args, **kwargs)
|
||||
|
||||
# ── 吊销旧 refresh token ──
|
||||
if old_jti and old_exp:
|
||||
revoke_token(old_jti, old_exp)
|
||||
|
||||
return response
|
||||
|
||||
|
||||
# 函数式引用,供 urls.py 保持一致风格
|
||||
refresh_token = CustomTokenRefreshView.as_view()
|
||||
Reference in New Issue
Block a user