
本文旨在提供一个在laravel socialite应用中实现单用户会话、强制多设备登出的专业教程。通过引入设备标识符、优化登录流程以及创建会话验证中间件,确保用户在任何时刻只能在一个设备上保持登录状态,从而提升应用的安全性和用户会话管理能力。
在现代Web应用中,尤其是在使用第三方认证(如Google、Twitter通过Laravel Socialite)的场景下,为了增强账户安全性并确保会话管理的严谨性,常常需要限制用户只能在一个设备上保持登录状态。当用户从新设备登录时,其在旧设备上的会话应自动失效。本教程将详细阐述如何通过数据库字段、会话管理和自定义中间件来实现这一目标。
首先,我们需要在 users 表中添加一个字段来存储当前用户登录设备的唯一标识符。这个标识符将在用户每次成功登录时更新。
创建迁移文件:
php artisan make:migration add_device_identifier_to_users_table --table=users
编辑迁移文件:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddDeviceIdentifierToUsersTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('users', function (Blueprint $table) {
$table->string('current_device_identifier')->nullable()->after('remember_token');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('current_device_identifier');
});
}
}运行迁移:
php artisan migrate
current_device_identifier 字段将用于存储用户当前活跃会话的设备标识。
在用户通过Laravel Socialite成功认证并登录后,我们需要生成一个唯一的设备标识符,将其存储到 users 表中,并同时将其写入到当前用户的会话(或JWT令牌)中。
生成设备标识符的策略:
设备标识符可以是一个简单的UUID,也可以是基于用户代理(User-Agent)和IP地址的哈希值,以提供更强的设备关联性。为了简化,这里我们使用一个随机字符串。
修改 Socialite 回调逻辑:
假设您的 Socialite 回调逻辑位于 AuthController 或类似的控制器中。
<?php
namespace 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
{
/**
* Redirect the user to the social provider authentication page.
*
* @param string $provider
* @return \Symfony\Component\HttpFoundation\RedirectResponse
*/
public function redirectToProvider($provider)
{
return Socialite::driver($provider)->redirect();
}
/**
* Obtain the user information from the social provider.
*
* @param string $provider
* @return \Illuminate\Http\RedirectResponse
*/
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');
}
/**
* Log the user out of the application.
*
* @return \Illuminate\Http\RedirectResponse
*/
public function logout()
{
Auth::logout();
session()->forget('device_identifier'); // 清除会话中的设备标识符
request()->session()->invalidate();
request()->session()->regenerateToken();
return redirect('/');
}
}在上述代码中:
现在,我们需要一个中间件来在每次请求时验证当前会话中的 device_identifier 是否与数据库中存储的 current_device_identifier 匹配。如果不匹配,则意味着用户已从其他设备登录,当前会话应被强制登出。
创建中间件:
php artisan make:middleware EnsureSingleSession
编辑 app/Http/Middleware/EnsureSingleSession.php:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class EnsureSingleSession
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
*/
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 路由中间件的路由都将受到此单会话验证的保护。
通过在 users 表中存储设备标识符、在登录时更新此标识符并将其写入会话,以及通过自定义中间件在每次请求时进行验证,我们成功地为Laravel Socialite应用构建了一个强制单用户会话和多设备登出策略。这不仅提升了应用安全性,也为用户提供了更清晰的会话管理体验。这种方法是实现账户安全和会话控制的有效实践。
以上就是Laravel Socialite单点登录:强制多设备登出实现教程的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号