【问题标题】:Dynamically adding roles to a user为用户动态添加角色
【发布时间】:2012-02-11 08:20:35
【问题描述】:

我们正在使用 Symfony2 的角色功能来限制用户访问我们应用程序的某些部分。用户可以购买年度订阅,我们的每个User 实体都有许多Subscription 实体,它们都有开始日期和结束时间。

现在,有没有一种方法可以根据用户是否拥有“活动”订阅来动态地向他们添加角色?在 Rails 中,我只是让模型处理它是否具有必要的权限,但我知道根据设计 symfony2 实体不应该有权访问 Doctrine。

我知道您可以从实体实例中访问实体的关联,但这会通过所有用户的订阅对象,这对我来说似乎不必要的麻烦。

【问题讨论】:

    标签: symfony user-roles


    【解决方案1】:

    我认为你最好设置一个自定义选民和属性。

    /**
     * @Route("/whatever/")
     * @Template
     * @Secure("SUBSCRIPTION_X")
     */
    public function viewAction()
    {
        // etc...
    }
    

    SUBSCRIPTION_X 角色(又名属性)需要由自定义选民类处理。

    class SubscriptionVoter implements VoterInterface
    {
        private $em;
    
        public function __construct($em)
        {
            $this->em = $em;
        }
    
        public function supportsAttribute($attribute)
        {
            return 0 === strpos($attribute, 'SUBSCRIPTION_');
        }
    
        public function supportsClass($class)
        {
            return true;
        }
    
        public function vote(TokenInterface $token, $object, array $attributes)
        {
            // run your query and return either...
            //  * VoterInterface::ACCESS_GRANTED
            //  * VoterInterface::ACCESS_ABSTAIN
            //  * VoterInterface::ACCESS_DENIED
        }
    }
    

    您需要配置和标记您的选民:

    services:
        subscription_voter:
            class: SubscriptionVoter
            public: false
            arguments: [ @doctrine.orm.entity_manager ]
            tags:
                - { name: security.voter }
    

    【讨论】:

    • @webda2l 我不明白你的问题
    • 对不起.. 我会尽量让你更容易理解。引发查询的 Voter 类仅由用户调用一次还是在每次页面加载时调用?在最后一种情况下,为了避免重复查询,最好的方法是在投票功能中使用 session 进行管理,不是吗?
    • 您可以添加缓存机制或根据需要进行优化。
    • 非常感谢!我以前没有调查过也没有听说过选民。我总是发现 sf2 更有趣的功能几乎没有文档记录,而且非常基本的东西太多了。
    【解决方案2】:

    假设您的用户实体中有正确的“订阅”关系。

    你可以试试这样的:

    public function getRoles()
    {
        $todayDate = new DateTime();
        $activesSubscriptions = $this->subscriptions->filter(function($entity) use ($todayDate) {
            return (($todayDate >= $entity->dateBegin()) && ($todayDate < $entity->dateEnd()));
        });
    
        if (!isEmpty($activesSubscriptions)) {
            return array('ROLE_OK');
        }
    
        return array('ROLE_KO');
    }
    

    可以通过以下方式更改角色:

    $sc = $this->get('security.context')
    $user = $sc->getToken()->getUser();
    $user->setRole('ROLE_NEW');
    // Assuming that "main" is your firewall name :
    $token = new \Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken($user, null, 'main', $user->getRoles());
    $sc->setToken($token);
    

    但在页面更改后,会调用提供者的 refreshUser 函数,有时,就像 EntityUserProvider 的情况一样,角色会被查询覆盖。 您需要一个自定义提供程序来避免这种情况。

    【讨论】:

    • 我知道。但正如我所说,我宁愿不遍历所有订阅对象,而是让 MySQL 来做“繁重的工作”,因为这必须在每个请求上发生。 (我从未测试过这个,但对我来说,不必要地为一堆物体补水似乎更慢。如果我错了,请纠正我。)
    • 您可以使用 form_login 的 success_handler 选项来使用可以访问 Doctrine 的服务。在其中,在 onAuthenticationSuccess 函数中,您执行获取正确角色的请求。而且,由于每次页面更改都会重新加载角色,因此您使用不会刷新用户的自定义用户提供程序。
    • 但是如果角色在会话期间发生变化,用户是否不必注销并重新登录?我不想那样。
    • 我已经编辑了我的答案。您可以强制用户在不注销的情况下更改其角色。
    • +1 表示第二个代码 sn-p。被困了很长时间,想知道为什么只是在我的User 实体中添加一个角色没有通过hasRole() 检查。在 Symfony 中角色属性几乎毫无意义,它是事实来源的令牌
    猜你喜欢
    • 1970-01-01
    • 2013-11-11
    • 2014-07-16
    • 1970-01-01
    • 2021-03-22
    • 2022-01-18
    • 2019-08-11
    • 1970-01-01
    • 2015-11-17
    相关资源
    最近更新 更多