Як імперсонувати користувача
Дата оновлення перекладу 2024-06-04
Як імперсонувати користувача
Іноді корисно мати можливість перемикатися з одного користувача на іншого, не виходячи з системи (наприклад, коли ви налагоджуєте або намагаєтесь зрозуміти баг, який бачить користувач, який ви не можете відтворити).
Caution
Імперсонація користувача несумісназ деякими механізмами аутентифікації (наприклад,
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
// 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\Voter\Voter;
use Symfony\Component\Security\Core\User\UserInterface;
class SwitchToCustomerVoter extends Voter
{
public function __construct(
private Security $security,
) {
}
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
, коли відбудеться
зміна користувачів.
Щоб дізнатися більше деталей про підписників подій, дивіться Події та слухачі подій.