API Platform安全层建立在 Symfony 安全组件之上。支持其所有功能,包括全局访问控制指令 。API Platform 还提供了方便的访问控制表达式 ,您可以在资源和操作层面应用这些表达式。
<?php
// api/src/Entity/Book.php
namespace App\Entity;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Post;
use ApiPlatform\Metadata\Put;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Secured resource.
*/
#[ApiResource(security: "is_granted('ROLE_USER')")]
#[Get]
#[Put(security: "is_granted('ROLE_ADMIN') or object.owner == user")]
#[GetCollection]
#[Post(security: "is_granted('ROLE_ADMIN')")]
#[ORM\Entity]
class Book
{
#[ORM\Id, ORM\Column, ORM\GeneratedValue]
private ?int $id = null;
#[ORM\Column]
#[Assert\NotBlank]
public string $title;
#[ORM\ManyToOne]
public User $owner;
// ...
}
资源签名也可以在属性级别修改:
<?php
// api/src/Entity/Book.php
namespace App\Entity;
use ApiPlatform\Metadata\ApiProperty;
class Book
{
//...
/**
* @var string Property viewable and writable only by users with ROLE_ADMIN
*/
#[ApiProperty(security: "is_granted('ROLE_ADMIN')", securityPostDenormalize: "is_granted('UPDATE', object)")]
private $adminOnlyProperty;
}
在此示例中:
- 用户必须登录才能与
Book
资源(在资源级别配置)进行交互 - 只有具有角色
ROLE_ADMIN
的用户才能创建新资源(在发布
作中配置) - 只有拥有
ROLE_ADMIN
或拥有当前对象的用户才能替换现有帐簿(在放置
作中配置) - 只有拥有
ROLE_ADMIN
的用户才能查看或修改adminOnlyProperty
属性。只有具有ROLE_ADMIN
的用户才能创建指定adminOnlyProperty
值的新资源。 - 只有在账簿上被授予
UPDATE
属性的用户(通过选民)才能写入字段
可用的变量包括:
user
:当前登录的对象(如果有)object
:非规范化期间的当前资源类、规范化期间的当前资源或收集资源的集合previous_object
:(仅限securityPostDenormalize
)在进行修改之前的对象
克隆 – 对于创建作,这为null
请求
(仅在资源级别):当前请求
安全
属性中的访问控制检查始终在非规范化步骤之前执行。这意味着对于 PUT
或 PATCH
请求, 对象
不包含用户提交的值,而是当前存储在持久层中的值。
非规范化后执行访问控制规则
在某些情况下,在非规范化步骤之后执行安全性可能很有用。为此,请使用 securityPostDenormalize
属性:
<?php
// api/src/Entity/Book.php
namespace App\Entity;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\Put;
#[ApiResource]
#[Get]
#[Put(securityPostDenormalize: "is_granted('ROLE_ADMIN') or (object.owner == user and previous_object.owner == user)")]
class Book
{
// ...
}
这一次, 对象
变量包含在非规范化过程中从 HTTP 请求正文中提取的数据。但是,该对象尚未持久化。
此外,在某些情况下,您需要对原始数据进行安全检查。例如,在这里,只有实际所有者才能编辑他们的书。在这些情况下,可以使用包含从状态提供程序读取的对象的 previous_object
变量。
previous_object
变量中的值是从原始对象克隆的。请注意,默认情况下,此克隆不是深层克隆(它不克隆关系,关系是引用)。若要进行深度克隆,请在相关资源类中实现 clone
方法 。
使用选民挂钩自定义权限检查
挂钩自定义访问控制逻辑的最简单和推荐的方法是编写 Symfony Voter 类 。您的自定义投票器将通过 is_granted()
函数自动用于安全表达式。
为了将当前对象
提供给您的选民,请使用表达式 is_granted('READ', object)
例如:
<?php
// api/src/Entity/Book.php
namespace App\Entity;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Delete;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Post;
use ApiPlatform\Metadata\Put;
#[ApiResource(security: "is_granted('ROLE_USER')")]
#[Get(security: "is_granted('BOOK_READ', object)")]
#[Put(security: "is_granted('BOOK_EDIT', object)")]
#[Delete(security: "is_granted('BOOK_DELETE', object)")]
#[GetCollection]
#[Post(securityPostDenormalize: "is_granted('BOOK_CREATE', object)")]
class Book
{
// ...
}
请注意,如果您同时使用安全性:“...”
,然后 "post" => ["securityPostDenormalize" => "..."]
调用顶层安全性
,然后调用 securityPostDenormalize
。这可能会导致不良行为,因此请避免同时使用它们。如果需要使用 securityPostDenormalize
,请考虑为其他作而不是全局作添加安全性
。
使用 bin/console make:voter
命令创建一个 BookVoter:
<?php
// api/src/Security/Voter/BookVoter.php
namespace App\Security\Voter;
use App\Entity\Book;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Core\User\UserInterface;
class BookVoter extends Voter
{
private $security = null;
public function __construct(Security $security)
{
$this->security = $security;
}
protected function supports($attribute, $subject): bool
{
$supportsAttribute = in_array($attribute, ['BOOK_CREATE', 'BOOK_READ', 'BOOK_EDIT', 'BOOK_DELETE']);
$supportsSubject = $subject instanceof Book;
return $supportsAttribute && $supportsSubject;
}
/**
* @param string $attribute
* @param Book $subject
* @param TokenInterface $token
* @return bool
*/
protected function voteOnAttribute($attribute, $subject, TokenInterface $token): bool
{
/** ... check if the user is anonymous ... **/
switch ($attribute) {
case 'BOOK_CREATE':
if ( $this->security->isGranted(Role::ADMIN) ) { return true; } // only admins can create books
break;
case 'BOOK_READ':
/** ... other authorization rules ... **/
}
return false;
}
}
注意 1:在 POST 方法上使用 Voters 时:投票者需要一个 $attribute
和 $subject
作为输入参数,因此您必须使用 securityPostDenormalize
(即 "post" = { "securityPostDenormalize" = "is_granted('BOOK_CREATE', object)" }
),因为该对象在非规范化之前不存在(尚未创建)。
注 2:不能在集合 GET 方法上使用 Voters,请改用集合筛选器 。
# 配置访问控制错误消息
默认情况下,当 API 请求被拒绝时,您将收到“访问被拒绝”消息。您可以通过配置 securityMessage
属性或属性 securityPostDenormalizeMessage
来更改它。
例如:
<?php
// api/src/Entity/Book.php
namespace App\Entity;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\Post;
use ApiPlatform\Metadata\Put;
#[ApiResource(
security: "is_granted('ROLE_USER')",
operations: [
new Get(
security: "is_granted('ROLE_USER') and object.owner == user",
securityMessage: 'Sorry, but you are not the book owner.'
)
new Put(
securityPostDenormalize: "is_granted('ROLE_ADMIN') or (object.owner == user and previous_object.owner == user)",
securityPostDenormalizeMessage: 'Sorry, but you are not the actual book owner.'
)
new Post(
security: "is_granted('ROLE_ADMIN')",
securityMessage: 'Only admins can add books.'
)
]
)]
class Book
{
// ...
}
根据当前用户权限筛选集合
必须根据当前用户的角色或权限筛选集合,必须直接在状态提供程序级别完成。例如,当使用 Doctrine ORM、MongoDB 和 ElasticSearch 的内置适配器时,应该使用扩展从集合中删除条目。扩展允许自定义生成的 DQL/Mongo/Elastic/…查询,用于检索集合(例如,根据当前连接的用户添加 WHERE
子句),而不是使用访问控制表达式。由于扩展是服务,您可以将 Symfony 安全
类注入其中以访问当前用户的角色和权限。
如果使用自定义状态提供程序 ,则必须根据所依赖的持久性层实现筛选逻辑。
禁用作
要完全禁用应用程序中的某些作,请参阅禁用作部分。
根据当前用户更改序列化组
了解如何根据当前登录的用户动态更改当前序列化程序上下文。