Как обновить вошедшую в систему роль пользователя, например. когда он был изменен администратором? Я нашел параметр безопасности 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 Я забыл сказать, что измененный пользователь и пользователь-администратор не совпадают. Повторное создание токена работает, но для пользователя, который редактирует другого пользователя (в этом случае пользователь-администратор становится редактируемым пользователем).
Упомянутый вами параметр always_authenticate_before_granting используется только AuthorizationChecker, например. когда вы вызываете метод is_granted().




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 без проверки изменений ролей, и это работает.
Правильно. Вы хотите сравнить текущую роль с ролью базы данных. Также вы должны быть осторожны при обращении с токеном из магазина. Там 1) может вообще не быть токена 2) токен может не содержать пользователя 3) пользователь может быть не вашим пользователем безопасности, а, например, строкой. Если вы хотите просмотреть свой код, вы должны вставить его. Я не могу сказать, что «не работает», если вы, очевидно, не поделитесь этим со мной.
Итак, где вы проверяете, изменилась ли роль? Вы ничего не делаете в этом слушателе. Сравните свой код с моим первым алгоритмом.
Снова сохраните токен проверки подлинности в хранилище токенов.