Symfony Security с Guard: Пользователь вошел в систему и сразу же вышел из системы

Я хочу аутентифицировать пользователей через веб-сервис. Я создал брандмауэр, использую Guard и настраиваемого пользователя.

Вот моя проблема: когда я вхожу в систему, пользователь аутентифицируется и сразу выходит из системы после первого перенаправления (web_profiler.intercept_redirects в true помогает мне определить это).

Я не знаю, что в моем коде или моей конфигурации может отвечать за это. Я создал аутентификатор, расширяющий AbstractGuardAuthenticator. В методе checkCredentials() я вызываю веб-службу, и ответ успешен.

В методе onAuthenticationSuccess() я устанавливаю токен, предоставленный веб-сервисом в сеансе, и перенаправляю на маршрут, по которому пользователь был до активации аутентификации. Когда перенаправление выполнено, пользователь больше не входит в систему.

Я изучил еще один момент: мой брандмауэр настроен для работы с определенными хостами благодаря ключу host в конфигурации моего брандмауэра:

customer_account:
    pattern: ^(?!/admin[/]*).*$ # all but /admin
    host: "%extranet_domain_one%|%extranet_domain_two%"
    anonymous: ~
    provider: customer_account
    form_login:
        login_path: customer_account_login
        check_path: customer_account_login
    guard:
       authenticators:
           - app.customer_web_service_authenticator
    logout:
       path: customer_account_logout
       target: /

Вот мой access_controlconfig:

access_control:
        - { host: "%extranet_domain_one%|%extranet_domain_two%", path: ^/connexion, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { host: "%extranet_domain_one%|%extranet_domain_two%", path: ^/mot-de-passe, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { host: "%extranet_domain_one%|%extranet_domain_two%", path: ^/, roles: ROLE_CUSTOMER_ACCOUNT }

В моем провайдере метод loadUserByUsername() возвращает нового пользователя с логином и токеном, хранящимся в сеансе, если он существует.

Любая идея?

Дополнительная информация:

CustomerWebServiceAuthenticator:

<?php

namespace AppBundle\Security;

use AppBundle\Security\User\CustomerAccount;
use AppBundle\Security\User\CustomerAccountUserProvider;
use AppBundle\Services\Webservice;
use AppBundle\Services\MultisiteHelper;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Guard\AbstractGuardAuthenticator;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\User\UserProviderInterface;

/**
 * Class CustomerWebServiceAuthenticator
 *
 * @package AppBundle\Security
 */
class CustomerWebServiceAuthenticator extends AbstractGuardAuthenticator
{

    /** @var webservice */
    protected $webservice;

    /** @var MultisiteHelper */
    protected $multisiteHelper;

    /** @var \Symfony\Component\Routing\RouterInterface */
    protected $router;

    /** @var SessionInterface */
    protected $session;

    /**
     * Indique si le site s'exécute en mode développement.
     *
     * @var boolean
     */
    private $dev;

    /**
     * Default message for authentication failure.
     *
     * @var string
     */
    private $failMessage = 'Mot de passe ou e-mail invalide';

    /**
     * WebServiceAuthenticator constructor.
     *
     * @param Webservice $webservice
     * @param MultisiteHelper  $multisiteHelper
     * @param RouterInterface  $router
     * @param SessionInterface $session
     * @param string $kernelEnvironment
     */
    public function __construct(
        Webservice $webservice,
        MultisiteHelper $multisiteHelper,
        RouterInterface $router,
        SessionInterface $session,
        $kernelEnvironment
    ) {
        $this->webservice = $webservice;
        $this->multisiteHelper  = $multisiteHelper;
        $this->router           = $router;
        $this->session          = $session;
        $this->dev              = $kernelEnvironment === 'dev';
    }

    /**
     * {@inheritdoc}
     * @param Request $request
     *
     * @return array|null
     */
    public function getCredentials(Request $request)
    {
        $url = $request->getRequestUri();

        if ($url !== $this->router->generate('customer_account_login') || !$request->isMethod('POST')) {
            return null;
        }

        return [
            'username' => $request->request->get('_username'),
            'password' => $request->request->get('_password'),
        ];
    }

    /**
     * {@inheritdoc}
     * @param mixed                 $credentials
     * @param UserProviderInterface $userProvider
     *
     * @return CustomerAccount|null|UserInterface
     */
    public function getUser($credentials, UserProviderInterface $userProvider)
    {
        if (!$userProvider instanceof CustomerAccountUserProvider) {
            return null;
        }

        try {
            return $userProvider->loadUserByUsername($credentials['username']);
        } catch (UsernameNotFoundException $e) {
            throw new CustomUserMessageAuthenticationException($this->failMessage);
        }
    }

    /**
     * {@inheritdoc}
     * @param mixed         $credentials
     * @param UserInterface $user
     *
     * @return bool
     */
    public function checkCredentials($credentials, UserInterface $user)
    {
        $xml = $this->webservice->loginCustomerAccount([
            'username'         => $credentials['username'],
            'password'         => ($credentials['password']),
        ]);

        if (isset($xml->identification) && isset($xml->identification['customer_id']) && isset($xml->identification['id_token_session'])) {
            /** @var $user CustomerAccount */
            $user
                ->setIdTokenSession((string) $xml->identification['id_token_session'])
                ->setCustomerId((string) $xml->identification['customer_id'])
            ;
            return true;
        }

        throw new CustomUserMessageAuthenticationException($this->failMessage);
    }

    /**
     * {@inheritdoc}
     */
    public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
    {
        /** @var $user CustomerAccount */
        $user = $token->getUser();

        // Stockage en session, c'est ce qui maintient la connexion chez nous
        $this->session->clear();
        $this->session->set('customer_id', $user->getCustomerId());
        $this->session->set('id_token_session_customer', $user->getIdTokenSession());
        $this->session->set('login', $user->getUsername());

        // @todo LDA : adapter
        if ($this->session->has('_security.' . $providerKey . '.target_path')) {
            $url = $this->session->get('_security.' . $providerKey . '.target_path');
        } else {
            $url = $this->router->generate('customer_account_account');
        }

        return new RedirectResponse($url);
    }

    /**
     * {@inheritdoc}
     */
    public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
    {
        $request->getSession()->set(Security::AUTHENTICATION_ERROR, $exception);
        $url = $this->router->generate('customer_account_login');

        return new RedirectResponse($url);
    }

    /**
     * {@inheritdoc}
     */
    public function start(Request $request, AuthenticationException $authException = null)
    {
        $url = $this->router->generate('customer_account_login');

        return new RedirectResponse($url);
    }

    /**
     * {@inheritdoc}
     */
    public function supportsRememberMe()
    {
        return false;
    }
}

person ldaguise    schedule 28.04.2017    source источник
comment
Обновите вопрос с помощью аутентификатора защиты.   -  person gp_sflover    schedule 28.04.2017
comment
@gp_sflover готово. Спасибо   -  person ldaguise    schedule 28.04.2017
comment
Почему вы очищаете сессию внутри onAuthenticationSuccess? У пользователя уже есть действующий токен аутентификации.   -  person gp_sflover    schedule 28.04.2017
comment
В профилировщике Symfony вы можете получить доступ к деталям каждого экземпляра. Взгляните на экземпляр входа в систему (до и после перенаправления), и вы должны увидеть трассировку стека, где, вероятно, сообщается, почему пользователь был аннулирован.   -  person gp_sflover    schedule 28.04.2017


Ответы (2)


Три проблемы заключаются в том, что вы очищаете сеанс в onAuthenticationSuccess. Если вы хотите реализовать свою собственную логику для сгенерированного токена для аутентификации, вы должны реализовать GuardAuthenticatorInterface, как сказано здесь http://symfony.com/doc/current/security/guard_authentication.html#the-guard-authenticator-методы

createAuthenticatedToken (UserInterface $ user, string $ providerKey) Если вы реализуете GuardAuthenticatorInterface вместо расширения класса AbstractGuardAuthenticator, вы должны реализовать этот метод. Он будет вызываться после успешной аутентификации для создания и возврата токена для пользователя, который был указан в качестве первого аргумента.

person ste    schedule 30.04.2017

Извините за задержку: нашла!

Произошла опечатка в isEqualTo() в моем классе CustomerAccount, реализующем Symfony\Component\Security\Core\User\EquatableInterface, поэтому он никогда не может вернуть true.

Спасибо, что потратили время, помогая мне!

person ldaguise    schedule 07.06.2017