策略类专为模型绑定授权设计,能彻底解耦权限逻辑,避免控制器重复判断;Policy方法签名与@can调用名必须严格一致,注册需满足类名、路径、Gate::policy三要素,before方法仅在Gate中生效。

因为策略类能把权限判断从控制器里彻底抽出来,避免重复写 if ($user->id !== $post->user_id) 这类逻辑,让控制器只管请求和响应,而不是当保安。
策略类比 Gate 更适合模型绑定操作
当你需要判断“用户能否更新某篇具体文章”,就必须同时拿到 User 和 Post 实例——这种带模型上下文的授权,Policy 是专为此设计的;而 Gate 更适合无模型依赖的通用判断,比如 'view-dashboard' 或 'can-export-report'。
-
PostPolicy::update(User $user, Post $post)方法天然支持类型提示、IDE 跳转、单元测试隔离 - 用
Gate::define('update-post', ...)实现同样功能,参数传递易出错,且无法自动绑定模型实例 - Laravel 5.5 开始强化了
Policy的注册与调用契约,比如@can('edit', $post)会严格查找PostPolicy::edit(),不匹配就报BadMethodCallException,反而帮你提前暴露问题
策略注册漏掉一个环节,授权就静默失效
很多人写了 PostPolicy 却发现 $this->authorize('update', $post) 总是抛 403,不是代码错,而是注册断链了:
- 类名必须是
PostPolicy(不能是PostsPolicy或PostAuthPolicy) - 文件路径必须是
app/Policies/PostPolicy.php,大小写敏感 -
AuthServiceProvider::boot()里必须显式调用Gate::policy(Post::class, PostPolicy::class);如果Post在App\Models\Post命名空间下,这里也得写全名 - 漏掉任一条件,Laravel 就当 Policy 不存在,直接 fallback 到 Gate —— 如果你没定义同名 Gate,结果就是
false,且不报错
before 方法在 Policy 里不生效,别乱放权限兜底逻辑
Gate::before() 可以拦截所有授权检查(比如给超级管理员开全局绿灯),但 Policy 类里没有等价机制。有人试图在 PostPolicy 里写:
public function before($user, $ability)
{
if ($user->isSuperAdmin()) {
return true;
}
}
这是无效的——Laravel 不会调用 Policy 里的 before。正确做法只有一个:在 AuthServiceProvider::boot() 里统一用 Gate::before() 处理全局规则,Policy 只专注模型级业务逻辑。
最常被忽略的是策略方法签名和调用名的严格一致:写 @can('edit', $post) 就必须有 edit() 方法,哪怕 IDE 提示了 update() 也不行;改名不改调用,运行时直接炸,而不是静默降级。











