如何定义Laravel Authorization中的Policy策略类?
- 内容介绍
- 文章标签
- 相关推荐
本文共计932个文字,预计阅读时间需要4分钟。
《策略类不是写了就能用的配置项,它必须被正确注册、配置模型,并在调用时传入对应实例,否则+》
策略类生成后必须手动注册到 AuthServiceProvider
运行 php artisan make:policy PostPolicy --model=Post 只是创建文件,不自动绑定。你得在 app/Providers/AuthServiceProvider.php 的 boot() 方法里显式注册:
- 单个策略:用
Gate::policy(Post::class, PostPolicy::class) - 多个策略:推荐在
registerPolicies()中统一注册(该方法默认已调用),并在$policies数组中添加映射,例如:Post::class => PostPolicy::class - 如果模型使用了自定义命名空间(如
App\Models\Post),$policies键必须完全一致,大小写敏感
$this->authorize() 调用时传参错误导致策略不触发
这是最常被忽略的坑:授权方法签名和调用参数必须严格匹配。例如策略中定义的是 public function update(User $user, Post $post),那么控制器里必须写成:
$this->authorize('update', $post);
而不是:
-
$this->authorize('update', Post::find($id))—— 如果$id不存在,返回null,Gate 拒绝授权并跳过策略 -
$this->authorize('update', $id)—— 传整数而非模型实例,Gate 找不到对应策略方法,直接返回false -
$this->authorize('update-post', $post)—— ability 名称错位,和策略方法名不一致(默认找updatePost或update)
资源控制器中误用 authorizeResource() 导致参数解析失败
$this->authorizeResource(Post::class) 依赖路由参数自动注入模型,但前提是:
- 路由必须含对应参数名,比如
Route::apiResource('posts', PostController::class)会生成/posts/{post},参数名是post(单数) - 控制器方法签名需匹配,如
public function update(Request $request, Post $post)—— 类型提示必须是模型类,且变量名与路由参数一致 - 若路由参数名是
id或pid,authorizeResource()就无法解析模型,策略不会被调用 - 调试时可在
Gate::inspect()或 Xdebug 中检查$arguments是否为空数组,空则说明模型未注入成功
策略里的 before() 方法不是万能钩子
before() 确实能提前拦截,但它只对认证用户生效,且一旦返回 true 或 false,后续策略方法将**完全跳过**:
- 返回
true→ 授权通过,不执行后续方法 - 返回
false→ 拒绝授权,不执行后续方法 - 返回
null→ 继续执行对应策略方法(这才是你想要的“兜底”行为) - 常见误用:在
before()里直接return $user->is_admin,结果管理员能进所有操作,连view都绕过了权限细节
真正容易出问题的地方在于模型实例是否被正确传递——它不像配置那样一写就生效,而是嵌在请求生命周期里,稍有断链,策略就形同虚设。调试时优先确认 $post 是不是 Eloquent 实例、有没有被软删除、路由参数名对不对,比反复检查策略逻辑本身更有效。
本文共计932个文字,预计阅读时间需要4分钟。
《策略类不是写了就能用的配置项,它必须被正确注册、配置模型,并在调用时传入对应实例,否则+》
策略类生成后必须手动注册到 AuthServiceProvider
运行 php artisan make:policy PostPolicy --model=Post 只是创建文件,不自动绑定。你得在 app/Providers/AuthServiceProvider.php 的 boot() 方法里显式注册:
- 单个策略:用
Gate::policy(Post::class, PostPolicy::class) - 多个策略:推荐在
registerPolicies()中统一注册(该方法默认已调用),并在$policies数组中添加映射,例如:Post::class => PostPolicy::class - 如果模型使用了自定义命名空间(如
App\Models\Post),$policies键必须完全一致,大小写敏感
$this->authorize() 调用时传参错误导致策略不触发
这是最常被忽略的坑:授权方法签名和调用参数必须严格匹配。例如策略中定义的是 public function update(User $user, Post $post),那么控制器里必须写成:
$this->authorize('update', $post);
而不是:
-
$this->authorize('update', Post::find($id))—— 如果$id不存在,返回null,Gate 拒绝授权并跳过策略 -
$this->authorize('update', $id)—— 传整数而非模型实例,Gate 找不到对应策略方法,直接返回false -
$this->authorize('update-post', $post)—— ability 名称错位,和策略方法名不一致(默认找updatePost或update)
资源控制器中误用 authorizeResource() 导致参数解析失败
$this->authorizeResource(Post::class) 依赖路由参数自动注入模型,但前提是:
- 路由必须含对应参数名,比如
Route::apiResource('posts', PostController::class)会生成/posts/{post},参数名是post(单数) - 控制器方法签名需匹配,如
public function update(Request $request, Post $post)—— 类型提示必须是模型类,且变量名与路由参数一致 - 若路由参数名是
id或pid,authorizeResource()就无法解析模型,策略不会被调用 - 调试时可在
Gate::inspect()或 Xdebug 中检查$arguments是否为空数组,空则说明模型未注入成功
策略里的 before() 方法不是万能钩子
before() 确实能提前拦截,但它只对认证用户生效,且一旦返回 true 或 false,后续策略方法将**完全跳过**:
- 返回
true→ 授权通过,不执行后续方法 - 返回
false→ 拒绝授权,不执行后续方法 - 返回
null→ 继续执行对应策略方法(这才是你想要的“兜底”行为) - 常见误用:在
before()里直接return $user->is_admin,结果管理员能进所有操作,连view都绕过了权限细节
真正容易出问题的地方在于模型实例是否被正确传递——它不像配置那样一写就生效,而是嵌在请求生命周期里,稍有断链,策略就形同虚设。调试时优先确认 $post 是不是 Eloquent 实例、有没有被软删除、路由参数名对不对,比反复检查策略逻辑本身更有效。

