jwt刷新关键在验证refresh_token未撤销、未过期且身份一致,须哈希查库校验并立即作废旧token;推荐access_token 15分钟、refresh_token 7天(每次刷新轮换),时间戳统一用utc,前端需防无限重试与xss风险。

JWT过期时前端发起刷新请求,后端如何验证refresh_token有效性
关键不是“生成新token”,而是验证refresh_token是否未被撤销、未过期、且与原access_token绑定的用户身份一致。常见错误是只校验签名和过期时间,却忽略存储层的状态检查。
实操建议:
- 把
refresh_token存进数据库(如Redis或带TTL的PostgreSQL表),字段至少包含:token_hash(SHA256加盐哈希)、user_id、expires_at、is_revoked - 收到
/auth/refresh请求时,先用hashlib.sha256(refresh_token + SECRET_KEY).hexdigest()算出哈希值,再查库;不直接存明文token - 验证通过后,立即调用
revoke_old_refresh_token()逻辑:更新原记录is_revoked = True,并生成新的refresh_token返回——避免单点长期有效
access_token短时效 + refresh_token长时效的典型配置值
不是越长越安全,也不是越短越合理。核心矛盾在于:缩短access_token有效期能降低被盗用风险,但太短会导致频繁刷新,增加网络开销和时钟漂移问题。
推荐组合(生产环境实测可行):
-
access_token:15分钟(exp设为datetime.utcnow() + timedelta(minutes=15)) -
refresh_token:7天(Redis中设置EXPIRE为604800秒),但强制要求每次刷新都换新,实际业务生命周期通常只有1–2天 - 注意:所有时间戳必须用UTC,禁止用
datetime.now(),否则部署在不同时区服务器上会校验失败
前端在401响应后自动触发refresh流程,但容易陷入无限重试
典型错误是拦截所有401后无条件发refresh请求,结果refresh接口也返回401(比如refresh_token已失效),导致前端反复调用,卡死UI。
必须加三层防护:
- 前端维护一个
isRefreshing标志位,同一时刻只允许一个refresh请求发出 - 对refresh接口单独处理:若它返回401或403,清空本地所有token并跳转登录页,不重试
- 在axios拦截器里用
Promise队列缓存原始请求,等新access_token拿到后再统一重放;别用async/await简单重发,会丢失并发请求上下文
为什么不能把refresh_token放进HttpOnly Cookie却允许access_token走Header
因为refresh_token是长期凭证,一旦泄露等于永久接管账户;而access_token本身短命,且服务端可随时废止。把refresh_token放Cookie看似防XSS,但若没配SameSite=Strict和Secure,反而可能被CSRF利用。
更稳妥的做法:
-
refresh_token只通过HTTPS POST body传,后端校验Origin和Referer头(需前端同域) -
access_token走Authorization: Bearer <token></token>Header,便于前后端分离部署 - 绝对不要在localStorage里存
refresh_token——XSS一击即溃
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











