"""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)))