Laravel控制器依赖注入早于中间件执行的权限控制问题
问题核心
你的问题本质是Laravel路由模型绑定的参数解析时机,默认早于中间件中全局作用域的添加逻辑,导致无权限用户数据仍被注入到控制器方法中。下面是几个符合你「中间件/全局控制」需求的解决方案:
方案1:利用容器 beforeResolving 钩子提前注入全局作用域
修改中间件代码,通过容器钩子确保全局作用域在User模型被解析(路由模型绑定查询)前生效:
public function handle(Request $request, Closure $next) { $authUser = auth()->user(); if ($authUser?->organization_id) { // 在容器准备解析User模型时,先添加全局作用域 app()->beforeResolving(User::class, function () use ($authUser) { User::addGlobalScope(new OrganizationScope($authUser->organization)); }); } return $next($request); }
原理:beforeResolving是Laravel容器的前置钩子,当路由模型绑定需要解析User实例时,会优先执行闭包内的全局作用域添加逻辑,确保查询时自动带上权限过滤,不符合条件的请求直接返回404。
方案2:自定义User模型的路由绑定逻辑
在RouteServiceProvider中覆盖默认的隐式绑定规则,全局添加组织权限过滤,无需依赖中间件:
// app/Providers/RouteServiceProvider.php public function boot() { parent::boot(); // 替换User模型默认的路由绑定查询逻辑 User::resolveRouteBindingUsing(function ($value, $field = null) { $query = User::query(); // 仅当用户登录且有组织ID时添加过滤条件 if (auth()->check() && auth()->user()->organization_id) { $query->where('organization_id', auth()->user()->organization_id); } // 按路由绑定字段查询(默认是id),查不到直接抛出404 return $query->where($field ?? 'id', $value)->firstOrFail(); }); }
原理:这个方法直接接管了User模型的路由绑定逻辑,所有使用User隐式绑定的路由都会自动应用权限过滤,完全符合你全局控制的需求,且不需要额外维护中间件。
方案3:优化Model booted 方法的全局作用域
如果你之前在Model中添加全局作用域遇到问题,可通过request()->user()获取请求上下文的登录用户,解决启动时无法获取用户的问题:
// app/Models/User.php protected static function booted() { parent::booted(); static::addGlobalScope('organization', function (Builder $builder) { // 从请求上下文获取当前登录用户,避免启动阶段无用户的问题 $currentUser = request()->user(); if ($currentUser?->organization_id) { $builder->where('organization_id', $currentUser->organization_id); } }); }
原理:request()->user()可以在请求处理阶段动态获取登录用户,确保全局作用域仅在有效请求中生效,所有User查询(包括路由模型绑定)都会自动带上过滤条件。若需临时绕过作用域,可使用User::withoutGlobalScope('organization')。
内容的提问来源于stack exchange,提问作者Jeremy Belolo
相关产品推荐
相关产品推荐

