You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel Passport实现SPA令牌闲置过期及有效期刷新问题咨询

实现Laravel Passport访问令牌的滑动过期(每次使用刷新有效期)

嘿,我之前在做Laravel Passport + SPA的认证时,也碰到过这个滑动过期的需求——默认的固定过期时间确实不够灵活。下面是我实际用过的一套解决方案,亲测可行:

1. 自定义Passport令牌模型,添加「最后使用时间」字段

首先,我们需要追踪令牌的最后使用时间,所以得给Passport的oauth_access_tokens表加个字段,同时自定义令牌模型来支持这个字段。

生成迁移文件

运行Artisan命令创建迁移:

php artisan make:migration add_last_used_at_to_oauth_access_tokens_table

打开生成的迁移文件,添加last_used_at字段:

public function up()
{
    Schema::table('oauth_access_tokens', function (Blueprint $table) {
        $table->timestamp('last_used_at')->nullable();
    });
}

public function down()
{
    Schema::table('oauth_access_tokens', function (Blueprint $table) {
        $table->dropColumn('last_used_at');
    });
}

执行迁移:

php artisan migrate

创建自定义AccessToken模型

在app/Models/Passport目录下创建AccessToken.php(如果目录不存在就新建):

<?php

namespace App\Models\Passport;

use Laravel\Passport\AccessToken as PassportAccessToken;

class AccessToken extends PassportAccessToken
{
    protected $fillable = [
        'name',
        'scopes',
        'revoked',
        'last_used_at',
    ];

    protected $dates = [
        'expires_at',
        'last_used_at',
    ];
}

注册自定义模型

打开app/Providers/AuthServiceProvider.php,在boot方法里添加以下代码,告诉Passport使用我们的自定义模型:

use Laravel\Passport\Passport;
use App\Models\Passport\AccessToken;

public function boot()
{
    $this->registerPolicies();

    Passport::routes();
    // 注册自定义AccessToken模型
    Passport::useAccessTokenModel(AccessToken::class);
    
    // 这里可以设置令牌初始过期时间,比如2小时
    Passport::tokensExpireIn(now()->addHours(2));
}

2. 创建中间件处理滑动过期逻辑

接下来我们需要一个中间件,每次请求时更新令牌的最后使用时间,并且在剩余有效期不足时生成新令牌。

生成中间件

运行命令:

php artisan make:middleware UpdateAccessTokenLastUsed

打开生成的中间件文件app/Http/Middleware/UpdateAccessTokenLastUsed.php,编写逻辑:

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Support\Facades\Auth;
use Carbon\Carbon;

class UpdateAccessTokenLastUsed
{
    public function handle($request, Closure $next)
    {
        $user = Auth::user();
        if ($user && $token = $user->token()) {
            // 更新令牌最后使用时间
            $token->last_used_at = Carbon::now();
            $token->save();

            // 检查剩余有效期(这里设置为30分钟,可根据需求调整)
            $threshold = env('ACCESS_TOKEN_SLIDING_THRESHOLD', 30);
            $remainingMinutes = $token->expires_at->diffInMinutes(Carbon::now());

            if ($remainingMinutes < $threshold) {
                // 生成新令牌,复制原令牌的名称和权限范围
                $newToken = $user->createToken($token->name, $token->scopes)->accessToken;
                
                // 吊销旧令牌(如果不需要立即吊销,可以注释这行)
                $token->revoke();

                // 将新令牌通过响应头返回给前端
                $response = $next($request);
                $response->headers->set('X-New-Access-Token', $newToken);
                
                return $response;
            }
        }

        return $next($request);
    }
}

注册中间件

打开app/Http/Kernel.php,把这个中间件加到API中间件组里:

protected $middlewareGroups = [
    'api' => [
        'throttle:api',
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
        // 添加我们的中间件
        \App\Http\Middleware\UpdateAccessTokenLastUsed::class,
    ],
];

3. 前端处理新令牌

前端需要拦截响应,检查是否有X-New-Access-Token响应头,如果有,就替换本地存储的旧令牌,后续请求使用新令牌。

以Axios为例,添加响应拦截器:

// 假设你用localStorage存储令牌
axios.interceptors.response.use(
    (response) => {
        const newToken = response.headers['x-new-access-token'];
        if (newToken) {
            // 更新本地存储的令牌
            localStorage.setItem('access_token', newToken);
            // 更新Axios默认请求头
            axios.defaults.headers.common['Authorization'] = `Bearer ${newToken}`;
        }
        return response;
    },
    (error) => {
        // 处理令牌过期等错误情况,比如跳转到登录页
        if (error.response?.status === 401) {
            localStorage.removeItem('access_token');
            window.location.href = '/login';
        }
        return Promise.reject(error);
    }
);

一些额外说明

  • 如果你不想立即吊销旧令牌(允许旧令牌继续到原过期时间),可以注释掉中间件里的$token->revoke();,但这样会存在多个有效令牌,需要权衡安全性。
  • 滑动过期的阈值时间可以在.env里配置ACCESS_TOKEN_SLIDING_THRESHOLD=30,方便灵活调整。
  • 初始令牌过期时间可以在AuthServiceProvider的boot方法里通过Passport::tokensExpireIn()设置。

内容的提问来源于stack exchange,提问作者no0by5

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 05:11:23