jwt多表认证本质是多个guard共存,需为users、admins等各模型配置独立guard与provider,实现token签发、校验、黑名单隔离,并确保中间件、路由、请求头三者guard名严格一致。

JWT 多表认证本质是多个 guard 共存,不是改一个 config 就完事
Laravel 的 JWT 认证(比如用 tymon/jwt-auth)默认只配一个 api guard,但“多表”(如 users、admins、merchants)意味着你要让不同模型走不同 guard,各自签发、校验、刷新 token —— 这不是靠改 jwt.php 里的 user 模型路径就能解决的。
核心动作是:为每个用户类型定义独立 guard + provider,并确保每个 guard 绑定正确的 model 和 token storage 行为。
-
config/auth.php中新增多个 guard,例如'admin' => ['driver' => 'jwt', 'provider' => 'admins'] - 对应新增 provider,如
'admins' => ['driver' => 'eloquent', 'model' => App\Models\Admin::class] - 每个 model 必须实现
JWTSubject接口,并重写getJWTIdentifier()和getJWTCustomClaims() - 别漏掉
Auth::guard('admin')->login($admin)—— 不是Auth::login($admin),后者走默认 guard
token 存储和刷新必须按 guard 隔离,否则 admin 登录会踢掉 user
JWT 默认把所有 token 都存在同一个 blacklist 表(或内存缓存),如果 admins 和 users 共用一套黑名单逻辑,Auth::guard('admin')->logout() 可能误删 users 的 token,或者刷新时混用 payload 导致鉴权绕过。
根本原因是 tymon/jwt-auth 的 Blacklist 和 Manager 是单例,不感知 guard 上下文。得手动切片。
- 在每个 model 的
getJWTCustomClaims()中加入区分字段,如'guard' => 'admin' - 重写
JWTBlacklist的add()方法,用$payload['guard']做前缀拼进 cache key 或数据库字段 - 刷新接口(
refresh)必须显式指定 guard:Auth::guard('admin')->refresh(),否则走默认 - 中间件也要带 guard 名:
middleware('auth:admin'),不能只写auth:api
Laravel 10+ 用 laravel/jetstream 或 laravel/sanctum?别混用 JWT
Jetstream 默认用 Sanctum,Sanctum 是 session + token 混合方案,不生成 JWT;强行把 tymon/jwt-auth 塞进去会导致 CSRF、跨域、token 自动续期逻辑冲突。
如果你已经用了 Jetstream,又需要多表 JWT,最稳的路是弃用它的 auth scaffold,自己写登录/注册控制器,用原生 Auth::guard('xxx')->attempt() + createToken() 流程,不碰 HasApiTokens trait。
- Jetstream 的
Fortify配置项(如features)会覆盖你自定义的 guard 行为 -
sanctum的abilities字段无法替代 JWT 的 claim 扩展能力(比如动态权限 scope) - 若坚持用 Sanctum 实现“多表”,只能靠
tokenable_type字段区分模型,但它不支持多 guard 独立签发,也无法做 token 黑名单隔离
前端请求头和路由分组必须严格对齐 guard 名
后端定义了 admin guard,但前端发请求时 header 还是 Authorization: Bearer xxx,没区别;或者路由写成 Route::middleware('auth:api'),那 admin 用户永远 401。
这不是 Laravel 懒,是它设计上就要求 guard 名在三个地方完全一致:配置、中间件、请求解析上下文。
- 登录成功后,后端返回的 token 要附带 guard 标识,比如
{ "token": "...", "guard": "admin" },前端存起来并用于后续请求 - API 路由必须按 guard 分组:
Route::middleware('auth:admin')->group(...),不能全塞进api中间件组 - 如果用 Nginx 或 CDN 做前置鉴权,header 名不能硬编码为
Authorization,得统一加前缀如X-Admin-Authorization,再在中间件里提取 - Postman 测试时,记得在 Authorization tab 选 Bearer Token,而不是 API Key 或其他类型
多表 JWT 最容易卡在“看起来都配对了,但某个 guard 死活不认 token”,八成是中间件名、config 里的 guard 键、路由声明这三者有大小写或拼写错位 —— 比如 admins vs admin,少个 s 就彻底失效。










