Laravel实现单IP单会话API限流及会话切换功能代码方案问询
Laravel单IP单会话实现方案
1. 会话存储准备
首先确保会话存储支持查询操作,推荐使用数据库驱动:
- 生成会话表:
php artisan session:table php artisan migrate
- 在
.env中配置会话驱动:
SESSION_DRIVER=database SESSION_LIFETIME=120 # 按实际需求设置过期时间(分钟)
2. 编写会话冲突检测中间件
生成中间件:
php artisan make:middleware CheckSingleSession
编辑app/Http/Middleware/CheckSingleSession.php:
<?php namespace App\Http\Middleware; use Closure; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Session; class CheckSingleSession { public function handle($request, Closure $next) { $currentIp = $request->ip(); $currentSessionId = Session::getId(); $sessionExpiry = now()->subMinutes(config('session.lifetime'))->timestamp; // 查询当前IP下的有效活跃会话(排除当前会话) $existingSession = DB::table('sessions') ->where('ip_address', $currentIp) ->where('last_activity', '>', $sessionExpiry) ->where('id', '!=', $currentSessionId) ->first(); if ($existingSession) { // 存在冲突会话,跳转到提示页面 return redirect()->route('session.conflict')->with('existing_session_id', $existingSession->id); } // 更新当前会话的IP和活跃时间(确保记录准确) DB::table('sessions') ->where('id', $currentSessionId) ->update([ 'ip_address' => $currentIp, 'last_activity' => now()->timestamp ]); return $next($request); } }
将中间件注册到app/Http/Kernel.php的web中间件组:
protected $middlewareGroups = [ 'web' => [ // ...其他中间件 \App\Http\Middleware\CheckSingleSession::class, ], ];
3. 创建冲突提示页面与前端逻辑
添加路由到routes/web.php:
Route::get('/session-conflict', function () { return view('session-conflict'); })->name('session.conflict'); Route::post('/force-new-session', [\App\Http\Controllers\SessionController::class, 'forceNewSession'])->name('session.force-new'); Route::get('/check-session-validity', [\App\Http\Controllers\SessionController::class, 'checkSessionValidity'])->name('session.check-validity');
创建视图resources/views/session-conflict.blade.php:
<!DOCTYPE html> <html> <head> <title>会话冲突</title> <script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js"></script> </head> <body> <h3>检测到您的IP已有活跃会话在其他窗口运行</h3> <p>您可以选择:</p> <button id="forceBtn">强制开启新会话(自动关闭旧窗口)</button> <script> $('#forceBtn').click(function() { $.post('{{ route("session.force-new") }}', function(response) { if (response.success) { window.location.href = response.redirect_url; } }); }); </script> </body> </html>
在全局布局文件(如resources/views/layouts/app.blade.php)中添加会话有效性监听,用于旧窗口自动关闭:
<script> setInterval(function() { $.get('{{ route("session.check-validity") }}', function(response) { if (!response.valid) { alert('您的会话已被强制下线,窗口即将关闭'); window.close(); } }); }, 5000); // 每5秒检查一次 </script>
4. 编写会话处理控制器
生成控制器:
php artisan make:controller SessionController
编辑app/Http/Controllers/SessionController.php:
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Session; class SessionController extends Controller { public function forceNewSession(Request $request) { $currentIp = $request->ip(); $currentSessionId = Session::getId(); $sessionExpiry = now()->subMinutes(config('session.lifetime'))->timestamp; // 删除当前IP下所有旧会话(排除当前会话,后续会重新生成) DB::table('sessions') ->where('ip_address', $currentIp) ->where('last_activity', '>', $sessionExpiry) ->where('id', '!=', $currentSessionId) ->delete(); // 重新生成当前会话ID Session::regenerate(true); return response()->json([ 'success' => true, 'redirect_url' => route('home') // 替换为你的首页路由 ]); } public function checkSessionValidity(Request $request) { $sessionId = Session::getId(); $sessionExists = DB::table('sessions') ->where('id', $sessionId) ->where('last_activity', '>', now()->subMinutes(config('session.lifetime'))->timestamp) ->exists(); return response()->json([ 'valid' => $sessionExists ]); } }
注意事项
- 对于反向代理后的应用,需确保Laravel能正确获取用户真实IP,可在
app/Http/Middleware/TrustProxies.php中配置信任的代理IP。 - JS关闭窗口的功能仅对通过JS打开的窗口生效,对于用户直接打开的标签页,只能提示用户手动关闭,或页面自动跳转至登录/提示页。
- 若使用Redis作为会话驱动,只需将数据库查询部分替换为Redis对应的操作逻辑即可。
内容的提问来源于stack exchange,提问作者Pradeep Kumar
相关产品推荐
相关产品推荐

