通过Symfony 3中的访问令牌对用户进行身份验证


Authenticate users via access token in Symfony 3

问题


我希望通过访问令牌对用户进行身份验证,该令牌作为GET参数提供给第一个请求。

我从未在Symfony中实现过这样的东西,所以我遵循了How to Create a custom Authentication Provider中概述的步骤,但它"不起作用"。不触发AuthenticationProviderInterfaceauthenticate方法。

我尝试过的


因为它主要是大量的配置,我甚至不知道如何调试它。到目前为止,我得出的结论是:只有AccessTokenProvider被构造,没有其他东西。

代码


这些是系统的相关部分:

安全性.yml

security:
    # Snip default (empty) in_memory provider
    firewalls:
        # Snip dev and main (symfony default)
        accesstoken_secured:
            pattern:       ^/admin/
            accesstoken:   true

服务.yml

services:
    accesstoken.security.authentication.provider:
        class: AppBundle'Security'Authentication'Provider'AccessTokenProvider
        arguments:
            - '' # User Provider
            - '%kernel.cache_dir%/security/nonces'
        public: false
    accesstoken.security.authentication.listener:
        class: AppBundle'Security'Firewall'AccessTokenListener
        arguments: ['@security.token_storage', '@security.authentication.manager']
        public: false

AccessTokenFactory

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

AccessTokenProvider

class AccessTokenProvider implements AuthenticationProviderInterface
{
    private $userProvider;
    public function __construct(UserProviderInterface $userProvider)
    {
        $this->userProvider = $userProvider;
    }
    public function authenticate(TokenInterface $token)
    {
        $user = $this->userProvider->loadUserByAccessToken($token->getAttribute('token'));
        if ($this->isTokenValid($token)) {
            $authenticatedToken = new AccessToken(['role_user']);
            $authenticatedToken->setUser($user);
            return $authenticatedToken;
        }
        throw new AuthenticationException('The WSSE authentication failed.');
    }
    protected function isTokenValid(AccessToken $token)
    {
        //TODO: Implement
        return (bool)$token->token;
    }
    public function supports(TokenInterface $token)
    {
        return $token instanceof AccessToken;
    }
}

AccessTokenListener

class AccessTokenListener
{
    protected $tokenStorage;
    protected $authenticationManager;
    /**
     * AccessTokenListener constructor.
     * @param TokenStorageInterface $tokenStorage
     * @param AuthenticationManagerInterface $authenticationManager
     */
    public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager)
    {
        $this->tokenStorage = $tokenStorage;
        $this->authenticationManager = $authenticationManager;
    }
    public function handle(GetResponseEvent $event)
    {
        $request = $event->getRequest();
        $accesstoken = $request->get('accesstoken');
        $token = new AccessToken();
        $token->token = $accesstoken;
        try {
            $authToken = $this->authenticationManager->authenticate($token);
            $this->tokenStorage->setToken($authToken);
            return;
        } catch (AuthenticationException $failed) {
            // ... you might log something here
        }
        // By default deny authorization
        $response = new Response();
        $response->setStatusCode(Response::HTTP_FORBIDDEN);
        $event->setResponse($response);
    }
}

AccessToken

class AccessToken extends AbstractToken
{
    public $token;
    /**
     * AccessToken constructor.
     * @param array $roles
     */
    public function __construct(array $roles = array())
    {
        parent::__construct($roles);
        // If the user has roles, consider it authenticated
        $this->setAuthenticated(count($roles) > 0);
    }
    /**
     * Returns the user credentials.
     *
     * @return mixed The user credentials
     */
    public function getCredentials()
    {
        return '';
    }
}

我最终尝试用另一种方式实现它,使用教程How to Create a Custom Authentication System with Guard。

这使用了Symfony的新Guard系统。它实际上非常容易设置!