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

Laravel 5.5(Auth):多设备“记住我”及设备管理实现问询

我之前刚好实现过类似的多设备登录管理功能,结合你的需求,分享下我的实战思路和安全建议,应该能帮到你!

核心思路:从单token到多token存储

默认Laravel把remember_token存在users表的单字段里,这就是为什么只能记住一个设备的原因。我们需要把每个设备的登录令牌单独存储,所以第一步是新建一个专门的表来保存多设备的记住令牌。

1. 创建多令牌存储表

先跑一个迁移,创建user_remember_tokens表,用来记录每个设备的登录信息:

Schema::create('user_remember_tokens', function (Blueprint $table) {
    $table->id();
    $table->foreignId('user_id')->constrained()->onDelete('cascade');
    $table->string('token', 60)->unique(); // 和Laravel默认token长度一致
    $table->text('device_info')->nullable(); // 存储设备标识,方便用户识别
    $table->timestamp('expires_at'); // 令牌过期时间
    $table->timestamps();
});

同时在User模型里添加关联:

public function rememberTokens()
{
    return $this->hasMany(UserRememberToken::class);
}

2. 重写自定义UserProvider的关键方法

你已经继承了EloquentUserProvider,接下来重写两个方法来适配多token逻辑:

① retrieveByToken:根据令牌查找用户

不再去users表查单字段,而是去新表找匹配且未过期的令牌:

public function retrieveByToken($identifier, $token)
{
    // 注意:Laravel默认会对remember_token做sha256哈希存储,所以要匹配哈希后的值
    $tokenRecord = DB::table('user_remember_tokens')
        ->where('user_id', $identifier)
        ->where('token', hash('sha256', $token))
        ->where('expires_at', '>', now())
        ->first();

    if (!$tokenRecord) {
        return null;
    }

    return $this->createModel()->newQuery()->find($identifier);
}

② updateRememberToken:生成新令牌并存储

默认逻辑是覆盖users表的token,现在改为插入新的令牌记录,同时记录设备信息:

public function updateRememberToken(UserContract $user, $token)
{
    // 如果token为空,说明用户取消了"记住我",可以按需清理该设备的令牌
    if (empty($token)) {
        // 这里可以根据会话关联的token删除,或者暂时留着过期自动清理
        return;
    }

    // 用Laravel默认的有效期配置(默认5年)
    $expiresAt = now()->addDays(config('auth.remember_expire', 1825));
    
    // 记录设备信息,比如User-Agent,方便用户识别是哪个设备
    $deviceInfo = request()->header('User-Agent');
    // 可以用jenssegers/agent包解析成更友好的名称,比如"Chrome on Windows 10"

    DB::table('user_remember_tokens')->insert([
        'user_id' => $user->getAuthIdentifier(),
        'token' => hash('sha256', $token),
        'device_info' => $deviceInfo,
        'expires_at' => $expiresAt,
        'created_at' => now(),
        'updated_at' => now(),
    ]);
}

3. 实现设备管理页面

现在用户的每个登录设备都有对应的令牌记录了,接下来做一个页面让用户查看和注销设备:

控制器方法示例

// 展示已登录设备列表
public function showDevices()
{
    $tokens = auth()->user()->rememberTokens()
        ->orderBy('created_at', 'desc')
        ->get();

    return view('user.devices', compact('tokens'));
}

// 注销指定设备
public function revokeDevice($tokenId)
{
    // 确保只能注销自己的设备
    $token = auth()->user()->rememberTokens()->findOrFail($tokenId);
    $token->delete();

    // 可选:同时销毁该设备对应的会话(如果用数据库会话驱动)
    DB::table('sessions')
        ->where('user_id', auth()->id())
        // 排除当前会话,避免把自己踢下线
        ->where('id', '!=', request()->session()->getId())
        ->delete();

    return back()->with('success', '该设备已成功注销');
}

前端视图示例(简化版)

<div class="device-list">
    @foreach($tokens as $token)
        <div class="device-item">
            <p>{{ $token->device_info }}</p>
            <p>登录时间:{{ $token->created_at->format('Y-m-d H:i') }}</p>
            <form action="{{ route('user.devices.revoke', $token->id) }}" method="POST">
                @csrf
                @method('DELETE')
                <button type="submit">注销该设备</button>
            </form>
        </div>
    @endforeach
</div>

4. 安全与优化建议

  • 令牌唯一性:始终用Str::random(60)生成令牌(Laravel默认逻辑),确保足够随机唯一。
  • 过期清理:定期用Laravel任务调度清理过期的令牌记录,避免表数据冗余:
    $schedule->call(function () {
        DB::table('user_remember_tokens')
            ->where('expires_at', '<', now())
            ->delete();
    })->daily();
    
  • 设备识别优化:不要直接显示原始的User-Agent,用jenssegers/agent包解析成用户友好的设备名称,提升体验。
  • 会话同步注销:如果用户注销某个设备,除了删除令牌,最好同时销毁对应的会话,避免对方还在会话有效期内继续访问。
  • CSRF保护:设备注销的表单一定要加@csrf令牌,防止跨站请求伪造。
  • 同一设备去重:如果用户在同一设备多次勾选“记住我”,可以考虑删除该设备之前的令牌,避免同一设备多条冗余记录。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 08:44:23