JWT 刷新令牌双令牌机制
解读
国内高并发业务(电商秒杀、直播打赏、小程序登录)普遍要求“无状态+安全+用户体验好”。
单用 Access Token 会面临两难:
- 过期时间短 → 频繁登录,体验差;
- 过期时间长 → 一旦泄露,风险高。
双令牌(Access + Refresh)把“鉴权”与“续签”拆开:
- Access Token:无状态、短效(5~15 min),只存用户标识与基础权限,放在请求头。
- Refresh Token:长效(7~30 天),存在服务端白名单或用户粒度高安全存储,只用来换新的 Access Token。
PHP 场景下,面试官想确认:
- 令牌结构、签名算法、刷新流程;
- 如何防重放、防泄露、防并发刷新;
- 与 Laravel/Symfony 集成、黑名单、续签策略;
- 分布式部署时 Redis 一致性、刷新窗口、滑窗算法。
知识点
- JWT 三段结构:Header.Payload.Signature,Base64Url + 非对称(RS256)或对称(HS256)签名。
- 刷新令牌五要素:jti、uid、scope、iat、exp,必须一次性使用(Rotation)。
- 存储方案:Refresh Token 存 Redis Hash(uid→jti+exp+ip+ua),支持手动踢人、单设备登录。
- 双时间窗:
- Access 过期后 1 min 宽限期,可换 Token 无需登录;
- Refresh 过期前 3 天触发静默续签(滑窗)。
- 安全加固:
- 全部走 HTTPS,JWT 加 ip_hash 绑定;
- 刷新接口限流(uid 维度 10 次/min);
- 异常时整链撤销:把 jti 加入 Redis Bloom 过滤器,网关层直接拒掉。
- PHP 实现细节:
- 使用 firebase/php-jwt 生成与校验;
- Laravel 中在 auth.php 新增 guard:driver=jwt,实现 Illuminate\Contracts\Auth\Guard;
- 中间件分两层:CheckAccess(只验签)、CheckRefresh(验签+白名单+旋转)。
答案
“我上一个直播电商项目日活 120 万,采用双令牌方案,核心流程如下:
- 登录:用户账密+验证码验证通过后,服务端生成 2 个 Token:
- Access:有效期 10 min,Payload 只含 uid、role、ip、jti;
- Refresh:有效期 7 天,Payload 含 uid、jti、rand;jti 作为唯一标识写入 Redis Hash,TTL 比 JWT exp 多 5 min 防止时钟漂移。
- 返回:HttpOnly SameSite=Strict 双 Cookie(X-Access-Token、X-Refresh-Token),同时给移动端多返回一份 JSON,方便自己存 Keychain。
- 请求:网关层统一 CheckAccess 中间件,用公钥 RS256 验签;失败返回 401,前端带 Refresh 调用 /auth/refresh。
- 刷新:
- 验 Refresh 签名→查 Redis 是否存在→比对 ip+ua→生成新 jti;
- 旧 jti 立即删除,新 jti 写入,实现“一次一刷新”;
- 返回新 Access+Refresh,前端原子替换。
- 并发控制:Redis Lua 脚本保证同一 uid 在 1 s 内只能刷新一次,防止 Token 风暴。
- 主动撤销:运营后台踢人时,把对应 jti 写入 Redis 的 Bloom 过滤器,网关层 O(1) 拒绝;用户改密则扫描该 uid 所有 jti 批量删除。
- 滑窗续签:Refresh 剩余有效期 < 3 天且用户活跃时,后台异步任务主动换新 Refresh,用户无感知。
- 灰度与回滚:JWT 公钥放配置中心,支持按版本灰度;紧急情况下直接改公钥,旧 Token 全部失效,强制重新登录。
上线后,接口 99.9% 在 5 ms 内完成验签,日均刷新 800 万次,无 Token 泄露导致的越权事件。”
拓展思考
- 微服务场景:网关层只验签,内部服务用 OAuth2 的 Scope+JWT 做细粒度 RBAC;Refresh 服务独立成“Token Service”,用 gRPC 提供校验接口,避免每个业务都直连 Redis。
- 多端一致性:PC、小程序、App 共用同一 Refresh 池,还是分 Pool?建议分 Pool,互踢逻辑更清;但要在用户中心聚合展示“在线设备”,支持一键退出。
- 合规与审计:国内《个人信息保护法》要求“数据可删除”,JWT 一旦发出无法强制失效,必须靠白名单+短有效期解决;审计日志要记录 jti、uid、ip、ua、expire,保存 6 个月备查。
- 性能极限:单 Redis 实例 QPS 约 5 万,若峰值 30 万,可用 Redis Cluster+本地缓存(随机 1% 采样回源),或把白名单换成 JWT+Session 混合模式,热点用户走 Session,冷用户走无状态。