Неправильные учетные данные поставщика пользовательской аутентификации Symfony2

Я реализую собственный поставщик аутентификации для использования внешнего API, примерно следуя поваренной книге на веб-сайте symfony. Он работает почти все правильно, слушатель правильно слушает форму входа, затем вызывает функцию аутентификации, которая возвращает аутентифицированный токен, проблема в том, что даже если я устанавливаю аутентифицированный токен в securityContextInterface, система возвращается на страницу входа с неправильным реквизиты для входа. Под кодом, который я использовал, что это может быть?

безопасность.yml

firewalls:
    dev:
        pattern:  ^/(_(profiler|wdt)|css|images|js)/
        security: false

    login:
        pattern:  ^/app/login$
        security: false

    api_secured:
        provider:   in_memory
        pattern:    ^/app
        form_login:
            login_path:  /app/login
            check_path:  /app/login_check
        logout:
            path:   /app/logout
            target: /
        api:   true

services.yml

api.security.authentication.provider:
    class:  Manuel\Myapp\MyAppBundle\Security\Authentication\Provider\ApiProvider
    arguments: ['', %kernel.cache_dir%/security/nonces]
api.security.authentication.listener:
    class:  Manuel\Myapp\MyAppBundle\Security\Firewall\ApiListener
    arguments: [@security.context, @security.authentication.manager, %api.url%]

ApiFactory.php

namespace Manuel\Myapp\MyAppBundle\DependencyInjection\Security\Factory;

use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SecurityFactoryInterface;

class ApiFactory implements SecurityFactoryInterface
{
    public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint)
    {
        $providerId = 'security.authentication.provider.api.'.$id;
        $container
            ->setDefinition($providerId, new DefinitionDecorator('api.security.authentication.provider'))
            ->replaceArgument(0, new Reference($userProvider))
        ;

        $listenerId = 'security.authentication.listener.api.'.$id;
        $listener = $container->setDefinition($listenerId, new DefinitionDecorator('api.security.authentication.listener'));

        return array($providerId, $listenerId, $defaultEntryPoint);
    }

    public function getPosition()
    {
        return 'pre_auth';
    }

    public function getKey()
    {
        return 'api';
    }

    public function addConfiguration(NodeDefinition $node)
    {
    }
}

ApiListener.php

namespace Manuel\Myapp\MyAppBundle\Security\Firewall;

use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\Security\Http\Firewall\ListenerInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\SecurityContextInterface;
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
use Manuel\Myapp\MyAppBundle\Security\Authentication\Token\ApiUserToken;
use Httpful\Request;

class ApiListener implements ListenerInterface {
    protected $securityContext;
    protected $authenticationManager;
    protected $container;

    public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager, $api)
    {
        $this->securityContext = $securityContext;
        $this->authenticationManager = $authenticationManager;
        //Prendo l'url delle api
        //Viene passato da services.yml alla classe
        $this->api = $api;
    }

    public function handle(GetResponseEvent $event)
    {
        $request = $event->getRequest();
        $data = $request->request->all();

        //Esiste username e password ?
        if(!array_key_exists('_username', $data) || !array_key_exists('_password', $data)) {
            //Ritorna alla pagina di login con bad credentials
            $this->securityContext->setToken(null);
            return;
        }

        //Autentico in remoto
        $username = $data['_username'];
        $password = $data['_password'];

        $response = Request::post($this->api."/token/new.json")
                    ->body(array(
                        'username'=> $username, 
                        'password'=> $password))
                    ->expectsJson()
                    ->sendsForm()
                    ->send(); 
        $decode = json_decode($response);

        //Se esiste allora vado avanti se no muoio
        if(!$decode->success) {
            $this->securityContext->setToken(null);
            return;
        }

        $token = new ApiUserToken();
        $token->setUser(''.$decode->user);
        $token->token = $decode->token;

        try {
            $authToken = $this->authenticationManager->authenticate($token);
            $this->securityContext->setToken($authToken);

        } catch (AuthenticationException $failed) {
            // ... si potrebbe loggare qualcosa in questo punto
            // Per negare l'autenticazione, pulire il token. L'utente sarà rinviato alla pagina di login.
            $this->securityContext->setToken(null);
            return;

            // Negare l'autenticazione con una risposta HTTP '403 Forbidden'
            //$response = new Response();
            //$response->setStatusCode(403);
            //$event->setResponse($response);

        }
    }
}

Если я напишу:

$authToken = $this->authenticationManager->authenticate($token);
var_dump($authToken); die();
$this->securityContext->setToken($authToken);

Результаты:

object(Manuel\Myapp\MyAppBundle\Security\Authentication\Token\ApiUserToken)#4780 (5) {["user":"Symfony\Component\Security\Core\Authentication\Token\AbstractToken":private]=> object(Symfony\Component\Security\Core\User\User)#4782 (7) { ["username":"Symfony\Component\Security\Core\User\User":private]=> string(4) "user" ["password":"Symfony\Component\Security\Core\User\User":private]=> string(15) "10dmao!?postino" ["enabled":"Symfony\Component\Security\Core\User\User":private]=> bool(true) ["accountNonExpired":"Symfony\Component\Security\Core\User\User":private]=> bool(true) ["credentialsNonExpired":"Symfony\Component\Security\Core\User\User":private]=> bool(true) ["accountNonLocked":"Symfony\Component\Security\Core\User\User":private]=> bool(true) ["roles":"Symfony\Component\Security\Core\User\User":private]=> array(1) { [0]=> string(9) "ROLE_USER" } } ["roles":"Symfony\Component\Security\Core\Authentication\Token\AbstractToken":private]=> array(1) { [0]=> object(Symfony\Component\Security\Core\Role\Role)#4779 (1) { ["role":"Symfony\Component\Security\Core\Role\Role":private]=> string(9) "ROLE_USER" } } ["authenticated":"Symfony\Component\Security\Core\Authentication\Token\AbstractToken":private]=> bool(true) ["attributes":"Symfony\Component\Security\Core\Authentication\Token\AbstractToken":private]=> array(0) { } }

Так что это правильно.

Апиусертокен.php

namespace Manuel\Myapp\MyAppBundle\Security\Authentication\Token;

use Symfony\Component\Security\Core\Authentication\Token\AbstractToken;

class ApiUserToken extends AbstractToken
{
    public $token;

    public function __construct(array $roles = array())
    {
        parent::__construct($roles);

        // If the user has roles, consider it authenticated
        $this->setAuthenticated(true);
    }

    public function getCredentials()
    {
        return '';
    }
}

APIProvider.php

namespace Manuel\Myapp\MyAppBundle\Security\Authentication\Provider;

use Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\NonceExpiredException;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Manuel\Myapp\MyAppBundle\Security\Authentication\Token\ApiUserToken;

class ApiProvider implements AuthenticationProviderInterface
{
    private $userProvider;
    private $cacheDir;

    public function __construct(UserProviderInterface $userProvider, $cacheDir)
    {
        $this->userProvider = $userProvider;
        $this->cacheDir     = $cacheDir;
    }

    public function authenticate(TokenInterface $token)
    {

        //Devo aggiungere utente
        $user = $this->userProvider->loadUserByUsername("user");

        if ($user) {
            $authenticatedToken = new ApiUserToken($user->getRoles());
            $authenticatedToken->setUser($user);

            return $authenticatedToken;
        }

        throw new AuthenticationException('The API authentication failed.');
    }

    public function supports(TokenInterface $token) {
        return $token instanceof ApiUserToken;
    }
}

person originof    schedule 26.03.2013    source источник


Ответы (1)


Я решил изменить security.yml

firewalls:
        dev:
            pattern:  ^/(_(profiler|wdt)|css|images|js)/
            security: false

        login:
            pattern:  ^/app/login$
            security: false

        secured_area:
            pattern:    ^/app
            api: true
            logout:
                path:   /app/logout
                target: /

и ApiListener.php

public function handle(GetResponseEvent $event) {

        if( $this->securityContext->getToken() ){
            return;
        }

Поскольку на каждом URL-адресе под брандмауэром (app/*) symfony вызывает метод обработки моего слушателя, если пользователь уже зарегистрирован, токен безопасности уже установлен, и я возвращаюсь

и функция check_login

public function securityCheckAction() {
        // The security layer will NOT intercept this request
        return $this->redirect($this->generateUrl('manuel_myapp_index_after_login'));

check_login — это действие формы входа, действие check_login находится под брандмауэром, поэтому метод дескриптора моего листенера будет вызываться в первый раз, если учетные данные верны (используя мой внешний API). Я заставил symfony использовать пользователя in_memory для входа в систему, а затем будет выполнено действие check_login. Затем, когда пользователь посещает другую страницу под брандмауэром, метод дескриптора будет отозван, но токен аутентификации уже установлен, поэтому метод дескриптора вернется, и все будет работать.

Вход через внешний API теперь работает!

person originof    schedule 05.04.2013