PayrollManagementSystem/backend/app/services/token_blacklist.py

40 lines
1.5 KiB
Python
Raw Normal View History

"""JWT 登出黑名单。
**TTL 必须等于令牌剩余有效期**:写死固定值会导致「登出 N 分钟后又活了」;
不设过期则 Redis 会随登出次数无限增长。两者都是错的。
**范围限定**:本模块只解决「用户主动登出」。
「禁用账号 / 改密 / 强制下线」由 `users.token_version` 承担 ——
那种场景按用户维度写入 N 个黑名单键既低效,又会在 Redis 故障时完全失效
(那时离职员工仍能用旧令牌查看全员薪酬)。鉴权时二者都要检查。
"""
from app.core.constants import REDIS_KEY_TOKEN_BLACKLIST
from app.core.redis_client import get_redis
def _key(jti: str) -> str:
return REDIS_KEY_TOKEN_BLACKLIST.format(jti=jti)
def revoke(jti: str, remaining_seconds: int) -> None:
"""把令牌加入黑名单。
:param jti: 令牌唯一标识
:param remaining_seconds: 令牌剩余有效期;**必须传真实剩余值**。
非正数表示令牌已自然过期,无需拉黑,本函数直接忽略。
"""
if remaining_seconds <= 0:
return
# 用 set(ex=…) 而非 setex(…):后者在 redis-py 8 已弃用(规范禁用弃用 API)
get_redis().set(_key(jti), "1", ex=remaining_seconds)
def is_revoked(jti: str) -> bool:
"""令牌是否已被拉黑。
Redis 不可用时本函数会抛异常 —— 这是**有意的 fail-closed**:
对能查看全员薪酬的系统,宁可拒绝请求也不能放行无法确定状态的令牌。
"""
return bool(get_redis().exists(_key(jti)))