Laravel JWT多用户分别刷新令牌实现完整代码

雨浩姑娘_9299

雨浩姑娘_9299

2026-08-19

206人浏览

原创

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

laravel jwt多用户分别刷新令牌实现完整代码

在 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:

Laravel
Laravel

避免常见的Laravel错误:N+1查询、批量赋值、缓存陷阱及队列序列化陷阱。

下载
  • 生成 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 刷新就稳了。

相关文章

PHP速学视频免费教程(入门到精通)
PHP速学视频免费教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

laravel

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
php文件怎么打开
php文件怎么打开

打开php文件步骤:1、选择文本编辑器;2、在选择的文本编辑器中,创建一个新的文件,并将其保存为.php文件;3、在创建的PHP文件中,编写PHP代码;4、要在本地计算机上运行PHP文件,需要设置一个服务器环境;5、安装服务器环境后,需要将PHP文件放入服务器目录中;6、一旦将PHP文件放入服务器目录中,就可以通过浏览器来运行它。

2023.09.01

9564

6

php怎么取出数组的前几个元素
php怎么取出数组的前几个元素

取出php数组的前几个元素的方法有使用array_slice()函数、使用array_splice()函数、使用循环遍历、使用array_slice()函数和array_values()函数等。本专题为大家提供php数组相关的文章、下载、课程内容,供大家免费下载体验。

2023.10.11

5761

5

php反序列化失败怎么办
php反序列化失败怎么办

php反序列化失败的解决办法检查序列化数据。检查类定义、检查错误日志、更新PHP版本和应用安全措施等。本专题为大家提供php反序列化相关的文章、下载、课程内容,供大家免费下载体验。

2023.10.11

2055

5

php怎么连接mssql数据库
php怎么连接mssql数据库

连接方法:1、通过mssql_系列函数;2、通过sqlsrv_系列函数;3、通过odbc方式连接;4、通过PDO方式;5、通过COM方式连接。想了解php怎么连接mssql数据库的详细内容,可以访问下面的文章。

2023.10.23

3588

4

php连接mssql数据库的方法
php连接mssql数据库的方法

php连接mssql数据库的方法有使用PHP的MSSQL扩展、使用PDO等。想了解更多php连接mssql数据库相关内容,可以阅读本专题下面的文章。

2023.10.23

4274

6

html怎么上传
html怎么上传

html通过使用HTML表单、JavaScript和PHP上传。更多关于html的问题详细请看本专题下面的文章。php中文网欢迎大家前来学习。

2023.11.03

3351

9

PHP出现乱码怎么解决
PHP出现乱码怎么解决

PHP出现乱码可以通过修改PHP文件头部的字符编码设置、检查PHP文件的编码格式、检查数据库连接设置和检查HTML页面的字符编码设置来解决。更多关于php乱码的问题详情请看本专题下面的文章。php中文网欢迎大家前来学习。

2023.11.09

4757

8

php文件怎么在手机上打开
php文件怎么在手机上打开

php文件在手机上打开需要在手机上搭建一个能够运行php的服务器环境,并将php文件上传到服务器上。再在手机上的浏览器中输入服务器的IP地址或域名,加上php文件的路径,即可打开php文件并查看其内容。更多关于php相关问题,详情请看本专题下面的文章。php中文网欢迎大家前来学习。

2023.11.13

3722

8

sprintf函数用法详解
sprintf函数用法详解

sprintf函数的用法:1、格式化字符串;2、指定输出宽度和精度;3、返回值。更多关于sprintf函数用法详解的内容,大家可以阅读下面的文章。

2023.11.27

11722

4

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
如何安装 Composer
如何安装 Composer

共1课时 | 194人学习

Composer手册
Composer手册

共0课时 | 0人学习