You need to enable JavaScript to run this app. Horizon使用自定义守卫时Gate授权回调的已认证对象位于第二参数是否正常?
优惠活动
大模型
产品
解决方案
定价
更多

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时手动传入的附加参数,会从回调的第二个位置开始依次入参,不会顶替框架自动注入的首个用户参数。

对应你的运行场景:

  1. 如果应用默认认证守卫不是web:admin,框架自动注入的第一个参数$user值为null
  2. 你手动从web:admin守卫解析出的Admin实例,会被框架识别为附加参数,放到回调的第二个参数$admin的位置
  3. 最终就出现了“实际认证对象跑到第二个参数”的现象
修正方案

可以通过两种方式让授权逻辑符合预期:

  1. 直接适配现有参数位置:当前代码逻辑其实可以正常运行,只是参数顺序和直觉不一致,不需要额外调整。
  2. 强制指定授权校验的用户对象,让框架跳过默认守卫的自动注入,调整Horizon授权判断的写法:
Horizon::auth(function ($request) {
    return app()->environment('local') ||
           Gate::forUser($request->user('web:admin'))->check('viewHorizon');
});

这种写法下,框架会将forUser指定的Admin实例作为回调的第一个参数注入,完全匹配常规的Gate参数预期。

内容的提问来源于stack exchange,提问作者myckhel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.27 16:24:04