JWT过期后请求返回401但未触发刷新,主因是仅依赖状态码判断而忽略响应头WWW-Authenticate字段或响应体中的过期关键词;并发刷新导致重复消耗Refresh Token引发403;Refresh Token自身过期或吊销时未清空凭证并引导重新登录。

JWT过期后请求返回401,但没触发刷新逻辑
常见原因是没正确识别认证失败的响应特征。很多接口返回401但响应体是JSON(如{"code": 401, "msg": "Token expired"}),也有些返回401却带Set-Cookie或新Authorization头。只靠状态码判断不够稳定。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 优先检查响应头中的
WWW-Authenticate字段是否含error="invalid_token"或类似提示 - 若响应体为JSON,解析后检查
code、error、message等字段是否匹配过期关键词(如"expired"、"invalid") - 避免仅依赖
response.status_code == 401,某些服务对非法Token返回400或422
刷新Token时并发请求导致重复刷新或403
多个请求几乎同时发现Token过期,各自发起刷新,后发的刷新请求因旧Refresh Token已被首次刷新消耗而失败,返回403 Forbidden或{"error": "refresh_token_revoked"}。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 用线程/协程安全的单例锁控制刷新动作,例如Python中用
threading.Lock或asyncio.Lock - 刷新成功后,立即更新全局持有的
access_token和refresh_token变量,并设置新过期时间戳 - 被阻塞等待的请求不重试原请求,而是用新Token重新发起——否则可能因原始请求参数已失效(如一次性
nonce)而再失败
Refresh Token本身也过期,无法续期
Refresh Token通常有更长有效期(7天/30天),但并非永不过期。用户长期未操作、密码重置、服务端主动吊销都会使其失效。此时刷新接口返回400 + {"error": "invalid_grant"}或401 + "refresh token invalid"。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 捕获刷新失败后,清空本地存储的
access_token和refresh_token - 不要静默重试,应抛出明确异常(如自定义
AuthRefreshFailedError),由上层决定是否引导重新登录或退出 - 若爬虫需长期运行,考虑在初始化阶段检查Refresh Token剩余有效期(从JWT payload解析
exp),提前数小时尝试刷新,避开集中过期高峰
Requests Session中如何安全注入新Token
直接修改session.headers["Authorization"]看似简单,但容易遗漏重试请求、重定向请求或multipart上传等特殊场景,导致部分请求仍带旧Token。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 封装一个
AuthSession类继承requests.Session,重写send()方法,在发送前动态注入最新Authorization头 - 避免使用
session.auth(它只对Basic Auth生效)或硬编码headers参数 - 若用
aiohttp,需在ClientSession.request()前统一处理,不能依赖中间件——因为中间件不覆盖已存在的Authorization头
最麻烦的不是怎么刷Token,而是怎么让所有请求都“知道”该用哪个Token,且不互相干扰。尤其在异步+重试+重定向混用时,Token状态必须全局唯一、更新原子、注入时机精准——漏掉任意一环,就会出现一半请求成功一半401的诡异现象。


















