Horizon使用自定义守卫时Gate授权回调的已认证对象位于第二参数是否正常?
当前运行环境
- Horizon Version: ^5.7
- Laravel Version: ^8.18
- PHP Version: ^8.0
- Redis Driver & Version: predis/phpredis ^1.1
问题描述
在配置Horizon授权逻辑时使用自定义守卫实现授权判断,发现Gate授权回调的已认证对象出现在回调的第二个参数而非第一个参数,该现象不符合预期。
复现代码
namespace App\Providers; class HorizonServiceProvider extends HorizonApplicationServiceProvider { // ... protected function authorization() { $this->gate(); Horizon::auth(function ($request) { return app()->environment('local') || Gate::check('viewHorizon', [$request->user('web:admin')]); // 此处使用了web:admin自定义守卫 }); } protected function gate() { Gate::define( 'viewHorizon', fn ($user = null, $admin = null) => // 实际运行时第二个参数$admin才是已认证的Admin对象,不符合预期 ($admin ? $admin::class : null) == Admin::class ); } }
核心疑问
该行为是否属于框架的正常设计?还是使用过程中存在错误?
问题解答
这个行为是Laravel Gate组件的固定设计逻辑,不属于框架bug,参数位置偏移的核心原因是Gate的强制参数注入规则:
- 所有通过
Gate::define定义的权限校验回调,框架会自动将当前默认认证守卫解析出的已认证用户,作为回调的第一个参数强制注入,不受你手动传入的参数影响。 - 调用
Gate::check时手动传入的附加参数,会从回调的第二个位置开始依次入参,不会顶替框架自动注入的首个用户参数。
对应你的运行场景:
- 如果应用默认认证守卫不是
web:admin,框架自动注入的第一个参数$user值为null - 你手动从
web:admin守卫解析出的Admin实例,会被框架识别为附加参数,放到回调的第二个参数$admin的位置 - 最终就出现了“实际认证对象跑到第二个参数”的现象
修正方案
可以通过两种方式让授权逻辑符合预期:
- 直接适配现有参数位置:当前代码逻辑其实可以正常运行,只是参数顺序和直觉不一致,不需要额外调整。
- 强制指定授权校验的用户对象,让框架跳过默认守卫的自动注入,调整Horizon授权判断的写法:
Horizon::auth(function ($request) { return app()->environment('local') || Gate::forUser($request->user('web:admin'))->check('viewHorizon'); });
这种写法下,框架会将forUser指定的Admin实例作为回调的第一个参数注入,完全匹配常规的Gate参数预期。
内容的提问来源于stack exchange,提问作者myckhel
相关产品推荐
相关产品推荐

