欢迎来到山东社交动力网络科技有限公司
建站资讯

当前位置: 首页 > 建站资讯 > 建站教程 > PHP教程

Laravel Socialite单点登录:强制多设备登出实现教程

作者:b2c商城系统 来源:php入门学习班日期:2025-12-02

Laravel Socialite单点登录:强制多设备登出实现教程

本文旨在提供一个在laravel socialite应用中实现单用户会话、强制多设备登出的专业教程。通过引入设备标识符、优化登录流程以及创建会话验证中间件,确保用户在任何时刻只能在一个设备上保持登录状态,从而提升应用的安全性和用户会话管理能力。

在现代Web应用中,尤其是在使用第三方认证(如Google、Twitter通过Laravel Socialite)的场景下,为了增强账户安全性并确保会话管理的严谨性,常常需要限制用户只能在一个设备上保持登录状态。当用户从新设备登录时,其在旧设备上的会话应自动失效。本教程将详细阐述如何通过数据库字段、会话管理和自定义中间件来实现这一目标。

1. 数据库结构调整:引入设备标识符

首先,我们需要在 users 表中添加一个字段来存储当前用户登录设备的唯一标识符。这个标识符将在用户每次成功登录时更新。

创建迁移文件:

php artisan make:migration add_device_identifier_to_users_table --table=users
登录后复制

编辑迁移文件:

<?phpuse Illuminate\Database\Migrations\Migration;use Illuminate\Database\Schema\Blueprint;use Illuminate\Support\Facades\Schema;class AddDeviceIdentifierToUsersTable extends Migration{        public function up()    {        Schema::table('users', function (Blueprint $table) {            $table->string('current_device_identifier')->nullable()->after('remember_token');        });    }        public function down()    {        Schema::table('users', function (Blueprint $table) {            $table->dropColumn('current_device_identifier');        });    }}
登录后复制

运行迁移:

php artisan migrate
登录后复制

current_device_identifier 字段将用于存储用户当前活跃会话的设备标识。

2. 登录流程优化:生成并存储设备标识

在用户通过Laravel Socialite成功认证并登录后,我们需要生成一个唯一的设备标识符,将其存储到 users 表中,并同时将其写入到当前用户的会话(或JWT令牌)中。

生成设备标识符的策略:

设备标识符可以是一个简单的UUID,也可以是基于用户代理(User-Agent)和IP地址的哈希值,以提供更强的设备关联性。为了简化,这里我们使用一个随机字符串。

修改 Socialite 回调逻辑:

腾讯Effidit 腾讯Effidit

腾讯AI Lab开发的AI写作助手,提升写作者的写作效率和创作体验

腾讯Effidit 65 查看详情 腾讯Effidit

假设您的 Socialite 回调逻辑位于 AuthController 或类似的控制器中。

<?phpnamespace App\Http\Controllers\Auth;use App\Http\Controllers\Controller;use App\Models\User;use Illuminate\Support\Facades\Auth;use Illuminate\Support\Str;use Laravel\Socialite\Facades\Socialite;class SocialLoginController extends Controller{        public function redirectToProvider($provider)    {        return Socialite::driver($provider)->redirect();    }        public function handleProviderCallback($provider)    {        try {            $socialUser = Socialite::driver($provider)->user();        } catch (\Exception $e) {            return redirect('/login')->withErrors(['social_login' => '无法通过 ' . ucfirst($provider) . ' 登录,请重试。']);        }        $user = User::where('provider_id', $socialUser->getId())                    ->where('provider', $provider)                    ->first();        if (!$user) {            // 如果用户不存在,则创建新用户            $user = User::create([                'name' => $socialUser->getName(),                'email' => $socialUser->getEmail(),                'provider' => $provider,                'provider_id' => $socialUser->getId(),                // ... 其他字段            ]);        }        // 生成新的设备标识符        $deviceIdentifier = Str::random(60);        // 更新用户表中的设备标识符        $user->update([            'current_device_identifier' => $deviceIdentifier,        ]);        // 登录用户        Auth::login($user, true);        // 将设备标识符存储到当前会话中        session(['device_identifier' => $deviceIdentifier]);        return redirect()->intended('/home');    }        public function logout()    {        Auth::logout();        session()->forget('device_identifier'); // 清除会话中的设备标识符        request()->session()->invalidate();        request()->session()->regenerateToken();        return redirect('/');    }}
登录后复制

在上述代码中:

我们生成了一个随机字符串作为 deviceIdentifier。将此标识符更新到 users 表的 current_device_identifier 字段中。在用户登录成功后,将相同的 deviceIdentifier 存储到当前用户的会话 (session) 中。

3. 创建并应用会话验证中间件

现在,我们需要一个中间件来在每次请求时验证当前会话中的 device_identifier 是否与数据库中存储的 current_device_identifier 匹配。如果不匹配,则意味着用户已从其他设备登录,当前会话应被强制登出。

创建中间件:

php artisan make:middleware EnsureSingleSession
登录后复制

编辑 app/Http/Middleware/EnsureSingleSession.php:

<?phpnamespace App\Http\Middleware;use Closure;use Illuminate\Http\Request;use Illuminate\Support\Facades\Auth;class EnsureSingleSession{        public function handle(Request $request, Closure $next)    {        if (Auth::check()) {            $user = Auth::user();            $sessionDeviceIdentifier = session('device_identifier');            // 检查数据库中的设备标识符是否与会话中的匹配            // 如果数据库中的为空,或者与会话中的不匹配,则强制登出            if (empty($user->current_device_identifier) || $user->current_device_identifier !== $sessionDeviceIdentifier) {                Auth::logout(); // 强制登出当前用户                $request->session()->invalidate(); // 使会话失效                $request->session()->regenerateToken(); // 重新生成CSRF令牌                // 可以重定向到登录页并附带提示信息                return redirect('/login')->withErrors(['session_expired' => '您已从其他设备登录,当前会话已失效。请重新登录。']);            }        }        return $next($request);    }}
登录后复制

注册中间件:

打开 app/Http/Kernel.php 文件,将 EnsureSingleSession 中间件添加到 web 中间件组中,确保它在所有需要保护的Web路由上运行。

protected array $middlewareGroups = [    'web' => [        \App\Http\Middleware\Encryptcookies::class,        \Illuminate\cookie\Middleware\AddQueuedcookiesToResponse::class,        \Illuminate\Session\Middleware\StartSession::class,        // \Illuminate\Session\Middleware\AuthenticateSession::class, // 如果使用,确保在自定义中间件之前        \Illuminate\View\Middleware\ShareErrorsFromSession::class,        \App\Http\Middleware\VerifyCsrfToken::class,        \Illuminate\Routing\Middleware\SubstituteBindings::class,        \App\Http\Middleware\EnsureSingleSession::class, // 添加到这里    ],    'api' => [        // \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,        'throttle:api',        \Illuminate\Routing\Middleware\SubstituteBindings::class,    ],];
登录后复制

将中间件添加到 web 组后,所有使用 web 路由中间件的路由都将受到此单会话验证的保护。

注意事项

设备标识符的生成策略: 上述示例使用了简单的随机字符串。在生产环境中,您可以考虑使用更复杂的策略,例如结合 request()->header('User-Agent') 和 request()->ip() 的哈希值,以更好地识别“设备”。然而,请注意IP地址和User-Agent的变动性(如移动网络IP切换、浏览器更新User-Agent)。UUID或加密的随机字符串通常是更可靠的选择。用户体验: 突然的登出可能会让用户感到困惑。可以在重定向到登录页时提供清晰的提示信息,解释登出的原因。API认证(JWT/Sanctum): 如果您的应用还使用JWT或Laravel Sanctum进行API认证,此逻辑需要进行相应调整。JWT通常是无状态的,但您可以在JWT的Payload中包含 device_identifier,并在每次请求时通过中间件验证该Payload中的标识符是否与数据库中的匹配。会话失效时间: Laravel的会话默认有过期时间。此机制与会话过期机制是互补的,即使会话未过期,如果用户从新设备登录,旧设备也会被强制登出。登出逻辑: 确保在用户主动登出时,也清除会话中的 device_identifier,并根据需要清除数据库中的 current_device_identifier(设置为 null 或更新为新值)。

总结

通过在 users 表中存储设备标识符、在登录时更新此标识符并将其写入会话,以及通过自定义中间件在每次请求时进行验证,我们成功地为Laravel Socialite应用构建了一个强制单用户会话和多设备登出策略。这不仅提升了应用安全性,也为用户提供了更清晰的会话管理体验。这种方法是实现账户安全和会话控制的有效实践。

以上就是Laravel Socialite单点登录:强制多设备登出实现教程的详细内容,更多请关注php中文网其它相关文章!

标签: php培训机构
上一篇: 在Laravel中,从深层嵌套函数直接返回验证失败响应的教程
下一篇: 在JavaScript中监听Laravel Livewire的生命周期钩子

推荐建站资讯

更多>