Laravel中的Policies

xcwong

系统中存在多种或者多个用户的时候,总会涉及到权限的问题。比如说一个多用户博客系统,我们总是希望作者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);
    }
}
...

allowscheck方法是一致的,与denies相反。

传递多个参数的情况

如果权限回调需要接收多个参数

Gate::define('delete-comment', function($user, $post, $comment) {
    //
}

权限需要多个参数,只需要传递一个 参数数组 进去就可以了。

if(Gate::allow('delete-comment', [$post, $comment])) {
    //
}

通过User模型实例检查权限。

App\User模型使用一个Authorizabletrait来提供两种方法: cancannot。 这两种方法的功能和Gate门面上的allowsdenies方法类似。因此,使用我们前面的例子:

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的文档

阅读 1.8K

helloword
happy coding

hello world

502 声望
20 粉丝
0 条评论

hello world

502 声望
20 粉丝
宣传栏