Пользовательский провайдер аутентификации Symfony выходит из системы при перекрытии запросов

Эта проблема была воспроизведена в Symfony 3.3.17 и 3.4.9.

У меня есть собственный провайдер аутентификации, который связывает устаревшее приложение и приложение Symfony:

приложение / config / security.yml

security:
    providers:
        zog:
            id: app.zog_user_provider


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

            anonymous: ~
            logout:
                path:   /logout
                target: /
            guard:
                authenticators:
                    - app.legacy_token_authenticator
                    - app.token_authenticator
                entry_point: app.legacy_token_authenticator

src / AppBundle / Security / LegacyTokenAuthenticator:

class LegacyTokenAuthenticator extends AbstractGuardAuthenticator
{
    private $session;

    private $router;

    public function __construct(
        RouterInterface $router,
        SessionInterface $session,
        $environment
    ) {
        if (session_status() != PHP_SESSION_ACTIVE) {
            if ($environment != 'test'){
                session_start();
            }
            $session->start();
            $this->setSession($session);
        }
        //if (!$session->isStarted()) {

        //}


        $this->router = $router;
    }


    /**
     * @return mixed
     */
    public function getSession()
    {
        return $this->session;
    }


    /**
     * @param mixed $session
     */
    public function setSession($session)
    {
        $this->session = $session;
    }


    /**
     * Called on every request. Return whatever credentials you want,
     * or null to stop authentication.
     */
    public function getCredentials(Request $request)
    {
        $session = $this->getSession();

        if (isset($_SESSION['ADMIN_logged_in']) && intval($_SESSION['ADMIN_logged_in'])){
            return $_SESSION['ADMIN_logged_in'];
        }
        return;
    }

    public function getUser($credentials, UserProviderInterface $userProvider)
    {
        return $userProvider->loadUserByUserId($credentials);
    }

    public function checkCredentials($credentials, UserInterface $user)
    {
        return $user->getUsername() == $credentials;
    }

    public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
    {
        return null;
    }

    public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
    {
        return null;
    }

    /**
     * Called when authentication is needed, but it's not sent
     */
    public function start(Request $request, AuthenticationException $authException = null)
    {
        $url = $this->router->generate('app_security_login');
        return new RedirectResponse($url);
    }

    public function supportsRememberMe()
    {
        return false;
    }
}

src / AppBundle / Security / TokenAuthenticator:

class TokenAuthenticator extends AbstractGuardAuthenticator
{

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

    /**
     * Default message for authentication failure.
     *
     * @var string
     */
    private $failMessage = 'Invalid credentials';

    /**
     * @var UserPasswordEncoderInterface
     */
    private $passwordEncoder;

    /**
     * Creates a new instance of FormAuthenticator
     */
    public function __construct(
        RouterInterface $router,
        SessionInterface $session,
        $environment,
        UserPasswordEncoderInterface $passwordEncoder
    ) {
        $this->passwordEncoder = $passwordEncoder;
        $this->router = $router;

        if (session_status() != PHP_SESSION_ACTIVE) {
            if ($environment != 'test') {
                session_start();
            }
            $session->start();
        }

    }

    /**
     * {@inheritdoc}
     */
    public function getCredentials(Request $request)
    {
        if ($request->getPathInfo() != '/security/login' || !$request->isMethod('POST')) {
            return;
        }

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

    /**
     * {@inheritdoc}
     */
    public function getUser($credentials, UserProviderInterface $userProvider)
    {
        try {
            return $userProvider->loadUserByUsername($credentials['username']);
        } catch (UsernameNotFoundException $e) {
            throw new CustomUserMessageAuthenticationException(
                $e->getMessage() != '' ?$e->getMessage():$this->failMessage
            );
        }
    }

    /**
     * {@inheritdoc}
     */
    public function checkCredentials($credentials, UserInterface $user)
    {
        if ($this->passwordEncoder->isPasswordValid($user, $credentials['password'])) {
            return true;
        }
        throw new CustomUserMessageAuthenticationException($this->failMessage);
    }

    /**
     * {@inheritdoc}
     */
    public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
    {
        $_SESSION['ADMIN_logged_in'] = $token->getUser()->getUsername();
        if ($_SESSION['legacy_page_requested'] ?? '/'){
            $url = $_SESSION['legacy_page_requested'] ?? '/';
        }else{
            $url = '/workflow_detailv2view.php';
        }
        unset($_SESSION['legacy_page_requested']);
        return new RedirectResponse($url);
    }

    /**
     * {@inheritdoc}
     */
    public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
    {
        $request->getSession()->set(Security::AUTHENTICATION_ERROR, $exception);

        $url = $this->router->generate('app_security_login');
        return new RedirectResponse($url);
    }

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

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

Я обнаружил, что эта система работает нормально. Однако новая функция на устаревшей странице запускает 2 запроса асинхронного приложения Symfony, которые перекрываются.

В этом случае происходит следующее: первый запрос показывает 2 файла cookie сеанса.

Request URL: https://somedomain.com/system/staff_meeting/edit/1
Request Method: GET
Status Code: 200 OK
Remote Address: 222.154.225.22:443
Referrer Policy: no-referrer-when-downgrade
Cache-Control: max-age=0, must-revalidate, private
Cache-Control: no-store, no-cache, must-revalidate
Connection: Keep-Alive
Content-Type: text/html; charset=UTF-8
Date: Wed, 13 Jun 2018 21:57:19 GMT
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Keep-Alive: timeout=15, max=90
Server: Apache/2.4.6 (CentOS) mpm-itk/2.4.7-04 OpenSSL/1.0.2k-fips PHP/7.0.20
Set-Cookie: PHPSESSID=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0; path=/
Set-Cookie: PHPSESSID=tn7jhi5n2iu16le1os971vn024; path=/
Transfer-Encoding: chunked
X-Powered-By: PHP/7.0.20

и второй запрос выходит из системы:

Request URL: https://somedomain.com/system/staff_meeting/edit/1
Request Method: GET
Status Code: 302 Found
Remote Address: 222.154.225.22:443
Referrer Policy: no-referrer-when-downgrade
Cache-Control: no-store, no-cache, must-revalidate
Cache-Control: max-age=0, must-revalidate, private
Connection: Keep-Alive
Content-Length: 332
Content-Type: text/html; charset=UTF-8
Date: Thu, 14 Jun 2018 01:26:43 GMT
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Keep-Alive: timeout=15, max=90
Location: /system/security/login
Server: Apache/2.4.6 (CentOS) mpm-itk/2.4.7-04 OpenSSL/1.0.2k-fips PHP/7.0.20
Set-Cookie: PHPSESSID=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0; path=/
X-Powered-By: PHP/7.0.20

Я считаю, что это должно быть какое-то состояние гонки при доступе к переменной $ _SESSION, которую мы используем для удержания вместе унаследованной системы и приложения Symfony.

Есть идеи, как решить эту проблему?

Для управления сеансом в Symfony лучше использовать $ request-> getSession () или использовать внедрение зависимостей и внедрить SessionInterface.

revengeance 21.06.2018 16:00
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
9
1
811
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Избегайте вызова session_start () или $ session-> start () ;! Symfony сделает это сама.

Это ответ? Сессия перезапускается при успешной аутентификации.

revengeance 21.06.2018 13:58

Дополнительные session_starts могут вызвать проблему. Я никогда не забочусь о начале сеанса, его стоит попробовать.

Rawburner 21.06.2018 15:50

Может быть. Но поскольку он работает с guardAuthenticator, сеанс управляется автоматически. У меня была проблема, когда переменные сеанса, которые я установил ранее при успешной аутентификации, терялись здесь.

revengeance 21.06.2018 15:58

Мне удалось обойти проблему, установив

security.yml

security:
    session_fixation_strategy: none

Однако я не уверен, как можно исправить регенерацию сеанса и соответствующее переименование значения Cookie.

Я все еще хочу услышать другие мысли по этому поводу.

Ответ принят как подходящий

внесите следующие изменения в ваши аутентификаторы, чтобы сохранить управление сеансом в symfony:

src / AppBundle / Security / LegacyTokenAuthenticator:

public function __construct(RouterInterface $router) {
    $this->router = $router;
}

public function getUser($credentials, UserProviderInterface $userProvider)
{
    return $userProvider->loadUserByUsername($credentials);
}

public function getCredentials(Request $request)
{
    $session = $request->getSession();
    if ($session->has('ADMIN_logged_in') && intval($session->get('ADMIN_logged_in'))){
        return $session->get('ADMIN_logged_in');
    }
    return null;
}

src / AppBundle / Security / TokenAuthenticator:

public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
    $session = $request->getSession();
    $session->set('ADMIN_logged_in', $token->getUser()->getUsername());
    if ($session->has('legacy_page_requested')) {
        $url = $session->get('legacy_page_requested') ?? '/';
        $session->remove('legacy_page_requested');
    } else {
        $url = '/workflow_detailv2view.php';
    }
    return new RedirectResponse($url);
}

Таким образом, с помощью этого решения я не вижу, как устаревшее приложение проверяет, что кто-то вошел в систему на стороне Symfony, поскольку в $ _SESSION ничего нет. Страница входа присутствует только в Symfony

jdog 14.08.2018 01:50

проверьте прослушиватель, предложенный в этом решении для совместного использования переменных сеанса между платформами: stackoverflow.com/questions/35601629/…

Abdelhafid El Kadiri 14.08.2018 10:08

Я не совсем понимаю вашу архитектуру, но я думаю, что лучше всего, если у вас нет двух аутентификаторов Guard на одном брандмауэре.

Вы можете сделать свой сеанс Symfony устаревшим сеансом следующим образом:

# config.yml
framework:
    session:
        handler_id: session.handler.native_file
        save_path: "/tmp/sessions/%kernel.environment%"

Отсюда любой устаревший сеанс, который у вас есть, должен управляться с помощью суперглобального $_SESSION (который часто используется в устаревшем коде).

Если вы хотите иметь разные правила входа для старой и новой системы, я рекомендую установить два разных брандмауэра. Вы можете назначить общий контекст, чтобы пользователю не приходилось повторно проходить аутентификацию, и просто добавить некоторые дополнительные правила в дочерний класс.

Другие вопросы по теме