背景:

帖子属于某个人(拥有属性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,在服务提供器中注册

服务提供器 App\Providers\AuthServiceProvider.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

典型的做法是在 App\Providers\AuthServiceProvider 类中使用 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. 编写文件

以上命令会在app\Policies\PostPolicy.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. 注册策略

服务提供器 App\Providers\AuthServiceProvider.php, 更改$policies属性

    protected $policies = [
'App\Post' => 'App\Policies\PostPolicy',
];
  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 App\Post;

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

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

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

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

控制器引入模型文件

use App\Http\Model\Post;

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,App\Post');

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

如果动作不被授权,authorize 方法会抛出 Illuminate\Auth\Access\AuthorizationException 异常,然后被 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', App\Post::class)
<!-- 当前用户可以新建博客 -->
@endcan @cannot('create', App\Post::class)
<!-- 当前用户不可以新建博客 -->
@endcannot

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

@if (Auth::user()->can('update', $post))
<!-- 当前用户可以更新博客 -->
@endif @unless (Auth::user()->can('update', $post))
<!-- 当前用户不可以更新博客 -->
@endunless

laravel5.5授权系统的更多相关文章

  1. Python+Django+SAE系列教程17-----authauth (认证与授权)系统1

    通过session,我们能够在多次浏览器请求中保持数据,接下来的部分就是用session来处理用户登录了. 当然,不能仅凭用户的一面之词,我们就相信,所以我们须要认证. 当然了,Django 也提供了 ...

  2. 02 【PMP】项目管理系统、PMIS、工作授权系统、配置管理系统、变更管理

    PMBOK融会贯通:盘点八大系统<项目管理系统.PMIS.工作授权系统.配置管理系统.变更管理>   一.  PMBOK相关系统: 工作系统作为事业环境因素,提高或限制项目管理的灵活性,并 ...

  3. 分享一个单点登录、OAuth2.0授权系统源码(SimpleSSO)

    SimpleSSO 关于OAuth 2.0介绍: http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html 系统效果: 登录界面: 首页: 应用界面: ...

  4. .Net Core 授权系统组件解析

    前面关于.Net Core如何进行用户认证的核心流程介绍完毕之后,.Net Core 认证系统之Cookie认证源码解析远程认证暂时不介绍,后期有时间,我会加上.接下去介绍认证组件是如何和认证组件一起 ...

  5. laravel5.5 调用系统自带登陆认证auth

    1执行命令 php artisan make:auth 2 编辑文件 config/auth guardes 'admin' => [ 'driver' => 'session', 'pr ...

  6. laravel5.5缓存系统

    目录 1 Redis的配置 1.1 安装PRedis 1.2 配置 1.2.1 配置redis数据库 1.2.2 更改session的配置 1.2.3 更改cache配置 1.3 使用redis 2 ...

  7. ABP框架 - 授权

    文档目录 本节内容: 简介 关于 IPermissionChecker 定义许可 检查许可 使用 AbpAuthorize特性 AbpAuthorize特性注意事项 废止授权 使用 IPermissi ...

  8. ABP理论学习之授权(Authorization)

    返回总目录 本篇目录 介绍 定义权限 检查权限 使用AbpAuthorize特性 使用IPermissionChecker Razor视图 客户端(Javascript) 权限管理者 介绍 几乎所有的 ...

  9. mysql系统数据库

    mysql系统数据库主要存储了一些存储MySQL服务的系统信息表.一般情况下mysql库的表都是MYASIM引擎,除非个别情况.mysql库的表的作用大致可以分为以下几类: (1)授权系统表 (2)系 ...

随机推荐

  1. php中的curl_multi的应用(php多进程)

    相信许多人对PHP手册中语焉不详的curl_multi一族的函数头疼不已,它们文档少,给的例子 更是简单的让你无从借鉴,我也曾经找了许多网页,都没见一个完整的应用例子. curl_multi_add_ ...

  2. centos 7中磁盘挂载重启后挂载失效

     在centos 7磁盘挂载成功后,关机重启,挂载磁盘失效,需要重新挂载,不用重新挂载的开机挂载方法如下: 1.先检验要挂载的磁盘是否已被挂载,有的话先卸除 2.修改 /etc/fstab 文件 ,最 ...

  3. react+webpack 引入字体图标

    在使用react+webpack 构建项目过程中免不了要用到字体图标,在引入过程中报错,不能识别字体图标文件中的@符,报错 Uncaught Error: Module parse failed: U ...

  4. CVE-2017-8464 LNK文件(快捷方式)远程代码执行漏洞复现

    北京时间2017年6月13日凌晨,微软官方发布6月安全补丁程序,“震网三代” LNK文件远程代码执行漏洞(CVE-2017-8464)和Windows搜索远程命令执行漏洞(CVE-2017-8543) ...

  5. 【转】Android UI开发第三十一篇——Android的Holo Theme

    好长时间没写Android UI方面的文章了,今天就闲扯一下Android的Holo主题.一直做android开发的可能都知道,Android 系统的UI有过两次大的变化,一次是android 3.0 ...

  6. 线程 task训练

    1. task类表示一个线程,最简单的task的构造方法是 ,参数是Action<t>,是一个无返回值的泛型委托. 指向要执行的函数.当调用·start()方法时,就执行子线程.执行指向的 ...

  7. Linux---who命令学习

    who命令 获取正在登录系统的用户 使用Linux的who命令 第一个参数book代表用户名,第二个参数tty7代表终端名,第三个参数代表时间,第四个参数代表用户的登录地址. 阅读手册 使用命令读手册 ...

  8. 基于java开发的开源代码GPS北斗位置服务监控平台

    最近在研究位置服务平台,基于全球卫星定位技术(GNSS).互联网技术.空间地理信息技术(GIS).3G/4G无线通信技术,面向全国公众用户建立大容量.实时.稳定的位置信息服务运营平台.实现管理目标的实 ...

  9. Vue源码学习三 ———— Vue构造函数包装

    Vue源码学习二 是对Vue的原型对象的包装,最后从Vue的出生文件导出了 Vue这个构造函数 来到 src/core/index.js 代码是: import Vue from './instanc ...

  10. Github学生包的申请

    Github Education为了大学生们更好的进行开发,进行边做边学,与一些合作伙伴和朋友一起创建GitHub学生开发者包. 里面内容非常丰富,应有尽有: AWS 亚马逊云服务 $75-$150  ...