Laravel

Laravel 授权:Gate、Policy

对应视频教程:https://www.bilibili.com/video/av84570897

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');
}

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

// 涉及更多参数
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 view
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

发表评论