You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在事件订阅器中获取投‘granted’票的Voter?相关实现咨询

如何在事件订阅器中获取投“granted”票的Voter

嘿,我来帮你搞定这个需求!Symfony本身并没有直接提供一个现成的服务来获取所有Voter的投票结果,但我们可以通过监听投票相关事件来完美实现你的场景——完全不用在Voter内部处理消息或分发事件,正好符合你想规避那些方案的要求。

实现思路

我们分两步走:

  1. 监听每个Voter的投票事件,记录下每个Voter的投票结果(是允许还是拒绝);
  2. 在整个授权流程结束后,检查最终的授权结果,再结合之前记录的投票情况,判断是否符合「主Voter拒绝、管理员Voter允许」的场景,然后触发提示消息。

1. 创建投票结果存储服务

首先我们需要一个临时存储当前请求中所有Voter投票结果的服务,因为每个请求是独立的,所以这个服务要设置为请求作用域(保证每个请求的记录互不干扰):

// src/Security/VoteResultStorage.php
namespace App\Security;

use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;

class VoteResultStorage
{
    private array $votes = [];

    // 记录单个Voter的投票结果
    public function recordVote(VoterInterface $voter, int $vote): void
    {
        $this->votes[get_class($voter)] = $vote;
    }

    // 获取所有Voter的投票记录
    public function getVotes(): array
    {
        return $this->votes;
    }

    // 请求结束后重置记录,避免影响下一个请求
    public function reset(): void
    {
        $this->votes = [];
    }
}

然后在services.yaml里配置这个服务为请求作用域:

# config/services.yaml
services:
    App\Security\VoteResultStorage:
        scope: request
        autowire: true
        autoconfigure: true

2. 监听VoteEvent记录投票结果

Symfony的VoteEvent会在每个Voter完成投票后立即触发,我们可以创建一个事件订阅器来监听这个事件,把每个Voter的投票结果存入刚才的存储服务:

// src/EventSubscriber/VoteRecordingSubscriber.php
namespace App\EventSubscriber;

use App\Security\VoteResultStorage;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Security\Core\Event\VoteEvent;

class VoteRecordingSubscriber implements EventSubscriberInterface
{
    public function __construct(private VoteResultStorage $voteResultStorage)
    {
    }

    // 声明要监听的事件
    public static function getSubscribedEvents(): array
    {
        return [
            VoteEvent::class => 'onVote',
        ];
    }

    // 处理VoteEvent,记录投票结果
    public function onVote(VoteEvent $event): void
    {
        $this->voteResultStorage->recordVote(
            $event->getVoter(),
            $event->getVote()
        );
    }
}

3. 监听AuthorizationCheckEvent判断业务场景

接下来监听AuthorizationCheckEvent——这个事件会在整个授权流程完全结束后触发。在这里我们可以检查最终的授权结果,再结合存储的投票记录,判断是否符合你的业务需求:

// src/EventSubscriber/AuthorizationResultSubscriber.php
namespace App\EventSubscriber;

use App\Security\AdminVoter; // 替换成你实际的管理员Voter类
use App\Security\MainVoter; // 替换成你实际的主Voter类
use App\Security\VoteResultStorage;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface;
use Symfony\Component\Security\Core\Event\AuthorizationCheckEvent;
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;

class AuthorizationResultSubscriber implements EventSubscriberInterface
{
    public function __construct(
        private VoteResultStorage $voteResultStorage,
        private FlashBagInterface $flashBag
    ) {
    }

    public static function getSubscribedEvents(): array
    {
        return [
            AuthorizationCheckEvent::class => 'onAuthorizationCheck',
        ];
    }

    public function onAuthorizationCheck(AuthorizationCheckEvent $event): void
    {
        // 只有当最终授权结果是允许时,才需要检查投票细节
        if (!$event->getAuthorizationDecision()) {
            return;
        }

        $votes = $this->voteResultStorage->getVotes();
        // 获取主Voter和管理员Voter的投票结果
        $mainVoterVote = $votes[MainVoter::class] ?? null;
        $adminVoterVote = $votes[AdminVoter::class] ?? null;

        // 判断是否符合你的场景:主Voter拒绝,管理员Voter允许
        if (
            $mainVoterVote === VoterInterface::ACCESS_DENIED
            && $adminVoterVote === VoterInterface::ACCESS_GRANTED
        ) {
            // 添加你需要的提示消息(这里用flash消息举例,你可以换成其他方式)
            $this->flashBag->add('info', '您通过管理员权限获得了访问权限');
        }

        // 重置投票记录,避免影响下一个请求
        $this->voteResultStorage->reset();
    }
}

关键注意点

  • 确保VoteResultStorage是请求作用域,这样每个请求的投票记录都是独立的,不会互相干扰;
  • 替换代码中的MainVoter和AdminVoter为你项目中实际的Voter类名;
  • 所有逻辑都集中在事件订阅器中,完全不需要修改Voter的代码,完美规避了你不想在Voter内处理的问题。

内容的提问来源于stack exchange,提问作者Tomasz Gąsior

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 05:03:45