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

Laravel Sanctum数据库令牌过多及设备令牌管理方案咨询

解决Laravel Sanctum令牌过多的最佳实践

核心问题拆解

  • 未授权请求可无限调用登录接口生成令牌,导致数据库冗余
  • 多设备登录场景下,无法直接删除所有现有令牌
  • 令牌以哈希形式存储,无法直接复用用户最新令牌

具体解决方案

1. 限制单用户令牌数量

给每个用户设置令牌数量上限,生成新令牌时自动清理超出上限的最旧令牌。比如设置每个用户最多保留10个有效令牌:

// 在登录逻辑中添加令牌数量控制
$maxAllowedTokens = 10;
$user->tokens()
    ->where('name', 'login-token') // 确保匹配你的令牌命名规则
    ->orderBy('created_at', 'asc')
    ->skip($maxAllowedTokens - 1)
    ->delete();

// 生成新令牌
$plainTextToken = $user->createToken('login-token')->plainTextToken;

这种方式既保留了多设备的登录权限,又避免了令牌无限增长。

2. 为令牌设置过期时间

利用Sanctum的令牌过期特性,生成令牌时指定有效期,配合定时任务自动清理过期令牌:

// 生成带7天有效期的令牌
$plainTextToken = $user->createToken(
    'login-token',
    [], // 权限范围,按需调整
    now()->addDays(7)
)->plainTextToken;

然后在app/Console/Kernel.php中添加定时任务,每日清理过期令牌:

protected function schedule(Schedule $schedule)
{
    $schedule->call(function () {
        DB::table('personal_access_tokens')
            ->where('expires_at', '<', now())
            ->delete();
    })->daily();
}

3. 对登录接口做防刷限制

阻止未授权请求无限调用登录接口,可通过两种方式实现:

  • 添加验证码验证:要求前端传入图形/短信验证码,验证通过后才生成令牌
  • 请求频率限制:使用Laravel内置的Throttle中间件限制请求次数,比如1分钟内最多5次请求:
// 在登录路由中添加中间件
Route::post('/api/login', [AuthController::class, 'login'])
    ->middleware('throttle:5,1');

4. 基于设备标识复用令牌(替代方案)

由于令牌哈希存储无法直接复用,可通过设备标识关联令牌,同一设备再次登录时更新令牌而非生成新令牌:

// 获取前端传入的设备唯一标识
$deviceId = $request->input('device_id');

// 查找该用户对应设备的有效令牌
$existingToken = $user->tokens()
    ->where('name', 'login-token')
    ->where('metadata->device_id', $deviceId) // 假设用metadata存储设备信息
    ->first();

if ($existingToken) {
    // 删除旧令牌,生成新令牌(无法直接获取明文,只能替换)
    $existingToken->delete();
    $plainTextToken = $user->createToken(
        'login-token',
        [],
        now()->addDays(7),
        ['device_id' => $deviceId]
    )->plainTextToken;
} else {
    // 生成新令牌并绑定设备标识
    $plainTextToken = $user->createToken(
        'login-token',
        [],
        now()->addDays(7),
        ['device_id' => $deviceId]
    )->plainTextToken;
}

额外优化建议

  • 给personal_access_tokens表的user_id、expires_at、last_used_at字段添加索引,提升查询和清理效率
  • 定期清理长期未使用的令牌,比如删除30天内未被使用的令牌:
$schedule->call(function () {
    DB::table('personal_access_tokens')
        ->where(function ($query) {
            $query->where('expires_at', '<', now())
                  ->orWhere('last_used_at', '<', now()->subDays(30));
        })
        ->delete();
})->weekly();

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 04:15:43