Policy的核心价值是将授权逻辑从控制器剥离以提升可读性、可测试性和复用性;需通过Artisan命令生成策略类、在AuthServiceProvider中注册绑定,并在控制器用authorize()、视图用@can()调用。

当控制器里塞满 if ($user->id === $post->user_id || $user->hasRole('admin')) 这类判断时,代码就失去了可读性和可测试性——每次改权限规则都要翻控制器,一不小心就漏掉某处校验。Policy 的核心价值不是“多写一个类”,而是把“谁能在什么条件下做什么”这件事,从 HTTP 流程里彻底剥离出来,让授权逻辑能被单独审视、复用和单元测试。
创建 Policy 类并绑定模型
运行 Artisan 命令生成策略类,例如针对 Post 模型:php artisan make:policy PostPolicy --model=Post。
生成的文件位于 app/Policies/PostPolicy.php,它默认包含 view、create、update、delete 四个空方法,但你完全可以按需增删,比如加一个 publish 方法。
打开 app/Providers/AuthServiceProvider.php,在 $policies 数组中注册绑定关系:Post::class => PostPolicy::class。这一步【必须完成,否则 authorize() 不会触发对应 Policy】。
在 Policy 中编写可测试的授权逻辑
不要把复杂条件硬塞进控制器或中间件——Policy 就是专为这种逻辑而生的。比如限制“仅作者或管理员可编辑”,直接写在 edit() 方法里:
public function edit(User $user, Post $post) { return $user->id === $post->user_id || $user->hasRole('admin'); }
如果业务要求更细粒度:文章发布后 24 小时内允许作者修改,超时则仅管理员可操作,就把时间判断也放进 Policy:
public function update(User $user, Post $post) { if ($post->isPublished() && now()->diffInHours($post->published_at) > 24) { return $user->hasRole('admin'); } return $user->id === $post->user_id; }
注意:Policy 方法第一个参数是 User $user,但它可能是 null(未登录用户)。若该操作对游客也开放(如查看草稿),需显式处理:public function view(?User $user, Post $post) { return $post->isPublished() || ($user && $user->id === $post->user_id); }。
在控制器与视图中调用 Policy
第一步:在控制器方法顶部调用 $this->authorize('edit', $post)。Laravel 会自动匹配到 PostPolicy@edit 并传入当前用户和资源实例。
第二步:遇到权限拒绝时,Laravel 默认返回 403 响应,无需手动 throw Exception。
第三步:在 Blade 模板中做条件渲染,用 @can('update', $post) 包裹按钮或链接,比在模板里写冗长的 if 判断清晰得多。
第四步:若需提前判断权限结果(比如设置页面标题或禁用按钮),可用辅助函数:if (auth()->user()?->can('delete', $post)) { ... }。注意这里用了空合并操作符 ?->,避免未登录时调用 can 报错。


















