1

我(再次)遇到了 Symfony2 的安全组件问题。

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

  1. 安全工厂接口
  2. AbstractAuthenticationListener
  3. 身份验证提供者接口
  4. 自定义令牌(从 AbstractToken 继承)
  5. 用户提供者接口

流动

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

侦听器是身份验证的指挥者。通过它的attemptAuthentication方法,它捕获表单提交(包含用户的凭据)并尝试对用户进行身份验证。在此方法中,Listener 将创建一个AbstractToken然后将令牌传递authenticateAuthenticationProvider.

之后,AuthenticationProvider调用UserProvider从网络服务或数据库等检索用户数据......

一旦UserProvider它发挥了魔力,它就会返回一个 User 对象给AuthenticationProvider.

AuthenticationProvider 然后创建一个新的 Token 填充由 检索到的用户UserProvider,然后将其返回到Listener.

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

问题

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

当从 中Listener检索经过身份验证的令牌时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)
    {
    }
}

听众

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;
    }
}

用户提供者

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';
    }
}

服务.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]

安全.yml

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 }

更新

这里有一些var_dumps解释我的问题:

用户提供者

var_dump($user);

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());

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

1 回答 1

0

经过大量挖掘,我发现“AuthenticationProviderManager”在对令牌进行身份验证后,默认调用用户的方法“EraseCredentials”。

下面的代码解释了为什么我的用户在登录后失去了他/她的角色。

public function eraseCredentials()
{
    $this->roles = array();
}
于 2013-08-02T13:46:39.260 回答