laravel多模型jwt访问控制需为user、admin、merchant等模型配置独立guard、provider及token生命周期,并在路由、中间件、模型和登出刷新中显式指定guard,防止越权与token冲突。

Laravel 实现多模型 JWT 访问控制,核心不是“一个 token 适配所有模型”,而是让每个用户类型(如 User、Admin、Merchant)拥有独立的认证上下文——包括独立的 guard、provider、token 生命周期和黑名单隔离。路由分组鉴权必须与之对齐,否则会出现越权、token 冲突或 401 频发。
明确区分 guard 是前提
Laravel 的 auth:xxx 中间件本质是调用 Auth::guard('xxx'),而 tymon/jwt-auth(或兼容方案)的 token 解析、签发、刷新、黑名单操作都绑定在该 guard 实例上。若未显式声明 guard 名,系统默认走 config/auth.php 中 defaults.guard,极易导致 admin 登录后 user 的 token 失效,或中间件误判身份。
路由分组需按 guard 显式声明
不能只写 middleware('auth:api'),而要为不同资源指定对应 guard:
// api.php
Route::prefix('admin')->group(function () {
Route::post('login', [AdminAuthController::class, 'login']);
Route::middleware('auth:admin')->group(function () {
Route::get('dashboard', [AdminController::class, 'index']);
Route::get('users', [UserController::class, 'list']);
});
});
Route::prefix('merchant')->group(function () {
Route::post('login', [MerchantAuthController::class, 'login']);
Route::middleware('auth:merchant')->group(function () {
Route::get('products', [ProductController::class, 'index']);
Route::post('orders', [OrderController::class, 'store']);
});
});
Route::prefix('user')->group(function () {
Route::post('login', [UserAuthController::class, 'login']);
Route::middleware('auth:user')->group(function () {
Route::get('profile', [ProfileController::class, 'show']);
Route::post('addresses', [AddressController::class, 'store']);
});
});
每个 guard 必须配套完整配置
在 config/auth.php 中补全三要素:guard → provider → model
'guards' => [
'admin' => [
'driver' => 'jwt',
'provider' => 'admins',
],
'merchant' => [
'driver' => 'jwt',
'provider' => 'merchants',
],
'user' => [
'driver' => 'jwt',
'provider' => 'users',
],
],
'providers' => [
'admins' => [
'driver' => 'eloquent',
'model' => App\Models\Admin::class,
],
'merchants' => [
'driver' => 'eloquent',
'model' => App\Models\Merchant::class,
],
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class,
],
],
模型必须实现 JWTSubject 并携带 guard 标识
仅实现接口不够,getJWTCustomClaims() 必须注入 guard 名,用于后续黑名单隔离和 payload 验证:
// App\Models\Admin.php
public function getJWTCustomClaims()
{
return [
'guard' => 'admin',
'type' => 'admin',
];
}
中间件逻辑需感知 guard 上下文
默认 auth:xxx 中间件不校验 payload 中的 guard 字段,容易被伪造。建议在自定义中间件中做二次校验:
// app/Http/Middleware/ValidateGuardInToken.php
public function handle($request, Closure $next, $guard)
{
$token = $request->bearerToken();
if (!$token) {
return response()->json(['error' => 'Token required'], 401);
}
try {
$payload = JWTAuth::setToken($token)->getPayload();
if ($payload['guard'] !== $guard) {
return response()->json(['error' => 'Invalid guard in token'], 401);
}
} catch (\Exception $e) {
return response()->json(['error' => 'Invalid token'], 401);
}
return $next($request);
}
然后注册并使用:
// 在 Kernel.php 的 $routeMiddleware 中
'auth.guard' => \App\Http\Middleware\ValidateGuardInToken::class,
// 路由中
Route::middleware('auth.guard:admin')->group(function () { ... });
刷新与登出必须指定 guardAuth::guard('admin')->refresh() 和 Auth::guard('admin')->logout() 不能省略 guard 参数,否则会操作默认 guard 的 token 存储,造成跨模型干扰。
注意:Laravel 9+ 官方已不推荐 tymon/jwt-auth,若项目较新(如 Laravel 13.2),建议改用 Sanctum + 手动 JWT 签发(如 firebase/php-jwt),或直接采用多 session guard + tokenable 模型设计,避免维护过时包带来的兼容风险。











