Як імперсонувати користувача
Дата оновлення перекладу 2025-09-15
Як імперсонувати користувача
Іноді корисно мати можливість перемикатися з одного користувача на іншого, не виходячи з системи (наприклад, коли ви налагоджуєте або намагаєтесь зрозуміти баг, який бачить користувач, який ви не можете відтворити).
Warning
Імперсонація користувача несумісназ деякими механізмами аутентифікації (наприклад,
REMOTE_USER), де інформація аутентифікації має відправлятися по кожному запиту.
Імперсонації користувача можна легко досягти, активуваши слухача брандмауера
switch_user:
1 2 3 4 5 6 7 8
# config/packages/security.yaml
security:
# ...
firewalls:
main:
# ...
switch_user: true
Щоб перемкнутися на іншого користувача, просто додайте рядок запиту з параметром
_switch_user та імʼя користувача (або будь-яке поле, яке використовує наш постачальник
користувачів для завантаження користувачів) як значення поточного URL:
1
http://example.com/somewhere?_switch_user=thomas
Tip
Ви можете скористатися функцією гілки impersonation_path('thomas')
Tip
Замість додавання параметра рядку запиту _switch_user, ви можете передати
імʼя користувача у користувацькому HTTP-заголовку, скоригувавши налаштування
parameter. Наприклад, щоб використати заголовок X-Switch-User (доступний
в PHP як HTTP_X_SWITCH_USER) додайте цю конфігурацію:
1 2 3 4 5 6 7
# config/packages/security.yaml
security:
# ...
firewalls:
main:
# ...
switch_user: { parameter: X-Switch-User }
Щоб перемкнутися назад на оригінального користувача, використайте спеціальне імʼя
користувача _exit:
1
http://example.com/somewhere?_switch_user=_exit
Tip
Ви можете скористатися функцією Twig impersonation_exit_path('/somewhere')
Під час імперсонації, користувачу надається спеціальна роль під назвою
ROLE_PREVIOUS_ADMIN. Використання role_hierarchy
- чудовий спосіб надати цю роль користувачам, які її потребують.
Як дізнатися, що імперсонація активна
Ви можете використати спеціальний атрибут IS_IMPERSONATOR, щоб перевірити,
чи активна імперсонація у цій сесії. Використайте цю спеціальну роль, наприклад,
щоб продемонструвати посилання для виходу з імперсонації у шаблоні:
1 2 3
{% if is_granted('IS_IMPERSONATOR') %}
<a href="{{ impersonation_exit_path(path('homepage') ) }}">Exit impersonation</a>
{% endif %}
Пошук оригінального користувача
У деяких випадках вам може знадобитися отримати обʼєкт, який представляє користувача-
імперсонатора замість імперсонованого користувача. Коли користувач імперсонізується,
токен, який зберігається у сховищі токенів, буде екземпляром SwitchUserToken.
Використайте наступний уривок, щоб отримати оригінальний токен, який надає вам доступ
до користувача-імперсонатора:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
// src/Service/SomeService.php
namespace App\Service;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\Security\Core\Authentication\Token\SwitchUserToken;
// ...
class SomeService
{
public function __construct(
private Security $security,
) {
}
public function someMethod(): void
{
// ...
$token = $this->security->getToken();
if ($token instanceof SwitchUserToken) {
$impersonatorUser = $token->getOriginalToken()->getUser();
}
// ...
}
}
Контроль параметра запиту
Ця функція має бути доступною лише для обмеженої групи користувачів. За замовчуванням,
доступ обмежено до користувачів, які мають роль ROLE_ALLOWED_TO_SWITCH. Імʼя цієї
роли можна змінити через налаштування role. Ви можете також налаштувати імʼя параметра
запиту через налаштування parameter:
1 2 3 4 5 6 7 8
# config/packages/security.yaml
security:
# ...
firewalls:
main:
# ...
switch_user: { role: ROLE_ADMIN, parameter: _want_to_be_this_user }
Перенаправлення за конкретним цільовим маршрутом
Note
Працює тільки у брандмауері зі станом.
Ця функція дозволяє вам контролювати перенаправлення цільового маршруту через
target_route.
1 2 3 4 5 6 7 8
# config/packages/security.yaml
security:
# ...
firewalls:
main:
# ...
switch_user: { target_route: app_user_dashboard }
Обмеження перемикання між користувачами
Якщо вам потрібно більше контролю над перемиканням між користувачами, ви можете використати
виборця безпеки. Спочатку, сконфігуруйте switch_user, щоб перевірити наявність якогось
нового користувацького атрибуту. Це може бути чим завгодно, але не може починатися з ROLE_
(щоб форсувати виклик тільки вашого виборця):
1 2 3 4 5 6 7 8
# config/packages/security.yaml
security:
# ...
firewalls:
main:
# ...
switch_user: { role: CAN_SWITCH_USER }
Потім, створіть клас виборця, який відповідає на цю роль та включа в себе будь-яку користувацьку логіку за вашим бажанням:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
// src/Security/Voter/SwitchToCustomerVoter.php
namespace App\Security\Voter;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
use Symfony\Component\Security\Core\User\UserInterface;
class SwitchToCustomerVoter extends Voter
{
public function __construct(
private AccessDecisionManagerInterface $accessDecisionManager,
) {
}
protected function supports($attribute, $subject): bool
{
return in_array($attribute, ['CAN_SWITCH_USER'])
&& $subject instanceof UserInterface;
}
protected function voteOnAttribute($attribute, $subject, TokenInterface $token): bool
{
$user = $token->getUser();
// якщо користувач анонімний або якщо субʼєкт не є користувачем, не надавати доступ
if (!$user instanceof UserInterface || !$subject instanceof UserInterface) {
return false;
}
// ви все ще можете перевірити наявність ROLE_ALLOWED_TO_SWITCH
if ($this->security->isGranted('ROLE_ALLOWED_TO_SWITCH')) {
return true;
}
// перевірити наявність будь яких бажаних ролей
if ($this->security->isGranted('ROLE_TECH_SUPPORT')) {
return true;
}
/*
* або використати якісь користувацькі дані з вашого обʼєкта User
if ($user->isAllowedToSwitch()) {
return true;
}
*/
return false;
}
}
Ось і все! При перемиканні між користувачами, ваш виборець тепер має повний контроль над тим дозволено це, чи ні. Якщо ваш виборець не викликається, див. .
Події
Брандмауер оголошує подію security.switch_user одразу після того, як буде виконано
імперсонацію. Ваш слухач або підписник отримає
SwitchUserEvent, який
ви можете використати, щоб отримати користувача, якого ви імперсонуєте.
Ця подія також оголошується прямо перед тим, як імперсонація буде повністю зупинена. Ви можете використовувати її, щоб отримати початкового користучава-імперсонатора.
Стаття Як зробити локаль "липкою" під час сесії користувача не оновлює локаль, коли ви імперсонуєте користувача. Якщо ви хочете бути впевненим в оновленні локалі при перемиканні між користувачами, додайте підписника подій до цієї події:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
// src/EventListener/SwitchUserSubscriber.php
namespace App\EventListener;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Security\Http\Event\SwitchUserEvent;
use Symfony\Component\Security\Http\SecurityEvents;
class SwitchUserSubscriber implements EventSubscriberInterface
{
public function onSwitchUser(SwitchUserEvent $event): void
{
$request = $event->getRequest();
if ($request->hasSession() && ($session = $request->getSession())) {
$session->set(
'_locale',
// припускаючи, що ваш User має якийсь метод getLocale()
$event->getTargetUser()->getLocale()
);
}
}
public static function getSubscribedEvents(): array
{
return [
// constant for security.switch_user
SecurityEvents::SWITCH_USER => 'onSwitchUser',
];
}
}
Ось і все! Якщо ви використовуєте
конфігурацію services.yml за замовчуванням ,
то Symfony автоматично виявить ваш сервіс та викличе onSwitchUser, коли відбудеться
зміна користувачів.
Щоб дізнатися більше деталей про підписників подій, дивіться Події та слухачі подій.