本文目录导读:

在 Laravel 中,策略(Policy) 和 门面(Gate) 是授权系统的两大核心组件,它们紧密配合,共同完成权限控制,可以理解为:
- 策略 是具体实现授权逻辑的“操作手册”(针对某个模型)。
- 门面 是调用授权检查的“总入口”(
Gatefacade 或Authfacade)。
下面详细介绍它们的配合方式。
核心思想
- 策略(Policy):定义某个模型(如
Post、User)的各种操作权限(如view,create,update,delete)。 - 门面(Gate):提供统一的接口(如
can、allows、denies)来调用这些策略,Gate 会自动根据模型类找到对应的策略。
配合流程:
调用 Gate → Gate 查找 Policy → Policy 的方法执行检查 → 返回 bool
创建和注册策略
1 生成策略
php artisan make:policy PostPolicy --model=Post
2 定义策略方法
// app/Policies/PostPolicy.php
class PostPolicy
{
use HandlesAuthorization;
public function viewAny(User $user)
{
return true; // 所有用户可查看列表
}
public function update(User $user, Post $post)
{
return $user->id === $post->user_id; // 只有作者可更新
}
public function delete(User $user, Post $post)
{
// 更复杂的逻辑
return $user->id === $post->user_id || $user->isAdmin();
}
}
3 注册策略
在 AuthServiceProvider 的 $policies 属性中注册:
// app/Providers/AuthServiceProvider.php
class AuthServiceProvider extends ServiceProvider
{
protected $policies = [
Post::class => PostPolicy::class,
];
public function boot()
{
$this->registerPolicies();
}
}
使用门面调用策略
有四种主要的调用方式:
1 使用 Gate Facade
use Illuminate\Support\Facades\Gate;
if (Gate::allows('update', $post)) {
// 允许更新
}
if (Gate::denies('update', $post)) {
// 拒绝更新
}
// 或指定用户(如管理员检查其他用户)
if (Gate::forUser($otherUser)->allows('update', $post)) { ... }
2 在 Controller 中辅助方法
public function update(Request $request, Post $post)
{
$this->authorize('update', $post); // 自动抛出 403 异常
// 更新逻辑...
}
3 在 Blade 视图中
@can('update', $post)
<a href="{{ route('posts.edit', $post) }}">编辑</a>
@endcan
@cannot('delete', $post)
<span>无权删除</span>
@endcannot
4 在模型实例上使用 can() 方法
if ($user->can('update', $post)) {
// ...
}
门面如何找到策略?
Gate 使用约定来自动匹配:
- 你传入模型的类名(
Post::class)或实例($post)。 - Gate 根据
AuthServiceProvider中注册的$policies数组,找到对应的PostPolicy。 - 从策略中调用与方法同名的方法(如
update→update())。 - 如果没有注册策略,Gate 会尝试查找
Policy命名规范(App\Policies\ModelNamePolicy)。
注意:如果没有显式注册,Gate 也能通过约定自动发现策略(Laravel 8+)。
更高级的配合方式
1 在 Gate 中直接定义逻辑(小型应用)
可以在 AuthServiceProvider::boot() 中直接使用 Gate::define:
public function boot()
{
$this->registerPolicies();
// 额外定义的授权逻辑(优先级低于策略)
Gate::define('admin-action', function ($user) {
return $user->isAdmin();
});
}
在 Blade 中使用:
@can('admin-action')
<!-- 管理员专用按钮 -->
@endcan
2 使用策略的 before 方法(超级管理员绕过)
class PostPolicy
{
public function before(User $user, $ability) // ability 表示操作名称
{
if ($user->isSuperAdmin()) {
return true; // 超级管理员通过所有检查
}
}
// ... 其他方法
}
注意:before 返回 true 直接通过,返回 false 会继续执行具体方法。
完整示例
场景:博客文章权限控制
策略(PostPolicy):
class PostPolicy
{
public function view(User $user, Post $post) { ... }
public function create(User $user) { ... }
public function update(User $user, Post $post) { ... }
public function delete(User $user, Post $post) { ... }
}
Controller 使用:
class PostController extends Controller
{
public function store(Request $request)
{
$this->authorize('create', Post::class); // 不需要模型实例
// 创建逻辑
}
public function update(Request $request, Post $post)
{
$this->authorize('update', $post);
// 更新逻辑
}
}
Blade 视图:
@can('update', $post)
<form action="/posts/{{ $post->id }}" method="POST">
@csrf @method('PUT')
<button>更新</button>
</form>
@endcan
API 路由(使用 Gate 门面手动检查):
Route::put('/posts/{post}', function (Post $post) {
if (Gate::allows('update', $post)) {
// 更新逻辑
}
abort(403);
});
总结配合关系
| 组件 | 职责 | 对应代码 |
|---|---|---|
| Policy | 定义针对某个模型的授权规则 | update(User $user, Post $post) |
| Gate | 提供统一授权检查接口 | Gate::allows('update', $post) |
| Controller | 使用 $this->authorize() 简化调用 |
自动调用 Gate |
| Blade | 使用 @can 指令展示/隐藏界面元素 |
@can('update', $post) |
| User Model | 使用 $user->can() 在代码中检查 |
$user->can('update', $post) |
最佳实践:
- 模型专属权限用 Policy(代码清晰、可维护性强)。
- 通用权限(如
admin-action)用 Gate::define 直接定义。 - Controller 里优先使用
$this->authorize()而非手动Gate::allows。 - 视图层用 Blade 指令,避免在模板中写复杂 PHP 逻辑。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播