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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 12:53:11