【问题标题】:Symfony2: Call Voter from another VoterSymfony2:从另一个选民那里呼叫选民
【发布时间】:2017-02-24 02:41:38
【问题描述】:

我正在使用 Voters 来限制对 REST API 中实体的访问。

步骤 1

考虑限制用户访问博客文章的投票者:

class BlogPostVoter extends Voter
{
    public function __construct(AccessDecisionManagerInterface $decisionManager)
    {
        $this->decisionManager = $decisionManager;
    }

    /**
     * Determines if the attribute and subject are supported by this voter.
     *
     * @param string $attribute An attribute
     * @param int $subject The subject to secure, e.g. an object the user wants to access or any other PHP type
     *
     * @return bool True if the attribute and subject are supported, false otherwise
     */
    protected function supports($attribute, $subject)
    {
        if (!in_array($attribute, $this->allowedAttributes)) {
            return false;
        }
        if (!$subject instanceof BlogPost) {
            return false;
        }

        return true;
    }

    /**
     * Perform a single access check operation on a given attribute, subject and token.
     *
     * @param string $attribute
     * @param mixed $subject
     * @param TokenInterface $token
     * @return bool
     * @throws \Exception
     */
    protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
    {
        return $this->canUserAccess($attribute, $subject, $token);
    }

    public function canUserAccess($attribute, $subject, TokenInterface $token) {
        if ($this->decisionManager->decide($token, array('ROLE_SUPPORT', 'ROLE_ADMIN'))) {
            return true;
        } 

        //other logic here omitted ...

        return false;
    }
}

您可以看到有一个公共函数canUserAccess 来确定是否允许用户查看博客帖子。这一切都很好。

第二步

现在我有另一个选民检查其他内容,但也需要检查博客帖子的相同逻辑。我的想法是:

  • 添加新选民
  • 执行一些其他检查
  • 但也执行此 BlogPost 检查

所以我想我会像这样将 BlogPostVoter 注入我的其他选民:

class SomeOtherVoter extends Voter
{
    public function __construct(BlogPostVoter $blogPostVoter)
    {
        $this->decisionManager = $decisionManager;
    }

    ...

    protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
    {
        //other logic

        if ($this->blogPostVoter->canUserAccess($attribute, $subject, $token))    {
           return true;
        }

        return false;
    }
}

问题

当我这样做时,我得到以下错误,同时使用 setter 和构造函数注入:

检测到服务“security.access.decision_manager”的循环引用,路径:“security.access.decision_manager”

我看不出 security.access.decision_manager 应该在哪里依赖于 Voter 实现。所以我没有看到循环引用在哪里。

我还有其他方法可以从 VoterB 呼叫 VoterA 吗?

【问题讨论】:

  • 您可以查看安全编译器(或者可能只是缓存中的结果容器),以了解决策管理器最终如何依赖选民。基本上,只要实例化决策管理器,就会注入所有标记的选民。 Doctrine 实体管理器和它的侦听器也会发生同样的事情。在我的脑海中,我可能会建议将 canUserAccess 功能移动到一个特征中,并在两个选民中使用该特征。取决于“附加功能”的作用。
  • 另一个技巧是使用setter注入来注入决策管理器。从来没有真正喜欢这样做,但它似乎解决了循环引用问题。
  • 不是答案,但是否可以用类继承来解决?
  • 感谢@Cerad!我试过setter注入,同样的问题。废话。现在,我已经将逻辑从投票者转移到一个单独的服务,每个投票者都可以安全地注入。我有点想将安全逻辑留给选民,但这似乎工作正常并且不是很脏。不过,我不熟悉这些特征......
  • @CameronHurd 是的,我敢打赌这也是解决问题的好方法。但我选择了一项服务。

标签: symfony symfony2-voter


【解决方案1】:

要从VoterTwo 引用VoterOne,您可以将AuthorizationCheckerInterface 注入VoterTwo,然后调用->isGranted('ONE')。其中 ONE 是 VoterOne 支持的属性。

喜欢:

class VoterTwo extends Voter
{
    private $authorizationChecker;

    public function __construct(AuthorizationCheckerInterface $authorizationChecker)
    {
        $this->authorizationChecker = $authorizationChecker;
    }

    protected function supports($attribute, $subject)
    {
        return in_array($attribute, ['TWO']);
    }

    protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
    {
        return $this->authorizationChecker->isGranted('ONE', $subject);
    }
}

在此示例中,VoterTwo 只是将请求重定向到VoterOne(或支持属性 ONE 的选民)。然后可以通过附加条件来扩展。

【讨论】:

  • OP 应该阅读文档。那里很好地涵盖了建议的用法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-02-15
  • 2017-02-21
  • 1970-01-01
  • 2013-10-20
  • 2015-02-01
  • 1970-01-01
  • 2013-03-11
相关资源
最近更新 更多