Перезагрузить роль пользователя после изменения без повторной регистрации

Как обновить вошедшую в систему роль пользователя, например. когда он был изменен администратором? Я нашел параметр безопасности always_authenticate_before_granting (он не включен в документацию Symfony 4) и установил его на true.

безопасность.yaml:

security:
    always_authenticate_before_granting: true
    encoders:
        App\Entity\Main\User:
            algorithm: bcrypt

    providers:
        app_user_provider:
            entity:
                class: App\Entity\Main\User
                property: email
    role_hierarchy:
        ROLE_ADMIN:       ROLE_USER
    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        main:
            anonymous: ~
            guard:
                authenticators:
                    - App\Security\LoginFormAuthenticator
            form_login:
                login_path: login
                check_path: login
            logout:
                path: logout
                target: homepage
            remember_me:
                secret:   '%kernel.secret%'
                path:     /
    access_control:
        - { path: ^/login$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/.*, roles: ROLE_USER }

но это не вступает в силу.

Обновлено

Я создал подписчика onRequest:

class RequestSubscriber implements EventSubscriberInterface
{
    private $tokenStorage;

    public function __construct(TokenStorageInterface $tokenStorage)
    {
        $this->tokenStorage = $tokenStorage;
    }

    public static function getSubscribedEvents(): array
    {
        return [
            KernelEvents::REQUEST => 'onRequest'
        ];
    }

    public function onRequest(GetResponseEvent $event): void
    {
        if (!$event->isMasterRequest()) {
            return;
        }

        if (!$token = $this->tokenStorage->getToken()) return;

        $sessionUser = $token->getUser();

        if ($sessionUser instanceof User) {
            $this->tokenStorage->setToken(new PostAuthenticationGuardToken($sessionUser, 'main', $sessionUser->getRoles()));
        }
    }
}

и теперь я могу обновлять обновленные роли при каждом запросе, но сравнивать sessionUser с databaseUser бессмысленно, потому что sessionUser всегда содержит недавно обновленные роли, хотя в Symfony Profiler > Security Token перечислены старые (в случае, если я не устанавливаю новый токен, конечно).

Снова сохраните токен проверки подлинности в хранилище токенов.

emix 18.03.2019 13:09

@emix Я забыл сказать, что измененный пользователь и пользователь-администратор не совпадают. Повторное создание токена работает, но для пользователя, который редактирует другого пользователя (в этом случае пользователь-администратор становится редактируемым пользователем).

Rodgard 18.03.2019 13:39

Упомянутый вами параметр always_authenticate_before_granting используется только AuthorizationChecker, например. когда вы вызываете метод is_granted().

emix 18.03.2019 13:46
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Symfony Station Communiqué - 17 февраля 2023 г
Symfony Station Communiqué - 17 февраля 2023 г
Это коммюнике первоначально появилось на Symfony Station , вашем источнике передовых новостей Symfony, PHP и кибербезопасности.
Управление ответами api для исключений на Symfony с помощью KernelEvents
Управление ответами api для исключений на Symfony с помощью KernelEvents
Много раз при создании api нам нужно возвращать клиентам разные ответы в зависимости от возникшего исключения.
1
3
2 227
1

Ответы 1

Tl;dr Боюсь, вам придется ввести собственный механизм, чтобы это заработало.

Токен сеанса хранится внутри сеанса пользователя. Это сильно повлияет на производительность вашего приложения, потому что каждый раз будет требоваться обращение к базе данных, чтобы проверить, не изменилась ли роль.

Таким образом, вам понадобится прослушиватель запросов, который будет сравнивать роль базы данных с текущей ролью пользователя, и если она не совпадает, замените токен в сеансе, на этот раз новым списком ролей, например. (псевдокод):

sessionUser = tokenStorage.token.user
databaseUser = userRepository.find(sessionUser.id)

if sessionUser.roles !== databaseUser.roles
    tokenStorage.setToken(new PostAuthenticationGuardToken(…))

или используйте кеш в качестве носителя флага, чтобы уведомить пользователя об изменении. Этот метод будет намного быстрее наверняка.

sessionUser = tokenStorage.token.user

// of course the flag has to be set when the user's role gets changed
cacheToken = 'users.role_reload.' . sessionUser.id

if cache.has(cacheToken)
    databaseUser = userRepository.find(sessionUser.id)
    tokenStorage.setToken(new PostAuthenticationGuardToken(…))
    cache.remove(cacheToken)

В любом случае пользователь должен спрашивать приложение, было ли изменение роли при каждом запросе.

Я попробовал первый, и он, похоже, не работает, в методе подписчика запроса я вызываю var_dump для $this->tokenStorage->getToken()->getUser()->getRoles();, и он возвращает обновленные роли, поэтому sessionUser.roles и databaseUser.roles всегда будут равны. Итак, теперь я установил токен PostAuthenticationGuardToken с данными sessionUser без проверки изменений ролей, и это работает.

Rodgard 18.03.2019 18:14

Правильно. Вы хотите сравнить текущую роль с ролью базы данных. Также вы должны быть осторожны при обращении с токеном из магазина. Там 1) может вообще не быть токена 2) токен может не содержать пользователя 3) пользователь может быть не вашим пользователем безопасности, а, например, строкой. Если вы хотите просмотреть свой код, вы должны вставить его. Я не могу сказать, что «не работает», если вы, очевидно, не поделитесь этим со мной.

emix 18.03.2019 18:59

Итак, где вы проверяете, изменилась ли роль? Вы ничего не делаете в этом слушателе. Сравните свой код с моим первым алгоритмом.

emix 18.03.2019 21:10

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