介绍 Laravel 授权方式 Gate 和 Policy

两种形式

Laravel 在 AuthServiceProviderboot 方法里定义授权,有两种形式。

Gate::define('update articles', function ($user, $article) {
    return $user->id == $article->user_id;
});

Gate::define('update articles', 'ArticlePolicy@update');
<?php

namespace App\Policies;

use App\User;
use App\Models\Article;

class ArticlePolicy
{
    public function update(User $user, Article $article)
    {
        return $user->id == $article->author_id;
    }
}

这两种形式的区别在于,授权逻辑是写在闭包里,还是 PHP 类方法里。闭包或方法最终都返回一个布尔值,true 表示有权限,false 表示没权限。

使用授权

有四种使用授权的方式:

  1. Gate 门面:Gate::allows('update articles', $article)Gate::denies('update articles', $article)
  2. Controller:$this->authorize('update articles', $article)
  3. Blade 模板:@can('update articles', $article)@cannot('update articles', $article) 指令。
  4. User Model 实例:$user->can('update articles', $article)$user->cannot('update articles', $article)

Policy

有些时候,直接用基于闭包的形式定义授权很不方便,特别是对某个资源(比如文章)基本的增、删、改、查操作。

Gate::define('view articles', function ($user, $article) {
    return true;
});

Gate::define('create articles', function ($user) {
    return true;
});

Gate::define('delete articles', function ($user, $article) {
    return $user->id == $article->user_id;
});

Gate::define('update articles', function ($user, $article) {
    return $user->id == $article->user_id;
});

如果再有一个视频资源的话,又得定义这四个方法了,这些方法堆在一起,很长。那么如果改成基于 PHP 类方法的形式,就好看一些了。

Gate::define('view articles',  'ArticlePolicy@view');
Gate::define('create articles', 'ArticlePolicy@create');
Gate::define('delete articles', 'ArticlePolicy@delete');
Gate::define('update articles', 'ArticlePolicy@update');
<?php

namespace App\Policies;

use App\User;
use App\Models\Article;

class ArticlePolicy
{
    public function view(User $user, Article $article)
    {
        return true;
    }

    public function create(User $user)
    {
        return true;
    }

    public function delete(User $user, Article $article)
    {
        return $user->id == $article->author_id;
    }

    public function update(User $user, Article $article)
    {
        return $user->id == $article->author_id;
    }
}

因为分类写了,所以定义授权的地方,看起来就比较清爽了。

为了能更加清爽地定义和使用授权,Laravel 引入了 Policy。在 AuthServiceProviderpolicies 数组属性里添加授权映射关系即可!

/**
 * The policy mappings for the application.
 *
 * @var array
 */
protected $policies = [
    Article::class => ArticlePolicy::class,
];

所有与 Article Model 有关的授权逻辑都写在 ArticlePolicy 里,你可以用 make:policy Artisan 命令生成他。Policy 只是在普通 PHP 类基础上添加了一个 HandlesAuthorization trait。

<?php

namespace App\Policies;

use App\User;
use App\Models\Article;
use Illuminate\Auth\Access\HandlesAuthorization;

class ArticlePolicy
{
    use HandlesAuthorization;

    public function view(User $user, Article $article)
    {
        return true;
    }

    public function create(User $user)
    {
        return true;
    }

    public function delete(User $user, Article $article)
    {
        return $user->id == $article->author_id;
    }

    public function update(User $user, Article $article)
    {
        return $user->id == $article->author_id;
    }
}

而且使用起来也非常方便。

$user->can('view', $article);
$user->can('create', Article::class);
$user->can('update', $article);
$user->can('delete', $article);

$article 是 Article Model 实例对象, Laravel 发现是对 Article Model 资源做授权判断,根据在 $policies 中定义的授权映射关系,自动找到 ArticlePolicy 下的 viewcreateupdatedelete 方法。

当然,授权方法可自定义。比如我在 ArticlePolicy 下自定义了一个方法 xxx,那么就可以直接用了。

$user->can('xxx', $article);

但有一个方法是特殊的——before,在 Policy 中会在所有方法执行前调用,经常用到的地方就是处理管理员授权逻辑。

public function before($user, $ability)
{
    if ($user->isSuperAdmin()) {
        return true;
    }
}
本作品采用《CC 协议》,转载必须注明作者和本文链接
本帖由 Summer 于 6年前 加精
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
讨论数量: 1
Complicated

laravel这套权限机制缺失很优雅,但是对初学者有点懵逼!对老手,又觉得自己写权限管理比较方便(自己根据需求后期扩展方便),感觉有点儿鸡肋了

5年前 评论
RZZG_HQB 3年前
Complicated (作者) 3年前
Complicated

laravel这套权限机制缺失很优雅,但是对初学者有点懵逼!对老手,又觉得自己写权限管理比较方便(自己根据需求后期扩展方便),感觉有点儿鸡肋了

5年前 评论
RZZG_HQB 3年前
Complicated (作者) 3年前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!