必须手动构造、签名和验证JWT或采用AES-CBC加密封装或纯Redis状态管理实现Token刷新。方案一基于hmac-sha256手动实现JWT三段式编解码与校验;方案二用AES-CBC加密JSON载荷并附加MAC;方案三以UUID为token,全量元数据存Redis查表校验。

如果您在 FastAPI 项目中需要实现 Token 刷新机制,但明确要求不依赖 PyJWT 或其他 JWT 封装库,则必须手动构造、签名和验证 JWT 结构。以下是实现该机制的多种可行方案:
一、基于 hmac-sha256 手动构造与验证 JWT
该方法完全绕过第三方 JWT 库,使用 Python 标准库的 hmac 和 base64 模块,按 RFC 7519 规范手动拼接 header、payload、signature 三部分,并完成签名与校验逻辑。
1、定义密钥和算法常量,如 SECRET_KEY = b"your-32-byte-secret-key-here" 且固定使用 HS256。
2、编写 encode 函数:将 header({"typ":"JWT","alg":"HS256"})和 payload(含 exp、jti、sub 等字段)分别 JSON 序列化后 base64url 编码,用点号连接,再对拼接字符串计算 hmac-sha256 并 base64url 编码,最后拼入第三段。
3、编写 decode 函数:按点号分割 token,提取前两段并重新计算 signature,比对第三段是否一致;若一致,则 base64url 解码 payload 并检查 exp 字段是否过期。
4、在刷新接口中,解析旧 refresh_token,验证其 jti 是否未被撤销、exp 是否有效,然后生成新的 access_token 和新的 refresh_token(含新 jti),并将旧 jti 加入 Redis 黑名单。
二、采用对称加密 AES-CBC 手动封装 token 数据
该方法不使用 JWT 格式,而是将用户 ID、过期时间、随机 salt 等结构体序列化为 JSON 后,使用 AES-CBC 加密,再附加 MAC 值防篡改,形成自定义 token 字符串。
1、生成 32 字节密钥和 16 字节 IV,存储于环境变量中,确保密钥永不硬编码在代码中。
2、构造明文字典:{"sub": user_id, "exp": int(time.time()) + 900, "jti": str(uuid4()), "type": "access"},JSON 序列化后填充至 PKCS#7 块大小。
3、使用 AES.new(key, AES.MODE_CBC, iv) 加密,并用 HMAC-SHA256(key, ciphertext) 计算 MAC,将 iv、ciphertext、mac 三者拼接并 base64url 编码输出 token。
4、刷新时解密 refresh_token,校验 MAC,检查 exp 和 type 字段,确认无误后生成一对新 token,同时将原 refresh_token 的 jti 写入 Redis 设置过期时间为原 refresh 过期时间加 7 天。
三、双 token 纯内存状态 + 时间窗口校验方案
该方法放弃 token 自包含性,将所有 token 元数据(user_id、issue_time、expire_time、is_revoked)全部存于 Redis,token 本身仅为随机 UUID 字符串,通过查表完成全部校验逻辑。
1、生成 access_token 时调用 uuid4() 得到字符串,同时向 Redis 写入键 f"at:{token}",值为 JSON 字符串 {"user_id":123,"issued_at":1717023456,"expires_in":900,"scope":"read"},设置 EX 900。
2、生成 refresh_token 时同样使用 uuid4(),写入键 f"rt:{token}",值含 user_id、issued_at、refresh_expires_in(如 2592000 秒)、used_count(初始为 0),EX 设为 30 天。
3、刷新接口接收 refresh_token,先查询 f"rt:{token}" 是否存在且 used_count
4、每次请求中间件拦截 /refresh 路径外的所有路径,从 Authorization 头提取 Bearer token,尝试读取 f"at:{token}",若不存在或已过期则返回 401。










