系统中存在多种或者多个用户的时候,总会涉及到权限的问题。比如说一个多用户博客系统,我们总是希望作者A写的内容,只能作者A来修改而不是其他人来修改。
从5.1.11版本开始,Laravel引入了一个简单的方式来管理授权逻辑以便控制对资源的访问权限。
对于比较简单的场景
如何定义权限
我们可以在AuthServiceProvider
定义权限。
例如
namespace App\Providers;
class AuthServiceProvider extends ServiceProvider
{
public function boot(gateContract $gate)
{
parent::registerPolicies($gate);
$gate->define('update-post', function($user, $post){
return $user->id === $post->user_id;
});
}
}
上述定义的update-post
方法并没有检查$user
是否为null
的情况,是因为用户未经过登录认证或者用户没有通过forUser
方法指定,Gate
会自动为所有的权限放回false
。
除了上述注册授权毁掉闭包的形式之外,还可以通过包含权限类名和类方法的方式来注册权限方法,当需要的时候,该类会通过服务容器进行解析。
$gate->define('update-post', 'PostPolicy@update');
如何检查权限
- 可以使用
Gate
门面的check
,allows
,denies
方法。所有这些方法都接受权限名和传递给给该权限回调的参数作为参数。
...
public function update($id)
{
$post = Post::findOrFail($id);
if(Gate::denies('update-post', post)) {
abort(403);
}
}
...
allows
和check
方法是一致的,与denies
相反。
传递多个参数的情况
如果权限回调需要接收多个参数
Gate::define('delete-comment', function($user, $post, $comment) {
//
}
权限需要多个参数,只需要传递一个 参数数组 进去就可以了。
if(Gate::allow('delete-comment', [$post, $comment])) {
//
}
通过User模型实例检查权限。
App\User
模型使用一个Authorizable
trait来提供两种方法: can
与cannot
。 这两种方法的功能和Gate
门面上的allows
和denies
方法类似。因此,使用我们前面的例子:
public function update(Request $request, $id)
{
$post = Post::findOrFail($id);
if($request->user()->cannot('update-post', $post)) {
abort(403);
}
// 更新文章操作
}
比较复杂的场景
在比较复杂的场景下,AuthServiceProvider
中定义所有的授权逻辑将会变得越来越臃肿。
在大型应用中,我们可以把不同的授权逻辑分割到多个“策略”中。
创建策略类
php artisan make:policy PostPolicy
该命令可以在app/Policies
生成一个策略类。
注册策略类
AuthServiceProvider里的policies
属性来映射实体及管理该实体的策略类。
class AuthServiceProvider extends ServiceProvider {
protected $policies = [
Post::class => PostPolicy::class,
];
}
编写策略类
namespace App\Policies
use App\User;
use App\Post;
class PostPolicy{
public function update(User $user, Post $post)
{
return $user->id === $post->user_id;
}
}
检查策略
策略类方法的调用方式跟之前基于授权回调的闭包一样,可以使用:
- Gate Facade
- User Model
- @can 模板指令
- 帮助函数
Gate Facade
Gate
会自动通过检测传递过来的第一个非User
类来判断。
if(Gate::denies('update', $post)) {
abort(403);
}
User模型
if($user->can('update', $post)) {
//
}
通过帮助函数 policy
if(policy($post)->update($user, $post)) {
//
}
控制器授权
App\Http\Controllers\Controller
使用了AuthorizesReuqest
trait,该trait提供了可用于快速授权给定动作的authorize
方法,如果授权不通过,则抛出HttpException
异常。
class PostController extends Controller {
public function update($id)
{
$post = Post::findOrFail($id);
$this->authorize('update', $post);
}
}
授权失败则会抛出HttpException
异常并生成带403 Not Authorized
状态码的Http响应。
对于非当前用户:
$this->authorizeForUser($user, 'update', $post);
自动策略类判断
通常,一个策略类方法对应一个控制器上的方法。
在这个前提上,Laravel允许你简单传递实例参数到authorize
方法,被授权的权限将会自动基于调用的方法名进行判断。
public function update($id){
$post = Post::findOrFail($id);
$this->authorize($post);
// 更新文章
}
声明: 本文参考了Laravel的文档