策略解析器

将资源对象映射到其各自的策略类是策略解析器处理的行为。我们提供了一些解析器来帮助您入门,但您可以通过实现 Authorization\Policy\ResolverInterface 来创建自己的解析器。内置的解析器是

  • MapResolver 允许您将资源名称映射到其策略类名称,或映射到对象和可调用对象。

  • OrmResolver 基于约定,针对常见的 ORM 对象应用策略解析。

  • ResolverCollection 允许您将多个解析器聚合在一起,并按顺序搜索它们。

使用 MapResolver

MapResolver 允许您将资源类名称映射到策略类名称、策略对象或工厂可调用对象。

use Authorization\Policy\MapResolver;

$mapResolver = new MapResolver();

// Map a resource class to a policy classname
$mapResolver->map(Article::class, ArticlePolicy::class);

// Map a resource class to a policy instance.
$mapResolver->map(Article::class, new ArticlePolicy());

// Map a resource class to a factory function
$mapResolver->map(Article::class, function ($resource, $mapResolver) {
    // Return a policy object.
});

使用 OrmResolver

OrmResolver 是 CakePHP ORM 的基于约定的策略解析器。OrmResolver 应用以下约定

  1. 策略位于 App\Policy

  2. 策略类以 Policy 类后缀结尾。

OrmResolver 可以解析以下对象类型的策略

  • 实体 - 使用实体类名。

  • 表 - 使用表类名。

  • 查询 - 使用查询的 repository() 的返回值获取类名。

在所有情况下,都会应用以下规则

  1. 资源类名用于生成策略类名。例如 App\Model\Entity\Bookmark 将映射到 App\Policy\BookmarkPolicy

  2. 插件资源将首先检查应用程序策略,例如 App\Policy\Bookmarks\BookmarkPolicy 用于 Bookmarks\Model\Entity\Bookmark

  3. 如果找不到应用程序覆盖策略,则会检查插件策略。例如:Bookmarks\Policy\BookmarkPolicy

对于表对象,类名转换将导致 App\Model\Table\ArticlesTable 映射到 App\Policy\ArticlesTablePolicy。查询对象将调用其 repository() 方法,并根据生成的结果表类生成策略。

OrmResolver 支持通过其构造函数进行自定义

use Authorization\Policy\OrmResolver;

// Change when using a custom application namespace.
$appNamespace = 'App';

// Map policies in one namespace to another.
// Here we have mapped policies for classes in the ``Blog`` namespace to be
// found in the ``Cms`` namespace.
$overrides = [
    'Blog' => 'Cms',
];
$resolver = new OrmResolver($appNamespace, $overrides)

使用多个解析器

ResolverCollection 允许您将多个解析器聚合在一起

use Authorization\Policy\ResolverCollection;
use Authorization\Policy\MapResolver;
use Authorization\Policy\OrmResolver;

$ormResolver = new OrmResolver();
$mapResolver = new MapResolver();

// Check the map resolver, and fallback to the orm resolver if
// a resource is not explicitly mapped.
$resolver = new ResolverCollection([$mapResolver, $ormResolver]);

创建解析器

您可以通过实现 Authorization\Policy\ResolverInterface 来实现自己的解析器,这需要定义 getPolicy($resource) 方法。

自定义解析器有用的一个示例场景是,当从 AuthComponent 迁移时,将授权插件与基于控制器的访问控制桥接。首先,我们需要创建一个通配策略,它将调用我们的控制器方法

// in src/Policy/ControllerHookPolicy.php
namespace App\Policy;

class ControllerHookPolicy
{
    public function __call(string $name, array $arguments)
    {
        /** @var ?\Authorization\Identity $user */
        [$user, $controller] = $arguments;

        return $controller->isAuthorized($user?->getOriginalData());
    }
}

我们的策略类使用 __call,以便它可以处理我们控制器中的所有操作。我们的策略调用我们控制器上的 isAuthorized() 方法,为我们提供了与现有逻辑的向后兼容性。接下来,我们将创建一个策略解析器,它将解析控制器以用于我们的自定义策略

// in src/Policy/ControllerResolver.php
namespace App\Policy;

use Authorization\Policy\ResolverInterface;
use Authorization\Policy\Exception\MissingPolicyException;
use Cake\Controller\Controller;

class ControllerResolver implements ResolverInterface
{
    public function getPolicy($resource)
    {
        if ($resource instanceof Controller) {
            return new ControllerHookPolicy();
        }

        throw new MissingPolicyException([get_class($resource)]);
    }
}

创建完策略和解析器后,我们可以直接将解析器添加到我们的应用程序中,或者使用 ResolverCollection 将其与其他解析器结合起来。