API-Platform中基于角色的结果过滤最佳实现方案
解决方案:基于角色的自动数据过滤与匿名化
一、基础实体配置
假设核心数据实体为DataEntry,先完成实体与机构的关联,并标记敏感字段:
// src/Entity/DataEntry.php namespace App\Entity; use ApiPlatform\Core\Annotation\ApiResource; use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Serializer\Annotation\Groups; /** * @ApiResource( * normalizationContext={"groups"={"data:read"}}, * denormalizationContext={"groups"={"data:write"}} * ) * @ORM\Entity(repositoryClass="App\Repository\DataEntryRepository") */ class DataEntry { /** * @ORM\Id * @ORM\GeneratedValue * @ORM\Column(type="integer") * @Groups({"data:read"}) */ private $id; /** * @ORM\Column(type="string", length=255) * @Groups({"data:read"}) */ private $name; /** * @ORM\ManyToOne(targetEntity="App\Entity\Institution") * @ORM\JoinColumn(nullable=false) */ private $institution; // Getters & Setters public function getInstitution(): ?Institution { return $this->institution; } public function setInstitution(?Institution $institution): self { $this->institution = $institution; return $this; } public function getName(): ?string { return $this->name; } public function setName(string $name): self { $this->name = $name; return $this; } }
用户实体需关联所属机构(管理员可无关联):
// src/Entity/User.php namespace App\Entity; use Symfony\Component\Security\Core\User\UserInterface; use Doctrine\ORM\Mapping as ORM; /** * @ORM\Entity(repositoryClass="App\Repository\UserRepository") */ class User implements UserInterface { // 其他字段... /** * @ORM\ManyToOne(targetEntity="App\Entity\Institution") * @ORM\JoinColumn(nullable=true) */ private $institution; public function getInstitution(): ?Institution { return $this->institution; } }
二、自动过滤机构数据(非管理员用户)
通过API-Platform的查询构建监听器,在数据库查询阶段自动添加机构过滤条件,避免全量加载后再过滤:
// src/EventListener/DataEntryQueryFilterSubscriber.php namespace App\EventListener; use ApiPlatform\Core\Bridge\Doctrine\Orm\Event\QueryBuilderEvent; use App\Entity\DataEntry; use App\Entity\User; use Symfony\Component\Security\Core\Security; class DataEntryQueryFilterSubscriber { private $security; public function __construct(Security $security) { $this->security = $security; } public function onQueryBuilder(QueryBuilderEvent $event) { $user = $this->security->getUser(); if (!$user instanceof User || $user->hasRole('ROLE_ADMIN')) { return; } $institution = $user->getInstitution(); if (!$institution) { $event->getQueryBuilder()->andWhere('1=0'); // 无机构用户返回空结果 return; } $resourceClass = $event->getResourceClass(); if ($resourceClass !== DataEntry::class) { return; } $rootAlias = $event->getQueryBuilder()->getRootAliases()[0]; $event->getQueryBuilder() ->andWhere(sprintf('%s.institution = :institution', $rootAlias)) ->setParameter('institution', $institution); } }
在services.yaml注册监听器:
services: App\EventListener\DataEntryQueryFilterSubscriber: tags: - { name: api_platform.doctrine.orm.query_builder_listener, event: "post_build" }
三、管理员数据匿名化处理
针对管理员角色,在序列化阶段动态修改敏感字段内容,确保数据匿名化:
// src/EventListener/DataEntryAnonymizationListener.php namespace App\EventListener; use ApiPlatform\Core\EventListener\EventPriorities; use App\Entity\DataEntry; use App\Entity\User; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpKernel\Event\ViewEvent; use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Component\Security\Core\Security; class DataEntryAnonymizationListener implements EventSubscriberInterface { private $security; public function __construct(Security $security) { $this->security = $security; } public static function getSubscribedEvents() { return [ KernelEvents::VIEW => ['anonymizeSensitiveData', EventPriorities::PRE_SERIALIZE], ]; } public function anonymizeSensitiveData(ViewEvent $event) { $user = $this->security->getUser(); if (!$user instanceof User || !$user->hasRole('ROLE_ADMIN')) { return; } $data = $event->getControllerResult(); $anonymizeField = function(DataEntry $entry) { // 姓名匿名化:保留首字符,其余替换为* $originalName = $entry->getName(); if ($originalName) { $entry->setName(substr($originalName, 0, 1) . str_repeat('*', strlen($originalName) - 1)); } }; if (is_iterable($data)) { foreach ($data as $entry) { if ($entry instanceof DataEntry) { $anonymizeField($entry); } } } elseif ($data instanceof DataEntry) { $anonymizeField($data); } } }
四、权限控制补充
在DataEntry的ApiResource注解中添加基础权限验证,作为双重保障:
/** * @ApiResource( * normalizationContext={"groups"={"data:read"}}, * denormalizationContext={"groups"={"data:write"}}, * accessControl="is_granted('ROLE_USER')", * accessControlMessage="需登录后访问" * ) */
替代思路
- 自定义Data Provider:重写API-Platform的Doctrine ORM Data Provider,在数据获取环节统一处理过滤与匿名化逻辑,适合复杂场景。
- 序列化组动态切换:为管理员单独设置序列化组,移除敏感字段的组标记,仅返回非敏感字段,无需修改实体数据。
内容的提问来源于stack exchange,提问作者user19733098
相关产品推荐
相关产品推荐

