Laravel中JWT实现多用户类型鉴权的关键在于中间件从token payload提取role/type等自定义字段而非仅依赖sub,需配合自定义claims、独立guard配置及类型感知中间件。

在 Laravel 中通过 JWT 实现多用户类型(如普通用户、管理员、访客)的鉴权,关键不在 token 本身有多“智能”,而在于中间件能否从 token 的 payload 中准确提取并判断用户类型,并据此执行对应逻辑。核心是让中间件能识别 sub 以外的标识字段(如 role、type 或 guard),而不是只依赖数据库查 user 表主键。
从 token payload 提取用户类型字段
JWT 默认只把用户 ID 放进 sub 字段,这对单表认证够用,但多类型场景下必须注入区分标识。推荐在签发时主动写入自定义声明:
- 登录或生成 token 时,用
JWTAuth::fromUser($user)->setClaims(['role' => 'admin'])或JWTFactory::customClaims(['type' => 'merchant']) - 访客 token 可直接构造:
JWTFactory::sub('guest_'.Str::random(8))->claims(['is_guest' => true, 'role' => 'guest'])->make() - 确保模型的
getJWTCustomClaims()返回包含类型信息的数组,例如return ['role' => $this->role ?? 'user'];
编写可识别类型的中间件
不要复用默认 jwt.auth 中间件——它只做基础解析和用户查找,不关心角色。应新建中间件(如 EnsureUserType),在 handle() 中手动解析 token:
OpenClaw Token 成本优化与 API 用量控制专家。用户提及 token 消耗过高、API 费用上涨、模型调用成本高、上下文过长、死循环、定时任务频率、多 Agent 重复请求、模型路由或任何降低运行成本的话题时,立即使用此 Skill。用户说“帮我省钱”时也适用。
- 用
JWTAuth::parseToken()->getPayload()获取完整 payload - 检查
$payload['role']或$payload['type']是否存在且匹配预期值(如in_array($payload['role'], ['admin', 'super_admin'])) - 可结合
Auth::shouldUse('admin')切换 guard,再调用Auth::user()获取对应模型实例 - 拒绝非法类型时直接
return response()->json(['error' => 'Unauthorized role'], 403);
配合 guard 隔离不同用户模型
类型判断只是第一步,后续操作(如 Auth::user()、权限检查)必须落到正确的 guard 和 provider 上:
- 在
config/auth.php中为每类用户定义独立 guard,例如'admin' => ['driver' => 'jwt', 'provider' => 'admins'] - 每个 provider 绑定对应模型(
App\Models\Admin、App\Models\Merchant),且模型需实现JWTSubject - 中间件里根据 payload 类型动态调用
Auth::guard('admin')->user(),而非默认 guard - 路由组可直接指定 guard:
Route::middleware('auth:admin')->group(...),但前提是该 guard 已配置为 jwt driver
避免常见陷阱
很多 401/403 错误其实不是 token 无效,而是类型逻辑没对齐:
- 别在中间件里只检查
Auth::check()——它只反映默认 guard 状态,对多 guard 场景无意义 - 黑名单或刷新逻辑若未按 guard 隔离(如共用一张
blacklist表),会导致 admin 登录后 user token 被误删 - 第三方 JWT(如 OAuth2 提供的 token)不含
sub或sub不是整数 ID,务必改用getJWTIdentifier()返回字符串标识,并确保数据库查询兼容 - 调试时可用
JWTAuth::parseToken()->getPayload()->toArray()打印原始 payload,确认字段名和值是否符合预期

















