遇到 Flask-JWT 刷新令牌报 Token revoked 错误,通常是因为令牌的唯一标识(JTI)被记录在了撤销列表(黑名单)中,或者回调函数逻辑误判了令牌状态。
先说结论:这个问题大多发生在启用了令牌撤销功能的项目中,重点检查黑名单存储状态和回调逻辑性能。
- 先确认:检查配置中是否开启了 JWT_TOKEN_BLOCKLIST_ENABLED
- 先处理:清理测试环境的黑名单存储或修正 token_in_blacklist_loader 逻辑(务必使用 SISMEMBER)
- 再验证:重新获取令牌并尝试访问受保护接口
快速处理思路
由于这是代码逻辑问题,没有通用的系统命令,建议按以下顺序排查:
1. 检查 Flask 配置项 JWT_TOKEN_BLOCKLIST_ENABLED 是否为 True
2. 查看 Redis 或数据库中是否存入了该令牌的 JTI
3. 确认 token_in_blacklist_loader 函数返回值是否正确且高效为什么会这样
Flask-JWT-Extended 库提供了令牌撤销机制,原理是将令牌的 JTI(JWT ID)存入黑名单存储(如 Redis 或数据库)。当请求携带令牌时,系统会调用回调函数查询该 JTI 是否在黑名单中。如果查到记录,就会抛出 Token revoked 异常。
常见触发场景包括:
- 用户主动登出,后端将该令牌加入黑名单
- 密码修改后,策略性撤销旧令牌
- 测试过程中重复使用已撤销的刷新令牌
- 回调逻辑编写错误,导致正常令牌被误判
分步处理
第一步:检查配置开关
确认项目中是否显式开启了黑名单功能。如果没有业务需求,可以关闭该功能以排除干扰。
app.config["JWT_TOKEN_BLOCKLIST_ENABLED"] = False # 临时关闭测试第二步:检查回调逻辑与 Redis 初始化
找到定义 token_in_blacklist_loader 的地方,确认逻辑是否过于严格且高效。官方文档建议在此处查询存储 backend。注意:必须使用 SISMEMBER 而非 SMEMBERS,避免性能问题。
import redis
# Redis 客户端初始化示例
redis_client = redis.Redis(host='localhost', port=6379, db=0, decode_responses=True)
@jwt.token_in_blocklist_loader
def check_if_token_revoked(jwt_header, jwt_payload):
jti = jwt_payload["jti"]
# 使用 sismember 判断单个成员存在性,时间复杂度 O(1)
# sismember 返回 1 或 0,在 Python 中可直接作为布尔值使用
return redis_client.sismember("token_blacklist", jti)第三步:清理黑名单存储(需谨慎)
如果是测试环境,可以直接清空存储中的黑名单数据。生产环境严禁直接使用 DEL 清空整个集合,这将导致所有已撤销令牌立即恢复生效,造成安全漏洞。建议依靠 TTL 自动过期。
# 测试环境 Redis 示例命令
redis-cli DEL token_blacklist
# 生产环境建议:在添加黑名单时设置过期时间(TTL)
# 示例:redis_client.expire("token_blacklist", access_token_expires)第四步:检查令牌类型
确认报错的是访问令牌(access token)还是刷新令牌(refresh token)。有时逻辑中只撤销了 access token,但代码误操作了 refresh token。
怎么验证是否生效
完成调整后,按以下步骤验证:
- 重新调用登录接口,获取新的 access token 和 refresh token。
- 使用新 token 访问需要认证的接口,观察是否返回 200 状态码。
- 查看应用日志,确认不再出现
TokenRevoked异常堆栈。 - 如果是刷新接口报错,尝试用新 refresh token 换取 access token,确认流程通畅。
常见坑
- 性能隐患:使用
SMEMBERS会将整个黑名单集合加载到内存,数据量大时导致内存溢出或性能下降,务必改用SISMEMBER。 - 存储连接失败:如果黑名单存储(如 Redis)连接超时,回调函数可能抛出异常或被默认视为 revoked,建议做好异常捕获。
- 生产环境误操作:直接删除黑名单集合会导致已登出用户令牌重新生效,清理时需确认范围。
- 过期清理缺失:黑名单数据应设置过期时间(TTL),否则存储会无限增长,建议 TTL 与令牌有效期一致。
参考来源
- Flask-JWT-Extended Documentation, Token Revocation (Blacklisting), https://flask-jwt-extended.readthedocs.io/en/latest/blacklist.html
- Flask-JWT-Extended GitHub Repository, Issues regarding token revoked, https://github.com/vimalloc/flask-jwt-extended