Laravel 8 强制未更新信息的已认证用户跳转至个人资料页
实现方案
你拿不到用户对象的原因很简单:路由文件在应用启动阶段就会加载解析,这个阶段请求还没经过auth:sanctum认证中间件处理,用户凭证还没完成校验,Auth::user()自然返回null。这类基于登录用户状态的请求拦截逻辑,通过自定义中间件实现即可,不需要改动现有业务控制器。
第一步:创建自定义中间件
在项目根目录执行artisan命令生成中间件文件:
php artisan make:middleware EnsureAccountIsCompliant
第二步:编写中间件拦截逻辑
打开生成的app/Http/Middleware/EnsureAccountIsCompliant.php,写入如下逻辑:
<?php namespace App\Http\Middleware; use Closure; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; class EnsureAccountIsCompliant { public function handle(Request $request, Closure $next) { $user = Auth::user(); // 中间件放在auth之后执行,这步为兜底判断 if (!$user) { return redirect('/login'); } // 白名单:未完成校验的用户允许访问的路由,和你实际路由名对应即可 $allowedRoutes = [ 'profile', 'profile.update', 'logout', // 必须加,否则用户无法退出登录 'password.confirm', // Jetstream自带密码确认路由,避免改密码流程被拦截 'two-factor.enable', 'two-factor.disable', 'two-factor.confirm', ]; // 校验规则和你之前在Fortify里写的保持一致即可 $needResetPassword = now()->diffInDays($user->password_changed_at) >= 60; $twoFactorNotEnabled = is_null($user->two_factor_secret); // 不符合要求且访问非白名单路由时,直接重定向到资料页 if (($needResetPassword || $twoFactorNotEnabled) && !$request->routeIs($allowedRoutes)) { return redirect()->route('profile')->with('warning', '请先完成密码更新或双因素认证开启后再访问其他页面'); } return $next($request); } }
注意:判断密码过期的字段、白名单路由名要和你项目实际配置一致,必须覆盖所有资料更新相关的POST/PUT提交接口,否则用户提交更新请求时会被拦截,造成死循环。
第三步:注册中间件
打开app/Http/Kernel.php,将自定义中间件加入$routeMiddleware数组:
protected $routeMiddleware = [ // 框架自带的其他中间件... 'account.compliant' => \App\Http\Middleware\EnsureAccountIsCompliant::class, ];
第四步:挂载到业务路由组
修改原有路由配置,把自定义中间件放在认证类中间件之后,保证执行到拦截逻辑时用户已经完成认证:
Route::middleware(['auth:sanctum', 'verified', 'account.compliant'])->group(function() { // 所有业务路由放在这里即可,白名单内的路由会自动放行,不需要单独拆分 });
踩坑提醒
- 不需要把个人资料相关路由移到中间件组外,否则会出现未登录用户可直接访问资料页的权限问题,白名单机制已经可以实现正常放行。
- 如果项目用Livewire/Inertia开发,要确认前端资料更新、2FA操作对应的后端路由全部加入白名单,避免提交请求被302重定向导致操作失败。
- 上线前用测试账号验证:将账号密码更新时间改为60天前、清空2FA密钥,确认访问非白名单页面会被重定向,完成密码更新、2FA开启后可正常访问所有页面。
内容的提问来源于stack exchange,提问作者Kamishiraishi
相关产品推荐
相关产品推荐

