laravel多用户jwt刷新机制核心是guard隔离:需为admin/user/seller配置独立guard与provider,各用专属密钥和ttl,通过自定义jwtservice按guard动态签发/刷新token,并在payload中嵌入guard标识以确保鉴权不混淆。

在 Laravel 中实现多用户类型(如 admin、user、seller)各自独立的 JWT 刷新机制,核心在于隔离 token 的签发与验证逻辑,避免不同用户类型共用同一密钥或中间件导致冲突。以下为完整、可直接运行的实现方案(基于 laravel/jwt-auth ^1.0 或 tymon/jwt-auth ^2.0 适配版,以主流的 php-jwt + 自定义 guard 方式为主,不依赖已停止维护的老版 jwt-auth 扩展)。
1. 多用户模型与认证守卫配置
假设你有三个用户模型:App\Models\Admin、App\Models\User、App\Models\Seller,需为每类用户定义独立 guard:
- 在
config/auth.php中添加自定义 guards:
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'admin' => [
'driver' => 'jwt',
'provider' => 'admins',
],
'api' => [
'driver' => 'jwt',
'provider' => 'users',
],
'seller' => [
'driver' => 'jwt',
'provider' => 'sellers',
],
],
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class,
],
'admins' => [
'driver' => 'eloquent',
'model' => App\Models\Admin::class,
],
'sellers' => [
'driver' => 'eloquent',
'model' => App\Models\Seller::class,
],
],
- 确保各模型实现
Authenticatable并使用HasApiTokens(Laravel 9+ 推荐手动管理 token); - 每个模型需重写
getJWTIdentifier()和getJWTCustomClaims()(若用 tymon/jwt-auth),但更推荐轻量级自建 JWT 工具类,避开扩展耦合。
2. 独立 Token 签发与刷新服务
创建统一 JWT 服务类,支持按 guard 动态选择密钥、算法、TTL:
- 生成
app/Services/JwtService.php:
namespace App\Services;
use Illuminate\Support\Facades\Hash;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
use Carbon\Carbon;
class JwtService
{
public function issue(string $guard, array $payload): string
{
$config = config("jwt.guards.{$guard}");
$issuedAt = Carbon::now()->timestamp;
$expireAt = $issuedAt + ($config['ttl'] ?? 3600);
$tokenPayload = array_merge([
'iat' => $issuedAt,
'exp' => $expireAt,
'jti' => bin2hex(random_bytes(16)),
'guard' => $guard,
], $payload);
return JWT::encode($tokenPayload, $config['secret'], $config['algorithm'] ?? 'HS256');
}
public function refresh(string $guard, string $oldToken): array
{
$config = config("jwt.guards.{$guard}");
$decoded = JWT::decode($oldToken, new Key($config['secret'], $config['algorithm'] ?? 'HS256'));
// 验证是否为当前 guard 类型且未过期(exp 检查由 decode 自动完成)
if ($decoded->guard !== $guard) {
throw new \Exception('Invalid guard for token');
}
// 生成新 token(可选:加入黑名单旧 token 或校验最后登录时间)
$newToken = $this->issue($guard, [
'sub' => $decoded->sub,
'email' => $decoded->email ?? null,
]);
return [
'access_token' => $newToken,
'expires_in' => config("jwt.guards.{$guard}.ttl", 3600),
];
}
}
- 在
config/jwt.php中定义各 guard 的密钥和有效期(避免硬编码):
return [
'guards' => [
'admin' => [
'secret' => env('JWT_ADMIN_SECRET', 'admin-secret-key-change-in-prod'),
'ttl' => 7200,
'algorithm' => 'HS256',
],
'api' => [
'secret' => env('JWT_USER_SECRET', 'user-secret-key-change-in-prod'),
'ttl' => 3600,
'algorithm' => 'HS256',
],
'seller' => [
'secret' => env('JWT_SELLER_SECRET', 'seller-secret-key-change-in-prod'),
'ttl' => 5400,
'algorithm' => 'HS256',
],
],
];
3. 多 guard 登录与刷新路由
定义清晰路由,显式指定 guard,避免中间件自动猜测:
-
routes/api.php:
use App\Http\Controllers\AuthController;
// 用户登录/刷新
Route::post('/user/login', [AuthController::class, 'userLogin']);
Route::middleware('auth:api')->post('/user/refresh', [AuthController::class, 'userRefresh']);
// 后台管理员
Route::post('/admin/login', [AuthController::class, 'adminLogin']);
Route::middleware('auth:admin')->post('/admin/refresh', [AuthController::class, 'adminRefresh']);
// 商家登录
Route::post('/seller/login', [AuthController::class, 'sellerLogin']);
Route::middleware('auth:seller')->post('/seller/refresh', [AuthController::class, 'sellerRefresh']);
-
app/Http/Controllers/AuthController.php示例方法(精简):
use App\Services\JwtService;
class AuthController extends Controller
{
protected $jwt;
public function __construct(JwtService $jwt)
{
$this->jwt = $jwt;
}
public function userLogin(Request $request)
{
$user = User::where('email', $request->email)->first();
if (!$user || !Hash::check($request->password, $user->password)) {
return response()->json(['message' => 'Unauthorized'], 401);
}
$token = $this->jwt->issue('api', ['sub' => $user->id, 'email' => $user->email]);
return response()->json(['access_token' => $token, 'token_type' => 'Bearer']);
}
public function userRefresh(Request $request)
{
$token = $request()->bearerToken();
$result = $this->jwt->refresh('api', $token);
return response()->json($result);
}
// adminRefresh / sellerRefresh 同理,仅改 guard 名称和模型查询逻辑
4. 安全增强建议
生产环境务必补充以下措施:
- 将 JWT 密钥存于
.env,严禁硬编码; - 每次刷新后将旧 token 加入 Redis 黑名单(key:
blacklist:{$jti},TTL = 原 token 剩余时间),并在refresh()前校验; - 登录时记录设备指纹(User-Agent + IP)并绑定到 token payload,刷新时比对;
- 前端需按 guard 区分存储 token(如 localStorage 中用
token_admin、token_user); - 所有刷新接口必须要求
Authorization: Bearer xxx,禁止传参传递 token。
该方案不依赖第三方 JWT 包,结构清晰、易于测试与维护,各用户类型完全隔离,刷新逻辑互不影响。关键点在于 guard 显式传递、密钥分离、payload 标识 guard 类型——做到这些,多用户 JWT 刷新就稳了。











