能否基于Session Key移除Laravel中的指定Middleware?
基于Session Key移除指定中间件的可行性及正确实现
你的思路是可行的,但当前的实现方式无法生效,原因是时机不对:路由组中配置的中间件会在控制器构造方法执行前就被加载到请求的中间件栈中,你在控制器内部的中间件闭包里调用withoutMiddleware时,那些2FA中间件已经处于待执行状态,根本无法被移除。
下面给出两种可行的实现方案:
方案一:在目标中间件内直接添加跳过逻辑
这是最直接简单的方式,不需要额外新增中间件,直接修改2fa.enabled和2fa.confirm中间件的handle方法,在原有逻辑前加入Session判断:
修改2fa.enabled中间件:
public function handle(Request $request, Closure $next) { // 存在 imperson_by 会话时直接跳过2FA启用校验 if ($request->session()->has('imperson_by')) { return $next($request); } // 原有2FA启用校验逻辑 if (! $request->user()->two_factor_enabled) { return redirect()->route('two-factor.enable'); } return $next($request); }
修改2fa.confirm中间件:
public function handle(Request $request, Closure $next) { // 存在 imperson_by 会话时直接跳过2FA确认校验 if ($request->session()->has('imperson_by')) { return $next($request); } // 原有2FA确认校验逻辑 if (! $request->session()->has('auth_2fa')) { return redirect()->route('two-factor.confirm'); } return $next($request); }
这种方式的好处是逻辑集中在目标中间件内,代码侵入性低,容易维护。
方案二:自定义前置中间件移除目标中间件
如果你不想修改原有的2FA中间件,可以新增一个前置中间件,在2FA中间件执行前从请求的中间件栈中移除它们:
1. 创建自定义中间件
<?php namespace App\Http\Middleware; use Closure; use Illuminate\Http\Request; use Illuminate\Routing\Route; class Skip2faWhenImpersonating { public function handle(Request $request, Closure $next) { if ($request->session()->has('imperson_by')) { /** @var Route $route */ $route = $request->route(); // 过滤掉不需要执行的2FA中间件 $filteredMiddleware = array_filter( $route->gatherMiddleware(), fn($middleware) => !in_array($middleware, ['2fa.enabled', '2fa.confirm']) ); // 更新路由的中间件列表 $route->setMiddleware($filteredMiddleware); } return $next($request); } }
2. 在Kernel.php中注册中间件
在$routeMiddleware数组中添加:
'skip.2fa' => \App\Http\Middleware\Skip2faWhenImpersonating::class,
3. 调整路由组的中间件顺序
确保自定义中间件放在2FA中间件之前:
Route::group([ 'namespace' => 'Employee', 'prefix' => 'employee', 'middleware' => [ 'auth', 'skip.2fa', // 必须在2FA中间件前面 '2fa.enabled', '2fa.confirm', ], ], function () { Route::get('/', [EmployeeController::class, 'index'])->name('employer.index'); Route::get('/data', [EmployeeController::class, 'data'])->name('employee.data'); });
这种方式适合需要保留原中间件逻辑,仅在特定场景下跳过的需求。
内容的提问来源于stack exchange,提问作者Benish Farooq
相关产品推荐
相关产品推荐

