Symfony2用户角色在Listener和UserProvider之间消失

时间:2013-07-18 09:29:27

标签: security symfony symfony-2.1

我在使用Symfony2的安全组件时遇到了麻烦。

首先,在身份验证过程中涉及多个对象:

  1. SecurityFactoryInterface
  2. AbstractAuthenticationListener
  3. AuthenticationProviderInterface
  4. 自定义标记(继承自AbstractToken)
  5. UserProviderInterface
  6. 流量

    如果我理解的话,安全工厂的目标是配置自定义身份验证。

    监听器是身份验证的指挥。通过其attemptAuthentication方法,它捕获表单提交(包含用户的凭据)并尝试对用户进行身份验证。在此方法中,监听器将创建AbstractToken,然后将令牌传递给authenticate的{​​{1}}方法。

    之后,AuthenticationProvider调用AuthenticationProvider从Web服务或数据库等中检索用户数据......

    UserProvider完成其魔法后,会将一个User对象返回UserProvider

    然后,AuthenticationProvider会创建一个新的令牌,该令牌由AuthenticationProvider检索到的用户填充,然后将其重新调整为UserProvider

    获得新令牌后,Listener会做一些未知的魔法(我认为它会将令牌设置为安全上下文,但我不确定)。

    问题

    在每个步骤中,我都做了一个用户对象的var_dump。除了“最后的步骤”之外,每个步骤都设置了角色:Listener

    ListenerListener检索经过身份验证的令牌时,用户的角色为空。我无法弄清楚为什么......

    SecurityFactory

    UserProvider

    监听

    class CompanyFactory implements SecurityFactoryInterface
    {
        public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint)
        {
            $providerId = 'security.authentication.provider.company.'.$id;
            $container
                ->setDefinition($providerId, new DefinitionDecorator('company.security.authentication.provider'))
                ->replaceArgument(0, new Reference($userProvider));
    
            $listenerId = 'security.authentication.listener.company.'.$id;
            $listener = $container->setDefinition($listenerId, new DefinitionDecorator('company.security.authentication.listener'));
    
            return array($providerId, $listenerId, $defaultEntryPoint);
        }
    
        public function getPosition()
        {
            return 'pre_auth';
        }
    
        public function getKey()
        {
            return 'company';
        }
    
        public function addConfiguration(NodeDefinition $node)
        {
        }
    }
    

    的AuthenticationProvider

    class CompanyListener extends AbstractAuthenticationListener
    {
        // Custructor stuff removed
    
        protected function requiresAuthentication(Request $request)
        {
            if ($this->options['post_only'] && !$request->isMethod('post'))
            {
                return false;
            }
    
            return parent::requiresAuthentication($request);
        }
    
        protected function attemptAuthentication(Request $request)
        {
            $username = trim($request->get($this->options['username_parameter'], null, true));
            $password = $request->get($this->options['password_parameter'], null, true);
            $ip       = $request->getClientIp();
    
            $request->getSession()->set(SecurityContextInterface::LAST_USERNAME, $username);
    
            $authToken = $this->authenticationManager->authenticate(new CompanyUserToken($username, $password, $ip));
    
            return $authToken;
        }
    }
    

    自定义标记

    class CompanyProvider implements AuthenticationProviderInterface
    {
        private $userProvider;
    
        public function __construct(UserProviderInterface $userProvider)
        {
            $this->userProvider = $userProvider;
        }
    
        public function authenticate(TokenInterface $token)
        {
            $user = $this->userProvider->loadUserByUsernamePassword($token->user, $token->getPassword(), $token->getIp());
    
            $authenticatedToken = new CompanyUserToken($user->getUsername(), $user->getPassword(), $user->getIp(), $user->getRoles());
            $authenticatedToken->setUser($user);
    
            return $authenticatedToken;
        }
    
        public function supports(TokenInterface $token)
        {
            return $token instanceof CompanyUserToken;
        }
    }
    

    用户提供商

    class CompanyUserToken extends AbstractToken
    {
        private $password;
        private $ip;
    
        public function __construct($username, $password, $ip, array $roles = array())
        {
            parent::__construct($roles);
    
            $this->password = $password;
            $this->user = $username;
            $this->ip = $ip;
    
            // If the user has roles, consider it authenticated
            $this->setAuthenticated(count($roles) > 0);
        }
    
        public function getCredentials()
        {
            return '';
        }
    
        public function getPassword()
        {
            return $this->password;
        }
    
        public function getIp()
        {
            return $this->ip;
        }
    }
    

    service.yml

    class CompanyUserProvider implements UserProviderInterface
    {
        private $documentManager;
    
        public function __construct($doctrineMongoDB)
        {
            $this->doctrineMongoDB = $doctrineMongoDB;
        }
    
        public function loadUserByUsername($username)
        {
            // Not used but needed by the interface
        }
    
        public function loadUserByUsernamePassword($username, $password, $ip)
        {
            // Does the magic, retrieve user datas from DB.
    
            return $user;
        }
    
        public function refreshUser(UserInterface $user)
        {
            // Does nearly the same thing that the above method
            return $refreshedUser;
        }
    
        public function supportsClass($class)
        {
            return $class === 'Company\UserBundle\Document\User';
        }
    }
    

    security.yml

    parameters: 
        security.authentication.handler.class: Company\UserBundle\Security\Authentication\Handler\CompnayAuthenticationHandler
        company_user_provider.class: Company\UserBundle\Security\User\CompanyUserProvider
    
    services:
        security.authentication.handler:
            class: %security.authentication.handler.class%
            public: false
            arguments: [@router, @security.http_utils]
    
        company.security.authentication.provider:
            class: Company\UserBundle\Security\Authentication\Provider\CompanyProvider
            arguments: [@company_user_provider]
    
        company.security.authentication.listener:
            class: Company\UserBundle\Security\Firewall\CompanyListener
            arguments: [@security.context, @security.authentication.manager, @security.authentication.session_strategy, @security.http_utils, "company", @security.authentication.handler, @security.authentication.handler, {}, @logger, @event_dispatcher, @user.service.captcha]
    
        company_user_provider:
            class: %company_user_provider.class%
            arguments: [@doctrine_mongodb]
    
        user.service.captcha:
            class: Company\UserBundle\Services\CaptchaService
            arguments: [@form.factory]
    

    更新

    以下是一些解释我问题的jms_security_extra: secure_all_services: false expressions: true security: encoders: Company\UserBundle\Document\User: plaintext role_hierarchy: ROLE_VIP_USER: ROLE_USER ROLE_ADMIN: [ROLE_USER, ROLE_VIP_USER] providers: webservice: id: company_user_provider firewalls: company_secured: pattern: ^/ company: true anonymous: true form_login: login_path: login check_path: login_check post_only: true use_referer: false success_handler: security.authentication.handler failure_handler: security.authentication.handler logout: path: /logout target: login access_control: - { path: ^/admin, role: ROLE_ADMIN }

    UserProvider

    var_dumps

    var_dump($user);

    的AuthenticationProvider

    object(Company\UserBundle\Document\User)[142] protected 'username' => string 'Supacoco' (length=13) protected 'roles' => array (size=1) 0 => string 'ROLE_ADMIN' (length=10)

    var_dump($authenticatedToken->getUser());

    监听

    object(Company\UserBundle\Document\User)[142] protected 'username' => string 'Supacoco' (length=13) protected 'roles' => array (size=1) 0 => string 'ROLE_ADMIN' (length=10)

    var_dump($authToken->getUser());

1 个答案:

答案 0 :(得分:0)

经过深入挖掘后,我发现“AuthenticationProviderManager”在验证令牌后默认调用用户的方法“EraseCredentials”。

下面的代码解释了我的用户登录后失去其角色的原因。

public function eraseCredentials()
{
    $this->roles = array();
}