laravel中的用户授权

一、授权操作有两种主要方式

1、gates

2、策略。

二、区别

1、Gates 提供了一种简单的基于闭包的授权方法,Gates 大部分应用在模型和资源没有关系的地方,比如查看管理员的面板

2、策略和控制器类似,围绕特定模型或资源对其逻辑进行分组来实现授权认证,策略应该在特定的模型或者资源中使用

三、Gates

1、编写gates

  • Gates 是用来决定用户是否授权执行给予动作的一个闭包函数,并且典型的做法就是在 App\Providers\AuthServiceProvider 中使用 Gate 来定义
  • Gates 总是接收一个用户实例作为第一个参数,并且可以接收可选参数,比如相关的 Eloquent 模型
/**
 * 注册任意用户认证、用户授权服务
 *
 * @return void
 */
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;
    });
}

2、授权动作

  • 使用 Gate 来授权动作的时候, 你应该使用 allows 或者 denies 方法
  • 注意,不需要将当前已认证用户传递给这些方法。 Laravel 会自动处理好已经认证通过的用户,然后传递给 Gate 闭包函数
  • 在需要进行授权验证的类和方法里进行判断
if (Gate::allows('edit-settings')) {
    // 指定当前用户可以编辑设置
}

if (Gate::allows('update-post', $post)) {
    // 指定当前用户可以进行更新...
}

if (Gate::denies('update-post', $post)) {
    // 指定当前用户不能更新...
}
  • 如果你想判断一个特定的用户是否已经被授权访问某个动作, 你可以使用在 Gate 在 facade 的 forUser 方法
if (Gate::forUser($user)->allows('update-post', $post)) {
    // 用户可以更新...
}

if (Gate::forUser($user)->denies('update-post', $post)) {
    // 用户不能更新...
}
  • 也可以使用类回调数组来定义 gates,例如控制器:

use App\Policies\PostPolicy;

/**
 * 注册任意用户认证、用户授权服务
 *
 * @return void
 */
public function boot()
{
    $this->registerPolicies();

    Gate::define('update-post', [PostPolicy::class, 'update']);
}

3、授权或抛出异常

如果要尝试对某个操作进行授权,并在未授权用户进行该操作的情况下抛出 illuminate\auth\access\authorizationexception,则可以使用 gate::authorize 方法。authorizationexception 的实例将自动转换为 403 http 响应

Gate::authorize('update-post', $post);

4、提供上下文

  • 能够用于授权的 Gate 方法
allows,denies,check,any,none,authorize,can,cannot
@can,@cannot,@canany

四、策略

1、生成策略

  • 策略是在特定模型或者资源中组织授权逻辑的类
  • 可以使用 make:policy artisan command 生成策略
php artisan make:policy PostPolicy
  • make:policy 命令将生成一个空策略类。如果你想生成一个包含基本的 “CRUD” 策略方法的类,你可以在执行命令时指定一个 --model
php artisan make:policy PostPolicy --model=Post

2、注册策略

  •  Laravel 应用程序中包含的 AuthServiceProvider 包含一个 policy 属性,它将您的 Eloquent 模型映射到它们相应的策略
<?php

namespace App\Providers;

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

class AuthServiceProvider extends ServiceProvider
{
    /**
     * 应用程序的策略映射
     *
     * @var array
     */
    protected $policies = [
        Post::class => PostPolicy::class,
    ];

    /**
     * 注册任何应用程序 authentication / authorization 服务
     *
     * @return void
     */
    public function boot()
    {
        $this->registerPolicies();

        //
    }
}

3、策略自动发现

  • 不需要手动注册模型策略,只要模型和策略遵循标准的 Laravel 命名约定,Laravel 就可以自动发现策略。
  • 具体来说,策略必须位于包含模型的目录下的 Policies 目录中。例如,模型可以放在 app 目录中,而策略可以放在 app/Policies 目录中。
  • 此外,策略名称必须与模型名称匹配,并具有 Policy 后缀。因此,一个 User 模型将对应于一个 UserPolicy 类

4、编写策略

  • 一旦注册了策略,您可以为它授权的每个操作添加方法
  • 我们在 PostPolicy 上定义一个 update 方法,它确定给定的 User 是否可以更新给定的 Post 实例。
  • update 方法将接收一个 User 和一个 Post 实例作为参数,并应返回 true 或 false,指示用户是否被授权更新给定的 Post
  • 您可以自由地为策略方法指定任何名称
<?php

namespace App\Policies;

use App\Models\Post;
use App\Models\User;

class PostPolicy
{
    /**
     * 确定用户是否可以更新给定的帖子
     *
     * @param  \App\Models\User  $user
     * @param  \App\Models\Post  $post
     * @return bool
     */
    public function update(User $user, Post $post)
    {
        return $user->id === $post->user_id;
    }
}

5、Guest 用户

  • 默认情况下,如果传入的 HTTP 请求不是由经过身份验证的用户发起的,所有的门和策略都会自动返回 false
  • 但是,您可以通过声明一个「可选」类型提示或为用户参数定义提供一个 null 默认值,从而允许这些授权检查通过您的 Gate 和策略
<?php

namespace App\Policies;

use App\Models\Post;
use App\Models\User;

class PostPolicy
{
    /**
     * 确定用户是否可以更新给定的帖子
     *
     * @param  \App\Models\User  $user
     * @param  \App\Models\Post  $post
     * @return bool
     */
    public function update(?User $user, Post $post)
    {
        return optional($user)->id === $post->user_id;
    }
}

6、策略过滤器

  • 对于某些用户,您可能希望授权给定策略中的所有操作。为此,在策略上定义一个 before 方法。
  • before 方法将在策略上的任何其他方法之前执行,从而使您有机会在实际调用预期的策略方法之前授权操作。
  • 此功能最常用于授权应用程序管理员执行任何操作

五、使用策略授权动作

1、通过【用户】模型

  • Laravel 应用程序中包含的 User 模型包括两个用于授权操作的有用方法:can 和 cant
  • can 方法接收您希望授权的操作和相关模型
if ($user->can('update', $post)) {
    //
}
  • 如果为给定模型(如$post) 注册了策略can 方法将自动调用适当的策略并返回布尔值。
  • 如果没有为模型注册策略,can 方法将尝试调用匹配给定操作名称的基于闭包的 Gate。

2、通过中间件

  • Laravel 包含一个中间件,可以在传入的请求到达路由或控制器之前对操作进行授权
  • 默认情况下,Illuminate\Auth\Middleware\Authorize 中间件被分配给 App\Http\Kernel 类中的 can 键
  • 在本例中,我们传递了 can 中间件两个参数。第一个是希望授权的操作的名称,第二个是希望传递给策略方法的路由参数。
use App\Models\Post;

Route::put('/post/{post}', function (Post $post) {
    // 当前用户可以更新帖子...
})->middleware('can:update,post');

3、通过控制器辅助函数

  • 除了为 User 模型提供的有用方法之外,Laravel 还为任何扩展 App\Http\Controllers\Controller 基类的控制器提供了一个有用的 authorize 方法
  • 与 can 方法一样,此方法接受您要授权的操作的名称和相关模型
  • 如果操作未被授权,authorize 方法将抛出一个 Illuminate\Auth\Access\AuthorizationException ,默认的 Laravel 异常处理程序将转换为具有 403 状态代码的 HTTP 响应
<?php

namespace App\Http\Controllers;

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

class PostController extends Controller
{
    /**
     * 更新指定博客帖子
     *
     * @param  Request  $request
     * @param  Post  $post
     * @return Response
     * @throws \Illuminate\Auth\Access\AuthorizationException
     */
    public function update(Request $request, Post $post)
    {
        $this->authorize('update', $post);

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

4、不需要指定模型的动作

像 create 这样的一些动作可能不需要模型实例。 在这些情况下,您应该将类名传递给 authorize 方法。 类名将用于确定授权操作时使用的策略

/**
 * 创建一个新的博客文章
 *
 * @param  Request  $request
 * @return Response
 * @throws \Illuminate\Auth\Access\AuthorizationException
 */
public function create(Request $request)
{
    $this->authorize('create', Post::class);

    // 当前用户可以新建博客文章
}

5、提供附加上下文

  • 使用策略授权操作时,您可以将数组作为第二个参数传递给各种授权函数和辅助方法
  • 数组中的第一个元素将用于确定应调用哪个策略,而其余数组元素作为参数传递给策略方法,并可在进行授权决策时用于其他上下文
  •  例如,考虑以下 PostPolicy 方法定义,其中包含一个额外的 $category 参数:
/**
 * 确定用户是否可以更新给定的帖子
 *
 * @param  \App\Models\User  $user
 * @param  \App\Models\  $post
 * @param  int  $category
 * @return bool
 */
public function update(User $user, Post $post, int $category)
{
    return $user->id === $post->user_id &&
           $category > 3;
}
  • 在尝试确定验证过的用户是否可以更新给定文章时,我们可以像这样调用此策略方法
/**
 * 更新给定的博客文章
 *
 * @param  Request  $request
 * @param  Post  $post
 * @return Response
 * @throws \Illuminate\Auth\Access\AuthorizationException
 */
public function update(Request $request, Post $post)
{
    $this->authorize('update', [$post, $request->input('category')]);

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

 

posted @ 2022-03-09 18:12  艾薇-Ivy  阅读(372)  评论(0编辑  收藏  举报