在Laravel中为我们提供了一种简单的方式来处理用户授权动作,主要有两种方式Gates
和Policy
(策略),可以将其类比路由器(Gates
)和控制器(Policy
)Gates
提供了一个简单的,基于闭包的方式来进行授权认证,而Policy
在特定的模型或资源中通过分组来实现授权认证的逻辑
这两种方式都能进行用户权限认证,但Gates
一般用于与模型和资源无关的地方,如查看管理员后台,而Policy
则用于特定的模型或者资源中
Gates是用来决定用户是否被授权执行给定的动作的闭包函数,并且典型的做法是在App\Providers\AuthServiceProvider
类中使用Gate
门面定义。
define('授权动作名','动作逻辑')
Gates
闭包函数接受两个参数
第一个参数:用户实例
第二个参数:(可选)如相关的Eloquent模型
如下例
在boot
方法中 判断用户是否有权限更新当前文章,返回true/false
public function boot()
{
$this->registerPolicies();
Gate::define('update-post',function($user,$post){
return $user->id == $post->user_id
})
}
同样Gates也是使用class@method
的风格回调字符串来定义如
Gate::define('update-post','PostPolicy@update')
资源Gates
我们还可以使用 resource
方法一次性定义多个Gate功能;像资源控制器那样
Gate::resource('post','PostPolicy')
//创建一个资源Gate
资源Gate会定义以下Gates
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']);
生成如下Gates
Gate::define('posts.view','PostPolicy@view');
Gate::define('posts.create','PostPolicy@create');
Gate::define('posts.update','PostPolicy@update');
Gate::define('posts.delete','PostPolicy`在这里插入代码片`@delete');
Gate::define('posts.image','PostPolicy@updateImage');
Gate::define('posts.photo','PostPolicy@updatePhoto');
授权动作
使用Gates来授权动作时,应该使用allows
或deies
方法,这里你并不需要传递当前认证通过的用户给这些方法,,Laravel会自动处理好传入的用户,然后传递给Gate闭包函数
public function root(){
$post = Post::find(1);//获取文章实例
if(Gate::allows('update-post',$post)){
//如果有更新权限
dump('当前用户可以更新此博客');
}
if(Gate::denies('update-post',$post)){
//如果没有更新权限
dump('当前用户不能更新此博客');
}
}
这里是判断当前登录用户的权限,如果你想指定一个特定用户的权限,可以通过Gate
门面的forUser
方法,传入用户实例
public function root(){
$user = User::find(3);
$post = Post::find(1);
if(Gate::forUser($user)->allows('update-post',$post)){
//指定的用户可以更新的博客
dump('主编更新所有博客');
}
创建策略(Policy)
策略是在特定模型或者资源中组织授权逻辑的类,如我们做一个博客系统,会有一个Post
模型和与之对应的PostPolicy
类来授权用户的动作。
可以使用artisan
命令make:policy
创建策略,生成的策略存放在app\Policies
目录下
php artisan make:policy PostPolicy
该命令会生成一个空的策略类
如果你想生成的策略类包含[CRUD·](view,create,update,delete)
可使用以下命令
php artisan make:policy PostPolicy --model=Post
注册策略
创建好策略后我们需将他注册,在AuthServiceProvider
的plicies
属性,建立模型与对应策略的映射
protected $policies = [
Post::class =>PostPolicy::class
];
编写策略
注册了策略后我们可以为每个授权的动作添加方法,如
在PostPolicy
中定义一个update
方法,在这个方法中判断用户是否可以更新某篇文章
update
方法接受两个参数,User
和Post
实例,并且返回值为true
或false
以此来判断用户是否可以更新谋篇文章
//判断用户是否可以更新某篇文章
public function update(User $user, $post)
{
dump('用户id:'.$user->id);
dump('文章用户id:'.$post->user_id);
return $user->id == $post->user_id;
}
不包含模型方法
一些策略方法只接受当前认证通过的用户作为参数,而不用传入相关模型实例。如create
动作
当创建一篇博客时,我们需要检测用户是否有权限创建博客
定义方法如下
public function create(User $user)
{
}
策略过滤器
如果你想通过策略指定一个特定用户的获取所有权限,可以在策略中定义一个before
方法,before
方法会在策略的其他方法之前执行
public function before(User $user){
if($user->id==1){
//假定id为1的用户拥有所有权限
return true;
}
}
相对的,拒绝用户所有权限,返回false
Laravel内置额User
模型包含2个方法来获取授权动作can
和cant
。can
方法需要指定授权的动作和相关的模型,如判断一个用户是否授权更新指定的Post
模型
$user = User::find(Auth::id());
$post = Post::find(2);
if($user->can('update',$post)){
dump('当前用户可以更新文章');
}
if($user->cant('update',$post)){
// dump($post);
dump('当前用户不能更新文章');
}
注意:can 方法不能静态调用
使用can
/cant
方法有两种情况
1.指定模型与对应策略建立了映射关系:这种情况can
/cant
方法会自动调用策略方法并返回布尔值
2.指定模型与对应策略没有建立映射关系:这种情况can
/cant
方法会尝试调用和动作名相匹配的基于闭包的Gate
不指定模型的授权动作
一些动作,如create
并不需要指定模型实例,在这种情况下,可传递一个类名给can
方法。当授权动作时,这个类名用于判断使用的是哪个策略
if($user->can('create',Post::class)){
}
Laravel包含一个可以在请求到达路由或控制器之前就进行动作授权的中间件,默认
Illuminate\Auth\Middleware\Authorize
中间件被指定到App\Http\Kernel
类的can
键上。
如:
//路由
Route::get('/test/{post}','TestControler@root')->middleware('can:update,post');
//控制器
public function root(Post $post){
dump('用户可以更新文章');
}
上述代码中我们调用中间件can
并传递了两个参数,第一个为需要授权的动作名,第二个参数是模型实例,由于这里使用了隐式模型绑定,所以Post
会传递到策略方法中,当用户不具备权限时,中间件会生成带有403状态码的HTTP响应
用户具有权限
用户不具权限
除了在User
模型中提供辅助方法外,Laravel也为所有继承了App\Http\Controllers\Controller
基类的控制器提供了一个有用的authorize
方法。和can
方法类似,这个方法接收需要授权的动作和相关的模型作为参数,如果动作不被授权,authorize
方法会抛出Illuminate\Auth\ Access\AuthorizationException
异常,然后被Laravel默认的异常处理转化为带有403
状态码的HTTP响应:
正常拥有权限
if( $this->authorize('update', $post)){
dump('当前用户可以更新');
}
不指定模型的动作
当你不用指定模型实例,需要传递一个类名给authorize
方法
public function create(Request $request)
{
$this->authorize('create', Post::class);
// 当前用户可以新建博客...
}
当编写Blade模板时,你可能希望模板或者模板的区域只展示给有相关权限的用户查看
对此可以直接使用@can
和@cannot
指令
@can('update',$post)
有更新权限的视图
@elsecan('create'.$post)
有创建权限的视图
@endcan
//--------------------
@cannot('update',$post)
用户不可以更新博客
@elsecannot('create',$post)
用户不可以创建博客
@endcannot
不需要指定模型的动作
还和上面一样 ,传入类名即可
@can('create',App\Post::class)
//用户可以新键博客
@endcan
@cannot('create',App\Post::class)
@enncannot
<!DOCTYPE html>
<html>
<head>
<title>测试模板</title>
</head>
<body>
<h1>用户授权认证</h1>
{{$post}}
@can('update',$post)
<div style='background: skyblue;width:300px;height:150px'>
用户更新权限
@endcan
@cannot('update',$post)
<div style='background: #eee;width:300px;height:150px'>
普通用户
</div>
@endcannot
</body>
</html>