zoukankan      html  css  js  c++  java
  • laravel5.5授权系统

    背景:

    帖子属于某个人(拥有属性user_id),如果这是个私密帖子,只有自己才可以看到,传统的做法是

    class PostsController extends Controller
    {
        public function show($id)
        {
            $post = Post::findOrFail($id);
    
            //只有登录的用户id和帖子的所属user_id相同才可以通过
            if (auth()->id() != $post->user_id) {
                // 403 权限错误
                abort(403, 'Sorry, not sorrry.'); 
            }
            
            return $post->title;
        }
    }
    

    以上做法问题不大,但是有没有更优雅的做法呢,今天要讲的就是laravel的用户授权,Laravel 有 2 种主要方式来实现用户授权:gates 和策略。

    1. Gates

    1.1 一个简单的使用Gates的例子

    1. 准备工作:

    创建迁移文件

    php artisan make:migration create_post_table --create=posts
    

    文件内容

        Schema::create('posts', function (Blueprint $table) {
            $table->increments('id');
            $table->integer('user_id')->unsigned()->index();
            $table->string('title');
            $table->text('body');
            $table->timestamps();
        }); 
    
        DB::table('posts')->insert(
            array(
                'user_id' => 1,
                'title' => '学习用户授权',
                'body' => '两种方式,Gates、策略',
            )   
        );  
    
    

    开始迁移

    php artisan migration
    

    我们主要使用user表(laravel自带迁移文件)和posts表,其它创建Model等工作自行完成

    1. 注册Gates,在服务提供器中注册

    服务提供器 AppProvidersAuthServiceProvider.php

    /**
     * 注册任意用户认证、用户授权服务。
     *
     * @return void
     */
    public function boot()
    {
        $this->registerPolicies();
    
        Gate::define('update-post', function ($user, $post) {
            return $user->id == $post->user_id;
        });
    }
    
    1. 授权判定,可以在控制器中进行权限的判定

    控制器 app/Http/Controllers/PostManager.php

    public function show($id)
    {
        $post = Post::findOrFail($id);
    
        if (Gate::allows('update-post', $post)) {
            // 指定用户可以更新博客...
        }
        
        abort(403, 'Sorry, Permission denied'); 
    }
    
    

    以上就是一个简单的用户授权,是不是很简单,下面我们理解下更多用法

    1.2 编写Gates

    典型的做法是在 AppProvidersAuthServiceProvider 类中使用 Gate facade 定义。Gates 接受一个用户实例作为第一个参数,并且可以接受可选参数。

    1. 匿名函数
    public function boot()
    {
        $this->registerPolicies();
    
        Gate::define('update-post', function ($user, $post) {
            return $user->id == $post->user_id;
        });
    }
    
    1. 还可以使用Class@method风格字符串,比如控制器
    Gate::define('update-post', 'PostPolicy@update');
    
    1. 使用资源Gates

    一次性定义多个Gates

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

    等同于定义了下面4个功能

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

    还可以传递第三个参数给resource方法,以增加功能

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

    1.3 授权动作

    注意: 上面我们定义Gate的时候,闭包函数第一个参数为$user, 但是你并不需要传递当前认证通过的用户给这些方法。Laravel 会自动处理好传入的用户,然后传递给 gate 闭包函数

    1. 基本用法,使用Gate Facades
    //允许授权
    if (Gate::allows('update-post', $post)) {
        // 指定用户可以更新博客...
    }
    
    //否定授权
    if (Gate::denies('update-post', $post)) {
        // 指定用户不能更新博客...
    }
    
    1. 不需自动处理用户,自己指定一个用户,可以使用Gate Facade的forUser()方法
    if (Gate::forUser($user)->allows('update-post', $post)) {
        // 指定用户可以更新博客...
    }
    
    if (Gate::forUser($user)->denies('update-post', $post)) {
        // 指定用户不能更新博客...
    }
    

    2. policy策略

    显然Gates简单易用,但是当我们需要授权的动作过多的时候,就显得比较臃肿了,管理起来麻烦,laravel提供了我们另一种方式,来实现同样的功能,就是policy

    2.1 还是先看个例子

    1. 创建策略文件
    php artisan make:policy PostPolicy --model=Post
    
    1. 编写文件

    以上命令会在appPoliciesPostPolicy.php文件,该文件已经包含了4个基本的「CRUD」策略方法,我们可以增删各种方法,这里只补充view方法

        public function view(User $user, Post $post)
        {   
            //  
            return $user->id == $post->user_id;
        }
    
    
        public function create(User $user)
        {   
            //  
        }
    
    
        public function update(User $user, Post $post)
        {
            //
        }
    
    
        public function delete(User $user, Post $post)
        {
            //
        }
    
    
    1. 注册策略

    服务提供器 AppProvidersAuthServiceProvider.php, 更改$policies属性

        protected $policies = [
            'AppPost' => 'AppPoliciesPostPolicy',
        ];
    
    1. 授权判定

    控制器 app/Http/Controllers/PostManager.php

    public function show($id)
    {
        $post = Post::findOrFail($id);
    
        if ($user->can('views', $post)) {
            // 指定用户可以 ......
        }
        
        abort(403, 'Sorry, Permission denied'); 
    }
    

    以上就是使用policy的一个简单实例,下面还是进行更细致的梳理

    2.2 编写策略

    生成策略文件后,我们可以自己创建删除方法,以满足我们的需求,可以为自定义策略方法使用自己喜欢的名字

        public function view(User $user, Post $post)
        {   
            //  
            return $user->id == $post->user_id;
        }
    
        public function create(User $user)
        {   
            //  
        }
        
        // 其它更多需要的方法
        
    
    

    我们还可以使用策略过滤器

    比如我们想要超级管理员有所有权限,可以在策略中定义一个 before 方法

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

    如果你想拒绝用户所有的授权,你应该在 before 方法中返回 false。如果返回的是 null,则通过其它的策略方法来决定授权与否。

    2.3 授权策略

    2.3.1 通过用户模型

    1. 指定模型的动作
      Laravel 应用内置的 User 模型包含 2 个有用的方法来授权动作:can 和 cant
    if ($user->can('update', $post)) {
        //
    }
    
    if ($user->cant('update', $post)) {
        //
    }
    

    注意: 如果指定模型的 策略已被注册,can 方法会自动调用核实的策略方法并且返回 boolean 值。如果没有策略注册到这个模型,can 方法会尝试调用和动作名相匹配的基于闭包的 Gate。

    1. 不指定模型的动作

    比较下面的两个方法

        public function view(User $user, Post $post)
        {   
            //  
            return $user->id == $post->user_id;
        }
    
        public function create(User $user)
        {   
            //  
        }
    

    试想,如果我们有另个一个VideoPolice, 同样有view和create方法。

        public function view(User $user, Video $video)
        {   
            //  
            return $user->id == $video->user_id;
        }
    
        public function create(User $user)
        {   
            //  
        }
    

    不难想象,尽管第一参数都是view, 第二个传入了模型实例,我们在注册策略的时候做了模型和策略的映射,这样就可以区分使用的是PostPolicy还是VideoPolicy

    $user->can('view', $post);
    $user->can('view', $video);
    

    那么create方法呢, 我们可以传递一个类名给 can 方法。当授权动作时,这个类名将被用来判断使用哪个策略

    $user->can('create', Post::class);
    $user->can('view', Video::class);
    

    2.3.2 通过中间件

    1. 通过隐式模型绑定,指定模型动作
    use AppPost;
    
    Route::put('/post/{post}', function (Post $post) {
        // 当前用户可以更新博客...
    })->middleware('can:update,post');
    

    关于隐式模型绑定,这里顺便提一下,更详细的请自己查询。

    首先可以定义这样一条路由

    Route::get('/test/{post}','TestController@test');
    
    

    控制器引入模型文件

    use AppHttpModelPost;
    

    test方法传入实例,并类型提示

        public function test(Post $post)
        {   
            http_response_code(500);
            dd($post);
        }
    
    

    我们访问这样的一条路由 http://xxxx.com/test/1 ,将dd()出id=1 的post实例,我们并没有专门的实例化post这个model,但是我们自动返回了id=参数的post实例

    1. 不需要指定模型的动作
    Route::post('/post', function () {
        // 当前用户可以创建博客...
    })->middleware('can:create,AppPost');
    

    2.3.3 通过控制器辅助函数autorize()

    如果动作不被授权,authorize 方法会抛出 IlluminateAuthAccessAuthorizationException 异常,然后被 Laravel 默认的异常处理器转化为带有 403 状态码的 HTTP 响应:

    1. 指定模型动作
    public function update(Request $request, Post $post)
    {
        $this->authorize('update', $post);
    
        // 当前用户可以更新博客...
    }
    
    1. 不指定模型动作
    public function create(Request $request)
    {
        $this->authorize('create', Post::class);
    
        // 当前用户可以新建博客...
    }
    

    2.3.4 通过blade模板

    可以使用@can 和 @cannot

    1. 指定模型动作
    @can('update', $post)
        <!-- 当前用户可以更新博客 -->
    @elsecan('create', $post)
        <!-- 当前用户可以新建博客 -->
    @endcan
    
    @cannot('update', $post)
        <!-- 当前用户不可以更新博客 -->
    @elsecannot('create', $post)
        <!-- 当前用户不可以新建博客 -->
    @endcannot
    
    1. 不指定模型动作
    @can('create', AppPost::class)
        <!-- 当前用户可以新建博客 -->
    @endcan
    
    @cannot('create', AppPost::class)
        <!-- 当前用户不可以新建博客 -->
    @endcannot
    

    实际上@can 和@cannot 提供了方便的缩写, 分别等同于下面写法

    @if (Auth::user()->can('update', $post))
        <!-- 当前用户可以更新博客 -->
    @endif
    
    @unless (Auth::user()->can('update', $post))
        <!-- 当前用户不可以更新博客 -->
    @endunless
    
  • 相关阅读:
    [CF603C] Lieges of Legendre
    [CF1070A] Find a Number
    [CF431D] Random Task
    2020牛客暑期多校训练营(第二场)C
    2020牛客暑期多校训练营(第二场)F
    2020牛客暑期多校训练营(第二场)D
    2020牛客暑期多校训练营(第一场)H
    [CF1000E] We Need More Bosses
    Java学习2 (ThreadLocal)
    Java复习1
  • 原文地址:https://www.cnblogs.com/redirect/p/8658740.html
Copyright © 2011-2022 走看看