策略是解析给定对象权限的类。您可以为应用程序中您希望应用权限检查的任何类创建策略。
您可以在 src/Policy
目录中创建策略。策略类没有预期实现的通用基类或接口。然后,应用程序类将“解析”到匹配的策略类。有关如何解析策略的更多信息,请参阅 策略解析器 部分。
通常,您希望将策略放在 src/Policy 中并使用 Policy
类后缀。现在,我们将为应用程序中的 Article 实体创建一个策略类。在 src/Policy/ArticlePolicy.php 中放入以下内容
<?php
namespace App\Policy;
use App\Model\Entity\Article;
use Authorization\IdentityInterface;
class ArticlePolicy
{
}
除了实体外,表对象和查询也可以解析策略。查询对象将调用其 repository()
方法,并将根据表名生成策略类。表类 App\Model\Table\ArticlesTable
将映射到 App\Policy\ArticlesTablePolicy
。
您可以使用 bake
为 ORM 对象生成空的策略类
# Create an entity policy
bin/cake bake policy --type entity Article
# Create a table policy
bin/cake bake policy --type table Articles
我们刚刚创建的策略类现在没有做太多事情。让我们定义一个方法,让我们检查用户是否可以更新文章
public function canUpdate(IdentityInterface $user, Article $article)
{
return $user->id == $article->user_id;
}
策略方法必须返回 true
或 Result
对象以表示成功。所有其他值将被解释为失败。
策略方法将在处理未经身份验证的用户时为 $user
参数接收 null
。如果您希望自动使匿名用户的策略方法失败,可以使用 IdentityInterface
类型提示。
除了布尔值外,策略方法还可以返回 Result
对象。 Result
对象允许提供更多有关策略通过/失败原因的上下文
use Authorization\Policy\Result;
public function canUpdate(IdentityInterface $user, Article $article)
{
if ($user->id == $article->user_id) {
return new Result(true);
}
// Results let you define a 'reason' for the failure.
return new Result(false, 'not-owner');
}
任何不是 true
或 ResultInterface
对象的返回值都将被视为失败。
除了策略能够定义通过/失败授权检查外,它们还可以定义“范围”。范围方法允许您修改应用授权条件的另一个对象。这在将列表视图限制为当前用户时非常有用
namespace App\Policy;
class ArticlesTablePolicy
{
public function scopeIndex($user, $query)
{
return $query->where(['Articles.user_id' => $user->getIdentifier()]);
}
}
在某些策略中,您可能希望对策略中的所有操作应用通用检查。当您需要拒绝对提供资源的所有操作时,这很有用。要使用前置条件,您需要在策略中实现 BeforePolicyInterface
namespace App\Policy;
use Authorization\IdentityInterface;
use Authorization\Policy\BeforePolicyInterface;
use Authorization\Policy\ResultInterface;
class ArticlesPolicy implements BeforePolicyInterface
{
public function before(?IdentityInterface $identity, mixed $resource, string $action): ResultInterface|bool|null
{
if ($identity->getOriginalData()->is_admin) {
return true;
}
// fall through
}
}
预期之前钩子将返回以下三个值之一
true
用户被允许继续执行该操作。
false
用户不被允许继续执行该操作。
null
之前钩子没有做出决定,将调用授权方法。
与策略一样,范围也可以定义前置条件。当您想要对策略中的所有范围应用通用条件时,这些条件很有用。要在范围内使用前置条件,您需要在范围策略中实现 BeforeScopeInterface
namespace App\Policy;
use Authorization\Policy\BeforeScopeInterface;
class ArticlesTablePolicy implements BeforeScopeInterface
{
public function beforeScope($user, $query, $action)
{
if ($user->getOriginalData()->is_trial_user) {
return $query->where(['Articles.is_paid_only' => false]);
}
// fall through
}
}
预期之前范围钩子将返回修改后的资源对象,或者如果返回 null
,则将按正常方式调用范围方法。
请参阅 应用策略范围,了解如何在控制器操作中应用策略。