API Platform:如何在GET集合操作中以Security作为过滤条件?
解决方案:为API Platform集合GET操作实现条目级权限校验
针对你的需求,无需重写整个DataProvider,以下两种方案可以实现集合GET操作的条目级MESSAGE_VIEW权限校验:
方法一:使用PostRead事件监听器过滤结果
该方案在数据读取完成后,对结果集进行过滤,移除用户无权限的条目,无需修改查询逻辑。
步骤1:创建事件订阅器
namespace App\EventListener; use ApiPlatform\Core\EventListener\EventPriorities; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpKernel\Event\ViewEvent; use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Component\Security\Core\Security; class MessageCollectionFilterSubscriber implements EventSubscriberInterface { private $security; public function __construct(Security $security) { $this->security = $security; } public static function getSubscribedEvents() { return [ KernelEvents::VIEW => ['filterMessageCollection', EventPriorities::POST_READ], ]; } public function filterMessageCollection(ViewEvent $event) { $request = $event->getRequest(); // 仅针对Message集合的GET操作生效 if ('api_messages_get_collection' !== $request->get('_route')) { return; } $data = $event->getControllerResult(); if (!is_iterable($data)) { return; } $filteredItems = []; foreach ($data as $item) { if ($this->security->isGranted('MESSAGE_VIEW', $item)) { $filteredItems[] = $item; } } $event->setControllerResult($filteredItems); } }
步骤2:注册订阅器
在config/services.yaml中添加配置:
App\EventListener\MessageCollectionFilterSubscriber: tags: - { name: kernel.event_subscriber }
方法二:自定义Doctrine ORM过滤器(推荐)
该方案直接在数据库查询阶段过滤无权限条目,性能更优,避免读取不必要的数据。
步骤1:创建自定义过滤器
namespace App\Filter; use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\AbstractFilter; use ApiPlatform\Core\Bridge\Doctrine\Orm\Util\QueryNameGeneratorInterface; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Security\Core\Security; class MessageViewPermissionFilter extends AbstractFilter { private $security; public function __construct(Security $security) { $this->security = $security; } protected function filterProperty(string $property, $value, QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, string $operationName = null) { // 仅对集合GET操作生效 if ('get_collection' !== $operationName) { return; } $currentUser = $this->security->getUser(); if (!$currentUser) { // 未登录用户直接返回空结果 $queryBuilder->andWhere('1 = 0'); return; } // 替换为你的实际权限判断逻辑(比如Message与User的关联规则) $rootAlias = $queryBuilder->getRootAliases()[0]; $queryBuilder->andWhere(sprintf('%s.owner = :currentUser', $rootAlias)) ->setParameter('currentUser', $currentUser); } public function getDescription(string $resourceClass): array { // 无需暴露公共过滤参数,返回空数组 return []; } }
步骤2:配置实体与过滤器
更新Message实体的注解,引入过滤器:
/** * @ApiResource( * collectionOperations={ * "post"={ * "security_post_denormalize"="is_granted('MESSAGE_CREATE', object)" * }, * "get"={ * "security"="is_authenticated()", * "filters"={"message_view_permission"} * } * }, * itemOperations={ * "get"={"security"="is_granted('MESSAGE_VIEW', object)"}, * "patch"={"security"="is_granted('MESSAGE_EDIT', object)"}, * "delete"={"security"="is_granted('MESSAGE_DELETE', object)"}, * "put"={"security"="is_granted('MESSAGE_REPLACE', object)"} * } * ) * @ApiFilter(MessageViewPermissionFilter::class, properties={}) */ class Message { // ... 实体字段与逻辑 }
步骤3:注册过滤器
在config/services.yaml中添加配置:
App\Filter\MessageViewPermissionFilter: tags: - { name: api_platform.filter, id: 'message_view_permission' }
内容的提问来源于stack exchange,提问作者Yann Chabot
相关产品推荐
相关产品推荐

