jwt多用户并发刷新导致token冲突,本质是竞态问题,需前端统一管理刷新时机、服务端提供幂等接口并校验用户身份、禁用自动refresh中间件、增加限流与黑名单机制。

JWT 令牌在多用户场景下重复刷新引发冲突,本质是多个并发请求同时触发 auth()->refresh(),导致旧 token 被覆盖、新 token 生效但部分请求仍携带已失效的“中间态”token,进而返回 401 或 TokenExpiredException。这不是 Laravel 或 jwt-auth 的 bug,而是无状态认证中典型的竞态问题,需从机制设计层面解决。
避免并发刷新:统一由前端主动管理刷新时机
后端不应被动响应每个过期请求都执行 refresh,而应让前端承担“是否该刷新”的判断责任。服务端只提供标准刷新接口(如 POST /api/token/refresh),并确保该接口幂等、不依赖隐式上下文。
- 前端在收到
401 + {"message":"Token has expired"}后,暂停所有待发请求,先调用刷新接口获取新 token - 刷新成功后,用新 token 重放原请求;失败则跳转登录页
- 禁止在中间件里自动 refresh 并透传新 token 到后续逻辑——这会掩盖真实过期路径,且无法控制并发
服务端刷新接口必须幂等且隔离用户上下文
刷新动作必须绑定明确的用户身份,不能仅靠 header 中的 token 解析,否则当多个设备共用同一账号时易混淆。
- 刷新接口应要求携带原始 token,并在校验通过后,从 payload 提取
sub(即 user_id),再查库确认用户未被禁用或登出 - 返回的新 token 不应继承旧 token 的全部 claims,尤其要重置
jti(唯一标识)、更新iat、缩短exp(例如设为 2 小时) - 建议在数据库中维护
user_tokens表,记录每次 refresh 的jti和状态(active/expired),便于主动吊销
禁用自动 refresh 中间件,改用显式异常拦截
像知识库中提到的 RefreshToken 中间件,在高并发下会因多次 $this->auth->refresh() 调用产生 token 覆盖风险。应弃用此类“透明刷新”逻辑。
- 路由统一使用
auth:api中间件,仅做校验,不过期则放行,过期则直接抛TokenExpiredException - 全局异常处理器中捕获该异常,统一返回结构化响应:
{"code":401,"message":"token_expired","need_refresh":true} - 不在此处调用
refresh(),把决策权交给前端,消除服务端并发刷新可能
补充防护:对 refresh 接口加简单限流与防重放
即使刷新由前端发起,也要防止恶意脚本高频调用导致 token 泛滥或 DB 压力。
- 给
/api/token/refresh加throttle:5,1中间件,每分钟最多刷新 5 次 - 刷新请求头中可选带
X-Request-ID,服务端用 Redis 缓存该 ID(有效期 60 秒),重复则拒绝 - 每次 refresh 成功后,将旧 token 的
jti写入 Redis 黑名单(TTL = 原 exp - 当前时间 + 30 秒),后续校验 token 前先查黑名单











