Laravel 9如何限制终端用户仅在单设备同时登录
Laravel 9 限制单设备登录的解决方案
针对你遇到的同一用户多设备登录问题,以及旧会话未主动登出就不会过期的痛点,这里提供两种可靠的实现方案:
方案一:基于数据库会话的旧会话强制失效
这个方案利用Laravel的数据库会话存储,在用户新登录时直接清除该用户的所有旧会话,从根源上阻止多设备同时在线。
步骤1:配置数据库会话驱动
- 修改
.env文件,将会话驱动改为数据库:
SESSION_DRIVER=database
- 生成会话表迁移文件并执行迁移:
php artisan session:table php artisan migrate
步骤2:监听登录事件清除旧会话
- 创建登录事件监听器:
php artisan make:listener RevokeOldSessions --event=Illuminate\Auth\Events\Login
- 在生成的
app/Listeners/RevokeOldSessions.php中编写处理逻辑:
<?php namespace App\Listeners; use Illuminate\Auth\Events\Login; use Illuminate\Support\Facades\DB; class RevokeOldSessions { public function handle(Login $event) { // 删除当前用户除当前会话外的所有旧会话 DB::table('sessions') ->where('user_id', $event->user->id) ->where('id', '!=', session()->getId()) ->delete(); } }
- 在
app/Providers/EventServiceProvider.php中注册监听器:
protected $listen = [ \Illuminate\Auth\Events\Login::class => [ \App\Listeners\RevokeOldSessions::class, ], ];
补充:自动清理过期会话
Laravel会自动清理过期会话,但可以通过任务调度确保定时执行:
在app/Console/Kernel.php的schedule方法中添加:
$schedule->command('session:gc')->hourly();
方案二:基于活跃会话ID的请求校验
这个方案通过在用户表存储当前活跃会话ID,搭配中间件实时校验请求会话是否为用户的唯一活跃会话,旧设备的请求会被强制登出。
步骤1:给用户表添加活跃会话ID字段
- 生成迁移文件:
php artisan make:migration add_active_session_id_to_users_table --table=users
- 编辑迁移文件:
<?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; return new class extends Migration { public function up() { Schema::table('users', function (Blueprint $table) { $table->string('active_session_id')->nullable(); }); } public function down() { Schema::table('users', function (Blueprint $table) { $table->dropColumn('active_session_id'); }); } };
- 执行迁移:
php artisan migrate
步骤2:登录时更新活跃会话ID
在app/Http/Controllers/Auth/LoginController.php中添加authenticated方法:
protected function authenticated(Request $request, $user) { // 将当前会话ID设为用户的活跃会话ID $user->update(['active_session_id' => session()->getId()]); }
步骤3:创建校验中间件
- 生成中间件:
php artisan make:middleware CheckActiveSession
- 编辑中间件逻辑:
<?php namespace App\Http\Middleware; use Closure; use Illuminate\Http\Request; class CheckActiveSession { public function handle(Request $request, Closure $next) { // 已登录用户校验会话是否匹配活跃会话ID if (auth()->check() && auth()->user()->active_session_id !== session()->getId()) { auth()->logout(); return redirect()->route('login')->with('error', '你的账号已在其他设备登录,请重新登录'); } return $next($request); } }
- 在
app/Http/Kernel.php的web中间件组中注册该中间件:
protected $middlewareGroups = [ 'web' => [ // ... 其他中间件 \App\Http\Middleware\CheckActiveSession::class, ], ];
步骤4:登出时清空活跃会话ID
在app/Http/Controllers/Auth/LogoutController.php中添加loggedOut方法:
protected function loggedOut(Request $request) { if (auth()->check()) { auth()->user()->update(['active_session_id' => null]); } }
方案对比
- 方案一:直接清除旧会话,逻辑简单,适合大多数web应用;需依赖数据库会话驱动,会自动处理会话过期。
- 方案二:通过会话ID校验,更灵活,可自定义提示信息;中间件会在每次请求时校验,性能影响极小。
内容的提问来源于stack exchange,提问作者Umar Adil
相关产品推荐
相关产品推荐

