14. Laravel 授权:Gate、Policy
Laravel 授权:Gate、Policy
Gates 和 Policies 相对于passport更细粒化
Gates 和 Policies
两者作用都是授权,本质上没有区别。
Gates 一般是个闭包,封装简单的逻辑。而 Policies 多以控制器的形式存在,可能会涉及到模型或者资源。
Gates 定义
# App\Providers\AuthServiceProvider
public function boot()
{
$this->registerPolicies();
// 最简单的
Gate::define('edit-settings', function ($user) {
return $user->isAdmin;
});
// 涉及特定资源
Gate::define('update-post', function ($user, $post) {
return $user->id == $post->user_id;
});
// 涉及更多参数
Gate::define('create-post', function ($user, $category, $extraFlag) {
return $category->group > 3 && $extraFlag === true;
});
// 指向某个控制器
Gate::define('update-post', 'App\Policies\PostPolicy@update');
}
#Routes/web.php
Route::get('/test', function(){
Auth::loginUsingId(1);
if (Gate::allows('edit-settings')) {
return '<h1>有权限</h1>';
} else {
return '<h1>没有权限</h1>';
}
});
Route::get('/for/user', function(){
Auth::loginUsingId(1);
if (Gate::forUser(\App\User::find(1)->allows('edit-settings'))) {
return '<h1>有权限</h1>';
}
return '<h1>没有权限</h1>';
});
# 非当前用户返回403
Route::get('/authorize', function(){
Auth::loginUsingId(1);
if (Gate::authorize('edit-settings')) {
return '<h1>你好</h1>';
}
});
Gates 使用
#当前用户
if (Gate::allows('edit-setting'))
if (Gate::allows('update-post', $post))
if (Gate::denies('update-post', $post))
# 其它用户
if (Gate::forUser($user)->allows('update-post', $post))
if (Gate::forUser($user)->denies('update-post', $post))
// allows, denies, check, any, none, authorize, can, cannot
# 有或有
if (Gate::any(['update-post', 'delete-post'], $post))
# 没有或没有
if (Gate::none(['update-post', 'delete-post'], $post))
Gate::authorize('update-post', $post); // AuthorizationException
// 涉及更多参数 check同名方法就是allows
if (Gate::check('create-post', [$category, $extraFlag]))
// 前置操作
Gate::before(function ($user, $ability) {
if ($user->isSuperAdmin()) {
return true;
}
});
// 后置操作
Gate::after(function ($user, $ability, $result, $arguments) {
if ($user->isSuperAdmin()) {
return true;
}
});
Gate::define('edit-settings', function ($user) {
return $user->isAdmin
? Response::allow()
: Response::deny('You must be a super administrator.');
});
$response = Gate::inspect('edit-settings', $post);
if ($response->allowed()) {
// The action is authorized...
} else {
echo $response->message();
}
Policies 生成和注册
php artisan make:policy PostPolicy
php artisan make:policy PostPolicy --model=Post
// viewAny, view, create, update, delete, restore, 和 forceDelete
# AuthServiceProvider 中注册
use App\Policies\PostPolicy;
use App\Post;
class AuthServiceProvider extends ServiceProvider
{
protected $policies = [
Post::class => PostPolicy::class,
];
public function boot()
{
$this->registerPolicies();
}
}
# Policy 自动发现,在 app/Policies 下,默认 User 对应 UserPolicy。如果想修改发现 policy 逻辑,在 AuthServiceProvider 中的 boot:
Gate::guessPolicyNamesUsing(function ($modelClass) {
// 返回 policy 名称
});
Policy 定义
// 最简单的
public function create(User $user)
{
}
// 涉及特定的资源
public function update(User $user, Post $post)
{
return $user->id === $post->user_id;
}
// 提供 null 默认值
public function update(?User $user, Post $post)
{
return optional($user)->id === $post->user_id;
}
# 如果返回null,则授权将落入策略方法。
# 如果未能找到和被检测的能力的名称对应的 policy 方法名, before 方法不被调用
public function before($user, $ability): bool
{
if ($user->isSuperAdmin()) {
return true;
}
}
Policies 使用
# 如果没有注册 policy,会尝试查找和操作名称相匹配的 Gate 。
// 最简单的
if ($user->can('create', Post::class)) {
}
// 涉及特定资源的
if ($user->can('update', $post)) {
//
}
// 失败直接 403
public function update(Request $request, Post $post)
{
$this->authorize('update', $post);
// 授权失败返回 403
}
# 如果是只有一个参数,如 create 操作
public function create(Request $request)
{
$this->authorize('create', Post::class);
}
通过中间件使用
Route::put('/post/{post}', function (Post $post) {
})->middleware('can:update,post'); // 第二个参数是 post 路由参数
// 认证失败返回 403
# 如果是只有一个参数,如 create 操作
Route::post('/post', function () {
})->middleware('can:create,App\Post');
授权资源控制器
public function __construct()
{
$this->authorizeResource(Post::class, 'post');
}
控制器方法 | Policy 方法 |
---|---|
index | viewAny |
show | |
create | create |
store | create |
edit | update |
update | update |
destroy | delete |
给 policy 的方法传递更多的参数
# policy
public function update(User $user, Post $post, int $category)
{
return $user->id === $post->user_id &&
$category->group > 3;
}
# controller
public function update(Request $request, Post $post)
{
$this->authorize('update', [$post, $request->input('category')]);
// ...
}
public function update(User $user, Post $post)
{
return $user->id === $post->user_id
? Response::allow()
: Response::deny('You do not own this post.');
}
$response = Gate::inspect('update', $post);
if ($response->allowed()) {
// The action is authorized...
} else {
echo $response->message();
}
在 blade 中使用
@can('update', $post)
<!-- The Current User Can Update The Post -->
@elsecan('create', App\Post::class)
<!-- The Current User Can Create New Post -->
@endcan
@cannot('update', $post)
<!-- The Current User Can't Update The Post -->
@elsecannot('create', App\Post::class)
<!-- The Current User Can't Create New Post -->
@endcannot
# 类似于
@if (Auth::user()->can('update', $post))
<!-- The Current User Can Update The Post -->
@endif
@unless (Auth::user()->can('update', $post))
<!-- The Current User Can't Update The Post -->
@endunless
@canany(['update', 'view', 'delete'], $post)
// The current user can update, view, or delete the post
@elsecanany(['create'], \App\Post::class)
// The current user can create a post
@endcanany
@can('create', App\Post::class)
<!-- The Current User Can Create Posts -->
@endcan
# 如果是只有一个参数,如 create 操作
@cannot('create', App\Post::class)
<!-- The Current User Can't Create Posts -->
@endcannot