Laravel 中基于用户配置的动态双因素认证中间件实现

1次阅读

Laravel 中基于用户配置的动态双因素认证中间件实现

本文介绍如何在 laravel 8 中结合 spatie/laravel-permission,为特定路由动态启用双因素认证(2FA),仅当当前登录用户 is_two_step_authorization = 1 且未完成验证时才触发跳转,实现细粒度、按需生效的安全控制。

本文介绍如何在 laravel 8 中结合 spatie/laravel-permission,为特定路由动态启用双因素认证(2fa),仅当当前登录用户 `is_two_step_authorization = 1` 且未完成验证时才触发跳转,实现细粒度、按需生效的安全控制。

在构建多角色企业级应用(如个体用户与公司用户共存)时,安全策略不应“一刀切”。例如,您可能希望仅对启用了双因素认证(2FA)的公司用户,在访问敏感操作(如查看寄出/接收包裹历史、下载 PDF 凭证等)时强制执行二次验证;而对未开启该功能的用户则完全绕过,保持体验流畅。这要求中间件具备运行时用户上下文感知能力,而非静态绑定到路由。

✅ 正确做法:创建动态感知型中间件

首先,生成自定义中间件:

php artisan make:middleware Check2FA

然后编辑 app/http/Middleware/Check2FA.php,关键逻辑在于:仅当当前用户已启用 2FA 且尚未通过验证时才拦截请求

<?php  namespace AppHttpMiddleware;  use Closure; use IlluminateHttpRequest; use IlluminateSupportFacadessession;  class Check2FA {     public function handle(Request $request, Closure $next)     {         // 确保用户已登录(避免未登录时调用 $request->user() 报错)         $user = $request->user();         if (!$user) {             return $next($request);         }          // 核心判断:仅当用户启用双因素认证,且 session 中无有效 2FA 标识时重定向         if ($user->is_two_step_authorization && !Session::has('user_2fa')) {             return redirect()->route('2fa.index'); // 假设您的 2FA 首页路由名为 '2fa.index'         }          return $next($request);     } }

⚠️ 注意事项:

  • 必须前置身份验证中间件:确保 Check2FA 在 auth 或 auth:sanctum 之后执行(在 app/Http/Kernel.php 的 $middlewareGroups[‘web’] 中,将其置于 auth 后);
  • Session 有效性保障:Session::has(‘user_2fa’) 应在用户成功完成 2FA 后由您的验证控制器(如 TwoStepVerificationController@verify)手动设置,例如:Session::put(‘user_2fa’, true);
  • 避免无限重定向:确保 2fa.index 路由本身不应用 Check2FA 中间件,否则将导致循环跳转。

? 集成到现有路由组

修改您原有的路由定义,将 Check2FA 中间件添加至需要受控的子路由(而非整个外层 group),以精准作用于敏感操作:

Route::group(['prefix' => '', 'middleware' => ['role:individual|company']], function () {     // 所有 company 权限路由均需先过角色检查,再按用户配置决定是否触发 2FA     Route::get('/cms-historia-przesylek-nadanych', [AccountSendPackageController::class, 'index'])         ->name('cms-history-send-packages')         ->middleware(['company', '2fa']); // ← 新增 '2fa' 别名      Route::get('/cms-przesyleka-nadana/{id}', [AccountSendPackageController::class, 'show'])         ->name('cms-view-send-package')         ->middleware(['company', '2fa']);      // ... 其他敏感路由同理,均追加 '2fa'      // 2FA 设置页本身不参与验证,保持开放     Route::get('/cms-dwu-stopniowa-weryfikacja', [AccountTwoStepVerificationController::class, 'index'])         ->name('cms-two-step-verification'); });

同时,在 app/Http/Kernel.php 中注册中间件别名:

protected $routeMiddleware = [     // ...     '2fa' => AppHttpMiddlewareCheck2FA::class, ];

? 补充建议与最佳实践

  • 数据库字段确认:确保 users.is_two_step_authorization 字段已正确迁移并设为 tinyInteger(1)->default(0),并在模型中添加访问器提升可读性:
    // 在 User.php 中添加 public function getIsTwoStepAuthorizationAttribute($value) {     return (bool) $value; }
  • 退出时清理 Session:用户登出时应清除 2FA 状态,防止会话残留:
    // 在登出逻辑中(如 LoginController@logout) Session::forget('user_2fa');
  • 用户体验优化:可在 2FA 页面显示提示:“您已启用双因素认证,访问敏感功能前需验证身份”,增强透明度。

通过以上实现,您不再依赖全局或静态中间件,而是让安全策略真正“因人而异”——既满足合规性要求,又兼顾不同用户的实际安全偏好,是 Laravel 权限与认证体系进阶落地的典型范式。

text=ZqhQzanResources