40 lines
1.5 KiB
Python
40 lines
1.5 KiB
Python
|
|
"""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)))
|