Laravel 策略类的使用

[文档:https://laravel-china.org/docs/laravel/5.5/authorization/1310](https://laravel-china.org/docs/laravel/5.5/authorization/1310)

## 创建策略:

策略是在特定模型或者资源中组织授权逻辑的类。

例如:给用户的操作创建一个策略里

```

php artisan make:policy UserPolicy

```

该命令会创建一个文件:app/Policies/UserPolicy.php

make:policy 会生成空的策略类。

==如果希望生成的类包含基本的「CRUD」策略方法, 可以在使用命令时指定 --model 选项:==

```

php artisan make:policy UserPolicy --model=User

```

如下:

```

<?php

namespace App\Policies;

use App\User;

use Illuminate\Auth\Access\HandlesAuthorization;

class UserPolicy

{

    use HandlesAuthorization;

    /**

    * Determine whether the user can view the model.

    *

    * @param  \App\User  $user

    * @param  \App\User  $model

    * @return mixed

    */

    public function view(User $user, User $model)

    {

        //

    }

    /**

    * Determine whether the user can create models.

    *

    * @param  \App\User  $user

    * @return mixed

    */

    public function create(User $user)

    {

        //

    }

    /**

    * Determine whether the user can update the model.

    *

    * @param  \App\User  $user

    * @param  \App\User  $model

    * @return mixed

    */

    public function update(User $user, User $model)

    {

        //

    }

    /**

    * Determine whether the user can delete the model.

    *

    * @param  \App\User  $user

    * @param  \App\User  $model

    * @return mixed

    */

    public function delete(User $user, User $model)

    {

        //

    }

}

```

## 注册策略

一旦该授权策略存在,需要将它进行注册。新的 Laravel 应用中包含的 AuthServiceProvider 包含了一个 policies 属性,可将各种模型对应至管理它们的授权策略。注册一个策略将引导 Laravel 在授权动作访问指定模型时使用何种策略:

文件:app/Providers/AuthServiceProvider.php

```

<?php

namespace App\Providers;

use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;

class AuthServiceProvider extends ServiceProvider

{

    /**

    * The policy mappings for the application.

    *

    * @var array

    */

    protected $policies = [

        'App\Model' => 'App\Policies\ModelPolicy',

        'App\User' => 'App\Policies\UserPolicy',//注册策略类

        Post::class => PostPolicy::class,

    ];

    /**

    * Register any authentication / authorization services.

    *

    * @return void

    */

    public function boot()

    {

        $this->registerPolicies();

        //

    }

}

```

## 编写策略

### 策略方法

一旦授权策略被生成且注册,我们就可以为授权的每个动作添加方法。

```

<?php

namespace App\Policies;

use App\User;

use Illuminate\Auth\Access\HandlesAuthorization;

class UserPolicy

{

    use HandlesAuthorization;

    /**

    * Determine whether the user can view the model.

    *  判断用户列表能否被用户查询

    * @param  \App\User $user 当前登录的用户

    * @param  \App\User $model 操作的对象

    * @return mixed

    */

    public function view(User $user, User $model)

    {

        //

        return $user->can('manage_users');

    }

    /**

    * Determine whether the user can create models.

    * 判断是否有权限创建用户

    * @param  \App\User $user 当前登录用户

    * @return mixed

    */

    public function create(User $user)

    {

        //

        return $user->can('manage_users');

    }

    /**

    * Determine whether the user can update the model.

    * 判断是否有权限更新用户:要求拥有用户管理权限或者是用户自己

    * @param  \App\User $user

    * @param  \App\User $model

    * @return mixed

    */

    public function update(User $user, User $model)

    {

        //

        return $user->can('manage_users') || $user->id == $model->id;

    }

    /**

    * Determine whether the user can delete the model.

    * 判断是否有权限删除用户

    * @param  \App\User $user

    * @param  \App\User $model

    * @return mixed

    */

    public function delete(User $user, User $model)

    {

        //

        return $user->can('manage_users');

    }

}

```

## 策略过滤器

对特定用户,你可能希望通过指定的策略授权所有动作。 要达到这个目的,可以在策略中定义一个 before 方法。before 方法会在策略中其它所有方法之前执行,这样提供了一种方式来授权动作而不是指定的策略方法来执行判断。这个功能最常见的场景是授权应用的管理员可以访问所有动作:

创建如下策略类:Policy,并让其他策略类继承此类。

```

<?php

namespace App\Policies;

use App\User;

use Illuminate\Auth\Access\HandlesAuthorization;

class Policy

{

    use HandlesAuthorization;

    /**

    * Create a new policy instance.

    *

    * @return void

    */

    public function __construct()

    {

        //

    }

    public function before(User $user, $ability)

    {

        if ($user->isSuperAdmin()) {

            return true;

        }

    }

}

```

## 使用策略授权动作

### 通过用户模型

Laravel 应用内置的 User 模型包含 2 个有用的方法来授权动作:can 和 cant。can 方法需要指定授权的动作和相关的模型。

如下:ajax请求 权限检验代码

```

    /**

    * 添加

    * @param UserRequest $request

    * @param Result $result

    * @return array

    */

    public function store(UserRequest $request, Result $result)

    {

        if ($request->user()->cant('create', User::class)) {

            return $result->failed('没有权限')->toArray();

        }

        try {

            $data = $request->only(['name', 'username', 'email', 'password', 'status', 'sex', 'bool_admin']);

            $data['password'] = bcrypt($data['password']);

            $user = User::create($data);

            $roles = $request->input('roles') ? $request->input('roles') : [];

            $user->assignRole($roles);

            // 要求:User和配置的 auth.default.guard 对应的provider 使用的 user model 为同一个类

            //(因为 Role 默认的guard为配置)

            $result->succeed($user);

        } catch (\Exception $exception) {

            $result->failed($exception->getMessage());

        }

        return $result->toArray();

    }




    /**

    * 更新

    *

    * @param UserRequest $request

    * @param $id

    * @param Result $result

    * @return array

    */

    public function update(UserRequest $request, $id, Result $result)

    {

        $user = User::find($id);

        if ($request->user()->cant('update', $user)) {

            return $result->failed('没有权限')->toArray();

        }

        $data = $request->only(['name', 'username', 'email', 'password', 'status', 'sex', 'bool_admin']);

        $user->update($data);

        $roles = $request->input('roles') ? $request->input('roles') : [];

        $user->syncRoles($roles);

        $result->succeed($user);

        return $result->toArray();

    }

```

### 视图权限控制

如下:创建用户的页面权限-若无权限抛出异常 AuthorizationException

```

/**

    * 新增页面

    * @param User $user

    * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View

    * @throws \Illuminate\Auth\Access\AuthorizationException

    */

    public function create(User $user)

    {

        $this->authorize('create', User::class);

        $roles = Role::get()->pluck('name', 'remarks')->toArray();

        $userRoles = [];

        return $this->backend_view('users.create_edit', compact('user', 'roles', 'userRoles'));

    }

```

处理抛出的授权异常:在app/Exceptions/Handler.php中判断异常类型,若是授权异常,则跳转到无权限页面

```

/**

    * Render an exception into an HTTP response.

    * @param \Illuminate\Http\Request $request

    * @param Exception $exception

    * @return \Illuminate\Http\RedirectResponse|\Symfony\Component\HttpFoundation\Response

    */

    public function render($request, Exception $exception)

    {

        if ($exception instanceof AuthorizationException) {

            return redirect()->guest(route('admin.permission-denied'));

        }

        return parent::render($request, $exception);

    }

```

## 通过控制器辅助函数

除了在 User 模型中提供辅助方法外,Laravel 也为所有继承了 App\Http\Controllers\Controller 基类的控制器提供了一个有用的 authorize 方法。和 can 方法类似,这个方法接收需要授权的动作和相关的模型作为参数。如果动作不被授权,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

{

    /**

    * 更新指定博客。

    *

    * @param  Request  $request

    * @param  Post  $post

    * @return Response

    */

    public function update(Request $request, Post $post)

    {

        $this->authorize('update', $post);

        // 当前用户可以更新博客...

    }

}

```

遇见异常:

```

protected function prepareException(Exception $e)

    {

        if ($e instanceof ModelNotFoundException) {

            $e = new NotFoundHttpException($e->getMessage(), $e);

        } elseif ($e instanceof AuthorizationException) {

            $e = new AccessDeniedHttpException($e->getMessage(), $e);

        } elseif ($e instanceof TokenMismatchException) {

            $e = new HttpException(419, $e->getMessage(), $e);

        }

        return $e;

    }


    //"This action is unauthorized."

```

原因:未注册用户的策略类UserPolicy 到 app/Providers/AuthServiceProvider.php (注意:每个策略类都要和对应的model绑定)

解决方案:

```

<?php

namespace App\Providers;

use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;

class AuthServiceProvider extends ServiceProvider

{

    /**

    * The policy mappings for the application.

    *

    * @var array

    */

    protected $policies = [

        'App\Model' => 'App\Policies\ModelPolicy',

        'App\User' => 'App\Policies\UserPolicy',//注册策略类

    ];

    /**

    * Register any authentication / authorization services.

    *

    * @return void

    */

    public function boot()

    {

        $this->registerPolicies();

        //

    }

}

```

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,402评论 6 499
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,377评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,483评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,165评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,176评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,146评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,032评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,896评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,311评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,536评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,696评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,413评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,008评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,815评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,698评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,592评论 2 353

推荐阅读更多精彩内容

  • Laravel框架一:原理机制篇 Laravel作为在国内国外都颇为流行的PHP框架,风格优雅,其拥有自己的一些特...
    Mr_Z_Heng阅读 3,684评论 0 13
  • 最近在和同学参与一个创业项目,用到了laravel,仔细研究了一下,发现laravel封装了很多开箱即用的方法,通...
    MakingChoice阅读 3,307评论 0 0
  • 一套完整的登陆注册业务逻辑 准备部分基础工具类Basepackage com.jericho.tools;impo...
    JerichoPH阅读 2,441评论 0 9
  • 计:计划、算计。谋:谋略、筹划。在作战时是不排斥用计谋、诓骗等手段迷惑敌人,从而最终取得战争的胜利。本期我们就来聊...
    轩辕说文史阅读 859评论 1 2
  • 有一次和朋友们聊天,说到女生一定要有两证:驾驶证、房产证。 不开心就可以开着车去兜风,还可以拿着属于自己房子的钥匙...
    蓝沄汐阅读 262评论 0 0