Laravel 5.1 中的ACL用户授权及权限检查功能实现教程
2016-12-09 15:06
891 查看
1、引言
Laravel提供的开箱即用的认证功能使得用户注册、登录、退出和密码重置变得便捷和简单。但是如果你需要控制访问站点特定部分,或者让非管理员打开/关闭特定页面,又或者确保某些用户只能编辑自己发布的东西(如文章),那么 你就需要引入类似BeatSwitch Lock这样的工具或者自己手动编写这样的功能。我们将这样的功能称之为ACL:Access
Control Lists(访问控制列表),用于定义用户基于其用户记录属性操作或查看特定事物的权限。
幸运的是,从Laravel 5.1.11开始,Laravel提供了开箱即用的授权功能用于实现上述需求,我们不再需要做任何额外工作,用就是了。
注:在开始本节之前,请参考升级指南将Laravel升级到Laravel 5.1.11,否则不能实现相关功能。
2、能做什么?
Laravel提供的开箱即用的ACL被称作Gate(这并不是一个类似Spark的产品名称,而只是一个类和门面的名称)。使用Gate类(注入或使用Gate门面)允许我们轻松检查某个用户(当前登录用户或指定用户)是否允许操作特定事物。检查代码如下:
if (Gate::denies('update-post', $post)) { abort(403); }
将这段代码放到控制器中,它将会使用定义好的规则
update-post来检查当前认证用户是否有权限更新指定文章。
你还可以使用
Gate::allows,该方法与
denies方法相对,还可以在Blade视图模板中通过
@can来使用,还有更多更多,让我们接下来一窥究竟。
3、如果使用?
Laravel ACL建立在“权限”概念之上,权限包括一个键(例如update-post)和一个返回
true或
false的闭包(可传入参数)。
3.1 定义权限
让我们在AuthServiceProvider中定义用户更新文章权限
update-post如下:
<?php namespace App\Providers; use Illuminate\Contracts\Auth\Access\Gate as GateContract; use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; class AuthServiceProvider extends ServiceProvider{ /** * 注册应用所有的认证/授权服务. * * @param \Illuminate\Contracts\Auth\Access\Gate $gate * @return void */ public function boot(GateContract $gate) { parent::registerPolicies($gate); $gate->define('update-post', function ($user, $post) { return $user->id === $post->user_id; }); } }
正如你所看到的,定义权限闭包的第一个参数是指定用户,如果当前用户没有通过登录认证,Gate将会自定返回
false。
当然,除了闭包之外,还可以通过类方法作为第二个参数来替代闭包,该类会在容器中解析:
$gate->define('update-post', 'PostPolicy@update');
3.2 通过Gate门面检查权限
Gate提供如下方法进行权限检查:check、
allows和
denies,
check和
allows功能用法完全一样,而
denies和
allows功能相反。
如果你使用门面检查权限,则不需要传递用户实例,Gate门面会自动将当前用户传递进来:
if (Gate::denies('update-post', $post)) { abort(403); }
如果你在权限中定义了多个参数:
Gate::define('delete-comment', function ($user, $post, $comment) { // });
则检查方法如下:
if (Gate::allows('delete-comment', [$post, $comment])) { // }
如果你想检查非当前认证用户是否有权限操作,调用方法如下:
if (Gate::forUser($user)->allows('update-post', $post)) { // }
3.3 使用Gate注入检查权限
和以往一样,可以注入Gate类而不是使用其门面,注入的类和你在AuthServiceProvider中一样——
Illuminate\Contracts\Auth\Access\Gate:
public function somethingResolvedFromContainer(Gate $gate) { if ($gate->denies('update-post')) { // etc. } }
3.4 使用User模型检查权限
Laravel的App\User模型现在使用了
Authorizabletrait,因此可以使用其提供的
can和
cannot方法,分别对应Gate的
allows和
denies方法。
所以我们也可以使用User模型来检查权限:
public function update(Request $request, $id) { $post = Post::findOrFail($id); if ($request->user()->cannot('update-post', $post)) { abort(403); } // 更新文章... }
3.5 在Blade中检查权限
你可以在Blade中使用@can指令来检查权限:
<a href="/post/{{ $post->id }}">查看文章</a> @can('update-post', $post) <a href="/post/{{ $post->id }}/edit">编辑文章</a> @endcan
与之相对的是
@else指令:
@can('update-post', $post) <!-- The Current User Can Update The Post --> @else <!-- The Current User Can't Update The Post --> @endcan
3.6 中止权限检查
如果是管理员或超级用户拥有所有权限怎么做?或者你想要为用户临时切换ACL逻辑又该当如何?Gate提供的
before方法允许你在一些特定情况下在执行其他检查前就返回,不再往下检查权限:
$gate->before(function ($user, $ability) { if ($user->last_name === 'Stauffer') { return true; } });
或者使用用户自己的时候:
$gate->before(function ($user, $ability) { if ($user->isOwner()) { return true; } });
3.7
策略类
随着应用逻辑越来越复杂,要处理的权限越来越多,将所有权限定义在AuthServiceProvider显然不是一个明智的做法,因此Laravel引入了策略类,策略类是一些原生的PHP类,和控制器基于资源对路由进行分组类似,策略类基于资源对权限进行分组管理。
生成策略类
可以使用如下Artisan命令生成
PostPolicy策略类:
php artisan make:policy PostPolicy
生成的策略类位于
app/Policies目录。
然后我们可以在
AuthServiceProvider的
policies属性中注册策略类:
protected $policies = [ Post::class => PostPolicy::class, ];
下面我们编辑
PostPolicy如下:
<?php namespace App\Policies; use App\User; use App\Post; class PostPolicy{ /** * 判断给定文章是否可以被给定用户更新 * * @param \App\User $user * @param \App\Post $post * @return bool */ public function update(User $user, Post $post) { return $user->id === $post->user_id; } }
注:所有策略类都通过服务容器进行解析,这意味着你可以在策略类的构造函数中类型提示任何依赖,它们将会自动被注入。
检查策略
如果为某个资源类型定义了策略类,Gate将会使用第一个参数来判断检查策略类上的哪个方法。
因此,要检查是否有权限更新某篇文章,只需要传入文章实例和update权限:
<?php namespace App\Http\Controllers; use Gate; use App\User; use App\Post; use App\Http\Controllers\Controller; class PostController extends Controller{ /** * 更新给定文章 * * @param int $id * @return Response */ public function update($id) { $post = Post::findOrFail($id); if (Gate::denies('update', $post)) { abort(403); } // 更新文章... } }
当然也可以使用User模型和Blade指令检查权限。
此外,Laravel还提供了一个全局帮助函数
policy来检查权限:
if (policy($post)->update($user, $post)) { // }
3.8 控制器授权
由于大多数授权都会在检查权限失败的情况下退出控制器方法,因此在控制器中检查权限有一条捷径(AuthorizesRequeststrait提供,该trait在基类控制器
Controller中被使用):
<?php namespace App\Http\Controllers; use App\Post; use App\Http\Controllers\Controller; class PostController extends Controller{ /** * 更新给定文章 * * @param int $id * @return Response */ public function update($id) { $post = Post::findOrFail($id); $this->authorize('update', $post); // 更新文章... } }
和我们上面的例子一样,如果授权失败会抛出403错误。
最后,如果你的控制器方法名和策略类中的方法名相同,例如都是
update,则可以省略
authorize的第一个参数:
public function update($id){ $post = Post::findOrFail($id); $this->authorize($post); // 更新文章... }
此外,
AuthorizesRequests也提供了对非当前认证用户权限检查的支持:
$this->authorizeForUser($user, 'update', $post);
相关文章推荐
- laravel5.1的用户权限管理的实现
- laravel博客开发之利用jcorp结合laravel和ajaxupload实现用户头像上传和裁剪功能
- 黄聪:WordPress 多站点建站教程(一):怎样开启WordPress多站点功能,实现手机移动端主题开发,与主站用户数据共享
- 基于 Laravel (5.1) & Ember.js (1.13.0) 的用户授权系统
- 框架 day54 BOS项目练习(权限/角色/用户管理(CRUD),基于数据库实现动态授权,ehcache缓存权限,shiro标签,菜单权限展示)
- OAuth2.0 微博登陆网站功能的实现(一)获取用户授权及令牌 Access Token
- zend_framework-----zend_acl实现用户权限的验证
- BOS项目练习(权限/角色/用户管理(CRUD),基于数据库实现动态授权,ehcache缓存权限,shiro标签,菜单权限展示)
- Ruby on Rails实现最基本的用户注册和登录功能的教程
- spring自定义拦截器实现用户登入权限检查
- ASP.NET MVC+EF框架+EasyUI实现权限管理系列(17)-注册用户功能的细节处理(各种验证)
- Zend framework 之zend_acl,zend_auth实现用户权限控制
- Android实战简易教程-第二十四枪(基于Baas的用户表查询功能实现!)
- ASP.NET MVC+EF框架+EasyUI实现权限管理系列(17)-注册用户功能的细节处理(各种验证)
- 探究问题:ssh+extjs 权限角色用户功能的实现
- ASP.NET MVC+EF框架+EasyUI实现权限管理系列(17)-注册用户功能的细节处理(各种验证)
- 权限设置页面之间PK用户体验(功能是否合理?操作是否方便?编程实现是否简单?)
- 用TreeGrid 实现用户授权 (权限管理)
- 二次开发Jumpserver,增加权限申请模块实现用户组归属,服务器及组授权,系统用户授权申请处理
- MVC4制作网站教程第二章 部分用户功能实现代码