PayrollManagementSystem/backend/app/services/token_blacklist.py
liuwenxin 273ebf4657 chore: 初始化项目结构与登录鉴权模块
后端 FastAPI 六层结构(api/services/repositories/models/schemas/core),
统一响应信封、全局异常、日志脱敏;6 张表经 Alembic 迁移建库,SQL 留存
db/migration;登录鉴权含图形验证码(Redis GETDEL 防重放)、JWT、登录限流、
登出黑名单(TTL=令牌剩余有效期)、登录日志。

前端 Vue3 + TS + Element Plus 骨架,含登录页、路由守卫、请求封装与 Vite 代理。

工程配置:uv 锁定 Python 3.12、ruff 检查与格式化、editorconfig、gitattributes。

测试 184 项(单元 155 / 集成 29)全绿,浏览器端到端 8 项通过。

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-09 18:46:36 +08:00

40 lines
1.5 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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