Laravel 文档阅读:授权

翻译、衍生自:https://learnku.com/docs/laravel/5.5/authorization

简介

除了开箱提供的认证服务,在 Laravel 中对用户授权某个资源的操作权限也非常容易实现。Laravel 授权的方式主要分两种:Gates 和策略(Policies)。

Gates 和策略的关系像路由和控制器的。Gates 提供基于路由的授权方式;而策略将针对某个资源的操作逻辑组织起来,放在一个地方管理。我们会先介绍 Gates,然后再介绍策略。

我们不需要在「项目里的授权,是选择使用 Gates,还是选择使用策略呢?」这个问题上纠结。许多项目里,都会混用这两种授权方式,而且运行良好。基本上,使用 Gates 的地方都是资源不相关的,比如查看管理员面板页;相反,策略是资源相关的,当你是具体授权某个资源的操作权限时,使用策略是没错啦。

Gates

我们通常在 App\Provider\AuthServiceProvider 类中使用 Gate 门面来定义 Gates。已经说过,Gates 是基于闭包判断用户是否有操作权限的,回调闭包的第一个参数是用户实例,额外可选的第二个参数是相关的 Eloquent Model 实例:

/**
 * Register any authentication / authorization services.
 *
 * @return void
 */
public function boot()
{
    $this->registerPolicies();

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

Gates 也可以以 Class@method 的形式定义授权逻辑,像控制器一样:

/**
 * Register any authentication / authorization services.
 *
 * @return void
 */
public function boot()
{
    $this->registerPolicies();

    Gate::define('update-post', 'PostPolicy@update');
}

在上面的例子中,我们称在 Gates 定义中的 'update-post' 叫「能力」(Ability)。

资源 Gates

使用 Gate::resource 方法一次定义多个 Gates:

Gate::resource('posts', 'PostPolicy');

这一句相当于手动定义了下面的 Gates:

Gate::define('posts.view', 'PostPolicy@view');
Gate::define('posts.create', 'PostPolicy@create');
Gate::define('posts.update', 'PostPolicy@update');
Gate::define('posts.delete', 'PostPolicy@delete');

默认,会定义 viewcreateupdatedelete 能力。如果要覆盖或添加,使用 resource 方法的第三个数组参数。数组的 Key 是能力名,Value 是方法名。下面的例子里,我们定义了两个 Gates 能力:posts.imageposts.photo

Gate::resource('photos', 'PostPolicy', [
    'image' => 'updateImage',
    'photo' => 'updatePhoto',
]);

授权操作

使用 Gates 授权操作,是用到 allowsdenies 方法。需要注意的是,你不需要传递当前的认证用户实例给这两个方法,因为 Laravel 会自动为我们的 Gates 传递用户实例的:

if (Gate::allows('update-post', $post) {
    // The current user can update the post...
})

if (Gate::denies('update-post', $post)) {
    // The current user can't update the post...
}

如果需要判断一个指定用户账号是否有进行某个操作的权限,那么在使用 forUser 方法吧!

if (Gate::forUser($user)->allows('update-post', $post) {
    // The current user can update the post...
})

if (Gate::forUser($user)->denies('update-post', $post)) {
    // The current user can't update the post...
}

创建策略

生成策略

策略是针对特定模型或资源、方便得组织它们的授权逻辑在一个地方的类。例如,在一个博客系统中,我们用 Post 模型对应的策略类 PostPolicy(注意,这里的跟上面的不一样)来授权认证用户创建和更新博客相关的权限。

你可以使用 Artisan 命令 make:policy 生成策略。生成的策略放在 app/Policies 目录下,如果这个目录不存在,它会在第一次执行此命令时创建:

php artisan make:policy PostPolicy

make:policy 命令会生成一个空的策略类。如果需要生成带有基本「CRUD」策略方法的策略类。那么在执行 make:policy 命令是跟上 --model 选择吧:

php artisan make:policy PostPolicy --model=Post

提示:-D 所有的策略都是通过 Laravel 的服务容器解析的,因此你可以在策略类的构造函数里添加任何你需要依赖注入的实例。

注册策略

策略类生成完毕后需要注册它。AuthServiceProviderpolicies 数组属性就是干这个的,在这里为你的 Eloquent Model 添加对应的策略类映射。注册后,Laravel 在对给定 Model 资源授权的时候,就知道来找哪个策略类了:

<?php

namespace App\Providers;

use App\Post;
use App\Policies\PostPolicy;
use Illuminate\Support\Facades\Gate;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;

class AuthServiceProvider extends ServiceProvider
{
    /**
     * The policy mappings for the application.
     *
     * @var array
     */
    protected $policies = [
        Post::class => PostPolicy::class,
    ];

    /**
     * Register any application authentication / authorization services.
     *
     * @return void
     */
    public function boot()
    {
        $this->registerPolicies();

        //
    }
}

写策略类

策略方法

策略类注册完毕后,再来看看怎样为每个操作添加对应的策略方法。在接下来的例子里,我们为 PostPolicy 定义一个 update 方法,用来判断是否指定的用户拥有更新博客的权限。

update 方法接受两个参数:User 实例和 Post 实例,我们最终返回 true 或者 false 来说明用户是否有权限进行操作。在此,我们使用的是用户的 id 字段来匹配博客的 user_id 字段的。

<?php

namespace App\Policies;

use App\User;
use App\Post;

class PostPolicy
{
    /**
     * Determine if the given post can be updated by the user.
     *
     * @param  \App\User  $user
     * @param  \App\Post  $post
     * @return bool
     */
    public function update(User $user, Post $post)
    {
        return $user->id === $post->user_id;
    }
}

你可以继续为你的其他操作添加对应策略方法,比如 view 或者 delete 啦,还有一点,策略方法的名字你是可以自由取的。

提示:-D 在控制台执行使用 --model 选项生成策略类的时候,会自动生成针对增删改查操作的 createdeleteupdateview 策略方法。

无 Model 策略方法

「无模型策略方法」是指策略方法里不需要传入模型实例的情况。最常见的场景就是创建操作,我们假设它对应 create 策略方法。例如,你可能会判断一个用户是否有创建博客的权限,这时就不需要有博客实例对象了,只要给个认证用户就行了:

/**
 * Determine if the given user can create posts.
 *
 * @param  \App\User  $user
 * @return bool
 */
public function create(User $user)
{
    //
}

策略过滤

对于一些用户,我们会碰到无论如何要给他整个策略类中所有方法的操作权限(想想管理员)。为了实现这个功能,在策略类中定义一个 before 方法就 OK。before 方法会在所有策略方法调用之前调用:

public function before($user, $ability)
{
    if ($user->isSuperAdmin()) {
        return true;
    }
}

如果要拒绝用户的所有操作权限,直接返回 false。如果返回 null 的话,就跟没设置 before 方法一样一样的。

重点提示( ⊙ o ⊙ ) 如果 Gates 能力对应的策略方法在策略类中未定义,before 方法是不会被调用的。

使用策略授权操作

通过 User Model

Laravel 中的 User Model 中包含两个有用的授权方法:cancantcan 方法接收两个参数,第一个参数是操作名,第二个就是相关 Model 实例了。下面的例子里,我们判断用户是否有更新指定 Post Model 实例的权限:

if ($user->can('update', $post)) {
    //
}

can 方法会自动调用正确的策略类的 update 方法并返回一个布尔值。如果没有知道的话,就会去 Gates 中找有没有一个能力叫 update 的。

无 Model 操作

前面已经说过,对于创建这一类授权呢,定义时是不需要给 Model 实例的。那么在使用这样的一类授权方法时,怎么写呢?这时候,给 can 方法传递对应 Model 类名就 OK。

use App\Post;

if ($user->can('create'), Post::class) {
    // Executes the "create" method on the relevant policy...
}

通过中间件

Laravel 提供了一个授权操作的中间件(Illuminate\Auth\Middleware\Authorize),它会在你的请求到大路由或者控制器的时候,判断用户是否有相关权限。这个中间件已经在 App\Http\Kernel 类中设置了,并且赋予了 can 这个 Key。我们来看下,针对更新博客的操作,咱们怎么去使用它:

use App\Post;

Route::put('/posts/{post}', function (Post $post) {
    // The current user may update the post...
})->middleware('can:update,post');

can 冒号后面用逗号隔开的就是两个参数。第一个参数是操作名,第二个参数是我们传递给策略方法的路由参数,在这里我们使用了隐式模型绑定,所以一个 Post 模型实例会被传递给策略方法。如果用户授权未通过,一个带有 403 状态码的 HTTP 响应就会通过该中间件生成。

无 Model 操作

一样的,对于不需要给 Model 实例的授权操作(比如创建)判断方法,我们需要给中间件传递的第二个参数是类名:

Route::post('/post', function () {
    // The current user may create posts...
})->middleware('can:create,App\Post');

通过控制器辅助函数

除了 User Model 提供的辅助函数,Laravel 为所有继承了 App\Http\Controllers\Controller 基类的控制器都提供了 auhtorize 辅助方法。类似 can 方法,authorize 方法接受操作名和相关 Model 实例对象作为参数。如果操作经过验证未授权,authorize 方法会抛出一个 Illuminate\Auth\Access\AuthorizationException 异常,Laravel 默认的异常处理器会把它转换为一个带有 403 状态码的 HTTP 响应:

<?php

namespace App\Http\Controllers;

use App\Post;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

class PostController extends Controller
{
    /**
     * Update the given blog post.
     *
     * @param  Request  $request
     * @param  Post  $post
     * @return Response
     */
    public function update(Request $request, Post $post)
    {
        $this->authorize('update', $post);

        // The current user can update the blog post...
    }
}

无 Model 操作

再一次,对于不需要给 Model 实例的授权操作判断方法,我们需要给 authorize 方法传递的第二个参数是类名:

/**
 * Create a new blog post.
 *
 * @param  Request  $request
 * @return Response
 */
public function create(Request $request)
{
    $this->authorize('create', Post::class);

    // The current user can create blog posts...
}

通过 Blade 模板

在 Blade 模板中,提供了两个用来判断用户是否具有操作权限的指令:@cancannot。它们在判断页面是否该显示一些内容上比较有用,使用方式如下:

@can('update', $post)
    <!-- The Current User Can Update The Post -->
@elsecan('create', $post)
    <!-- The Current User Can Create New Post -->
@endcan

@cannot('update', $post)
    <!-- The Current User Can't Update The Post -->
@elsecannot('create', $post)
    <!-- The Current User Can't Create New Post -->
@endcannot

上面的指令其实是 @if@unless 的快捷形式的写法。等同于

@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

无 Model 操作

你可以用 @can@cannot 指令处理无 Model 操作的权限判断场景,需要传递的是相关类名:

@can('create', App\Post::class)
    <!-- The Current User Can Create Posts -->
@endcan

@cannot('create', App\Post::class)
    <!-- The Current User Can't Create Posts -->
@endcannot
本作品采用《CC 协议》,转载必须注明作者和本文链接
本帖由 Summer 于 6年前 加精
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

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