多用户令牌黑名单需按guard完全隔离,关键在于:1.各模型getjwtcustomclaims()注入'guard'标识;2.黑名单key按guard分片(如blacklist:admin:abc123);3.刷新登出必须显式指定guard;4.中间件路由名须与config/auth.php中guard名严格一致。

多用户令牌黑名单不是加个中间件就能自动隔离,关键在于让每个 guard 的 token 生命周期(签发、校验、刷新、销毁)完全独立,避免 admin 登录导致 user token 失效这类误杀。
1. 每个模型必须注入 guard 标识到 payload
JWT 默认不区分用户类型,所有 token 共用黑名单存储,容易交叉污染。解决办法是在生成 token 时主动写入 guard 名称,作为后续隔离依据。
- 在 Admin、User、Merchant 等模型的 getJWTCustomClaims() 中统一添加
'guard' => 'admin'或'guard' => 'user' - 确保 getJWTIdentifier() 返回标量值(如
$this->getKey()),不能返回对象或 null - 这样每次签发的 token payload 都带明确上下文,例如:
{"sub":1,"guard":"admin","iat":1723546080}
2. 黑名单 key 必须按 guard 分片
tymon/jwt-auth 的黑名单机制默认是全局单例,不感知 guard。若不改造,Auth::guard('admin')->logout() 可能清掉 user 的 token。
- 重写
JWTBlacklist::add()方法,在 cache key 或数据库字段中拼接$payload['guard'],例如:blacklist:admin:abc123和blacklist:user:def456 - 同理修改
isBlacklisted()和remove(),确保校验和清理都带 guard 前缀 - 如果用 Redis 存储,直接用不同 key;若用数据库,建议新增
guard字段并建立联合索引
3. 刷新与登出必须显式指定 guard
JWT 的 refresh 和 logout 行为依赖当前 guard 上下文,不指定就会走默认 guard,导致逻辑错乱。
- 登录后获取 token:用
Auth::guard('admin')->login($admin),而非Auth::login($admin) - 刷新 token:调用
Auth::guard('admin')->refresh(),不能只写auth()->refresh() - 登出操作:执行
Auth::guard('admin')->logout(),它会自动把当前 guard 对应的 token 加入黑名单
4. 中间件路由需严格匹配 guard 名
中间件名称和路由声明必须与 config/auth.php 中定义的 guard 名完全一致,否则认证链根本不会触发对应逻辑。
- 在
config/auth.php中定义多个 guard,例如:'admin' => ['driver' => 'jwt', 'provider' => 'admins'] - 路由中使用
middleware('auth:admin'),而不是middleware('auth:api')或middleware('jwt.auth') - 确保对应 provider 已配置,且 model 实现了
JWTSubject接口











