多用户模型需为每个模型配置独立 guard + provider 组合,而非共用 jwt 驱动;须在 auth.php 定义多个 guard 及对应 provider,各模型实现 jwtsubject 并注入 guard 标识,所有 auth 操作及中间件必须显式指定 guard,黑名单与缓存需按 guard 分片隔离。

多用户模型共用 JWT 认证驱动,本质不是“共用”,而是**为每个模型配置独立 guard + provider 组合**。Laravel 的 JWT(如 tymon/jwt-auth)本身不支持“一驱多模”,强行复用同一 guard 会导致 token 冲突、黑名单误删、payload 混淆等安全问题。关键在于隔离而非共享。
1. 在 auth.php 中定义多个 guard
不能只改 api 这一个 guard,需为 users、admins、merchants 等各自声明:
'users' => ['driver' => 'jwt', 'provider' => 'users']'admins' => ['driver' => 'jwt', 'provider' => 'admins']'merchants' => ['driver' => 'jwt', 'provider' => 'merchants']
注意:guard 名必须全局唯一,且后续所有地方(中间件、Auth::guard()、路由守卫)都必须显式使用该名称。
2. 为每个 guard 配置对应 provider
在 auth.providers 下新增 provider,确保 model 路径精准指向对应模型:
'users' => ['driver' => 'eloquent', 'model' => App\Models\User::class]'admins' => ['driver' => 'eloquent', 'model' => App\Models\Admin::class]'merchants' => ['driver' => 'eloquent', 'model' => App\Models\Merchant::class]
provider 名(如 admins)必须与 guard 中的 'provider' => 'admins' 完全一致,大小写敏感。
3. 各模型实现 JWTSubject 并注入 guard 标识
每个模型都要实现 JWTSubject 接口,并在 getJWTCustomClaims() 中写入 guard 名,用于后续 token 隔离:
public function getJWTCustomClaims()
{
return [
'guard' => 'admin', // 与 guard 名一致
'type' => 'admin'
];
}
这个字段会进入 token payload,是后续黑名单分片、刷新校验、中间件路由判断的核心依据。
4. 登录、刷新、登出必须指定 guard
所有 Auth 操作都不能走默认 guard,否则会命中错误模型或 token 存储区:
- 登录:
Auth::guard('admin')->login($admin) - 刷新:
Auth::guard('admin')->refresh() - 登出:
Auth::guard('admin')->logout() - 获取用户:
Auth::guard('admin')->user()
中间件也必须带 guard 名:middleware('auth:admin'),不能只写 auth:api 或 auth。
5. 黑名单与缓存需按 guard 分片
tymon/jwt-auth 的 Blacklist 是单例,默认不区分 guard。若不处理,Auth::guard('admin')->logout() 可能清掉 user 的 token。
解决方案有两种:
- 重写
JWTBlacklist::add(),用$payload['guard']作为 cache key 前缀,例如:cache()->put("blacklist_{$guard}_{$jti}", ...) - 或在数据库 blacklist 表中新增
guard字段,并在 add / has 方法中加入 where 条件
刷新逻辑也要检查 payload 中的 guard 字段是否匹配当前 guard,防止跨模型 token 复用。











