Как олицетворить пользователя¶
Иногда полезно иметь возможность переключаться с одного пользователя на другого, не выходя из системы (например, когда вы отлаживаете или пытаетесь понять баг, который видит пользователь, который вы не можете воспроизвести).
Caution
Олицетворение пользователя несовместимо с
предварительно аутентифицированными брандмауэрами.
Причиной этого является то, что олицетвореие требует, чтобы состояние аутентификации
обрабатывалось серверной стороной, а предварительно аутентифицированная информация
(SSL_CLIENT_S_DN_Email
, REMOTE_USER
или другая) отправляется по каждому запросу.
Олицетворения пользователя можно легко добиться, активировав слушатель
брандмауэра switch_user
:
- YAML
1 2 3 4 5 6 7 8
# app/config/security.yml security: # ... firewalls: main: # ... switch_user: true
- XML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
<!-- app/config/security.xml --> <?xml version="1.0" encoding="UTF-8"?> <srv:container xmlns="http://symfony.com/schema/dic/security" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:srv="http://symfony.com/schema/dic/services" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> <config> <!-- ... --> <firewall name="main"> <!-- ... --> <switch-user /> </firewall> </config> </srv:container>
- PHP
1 2 3 4 5 6 7 8 9 10 11
// app/config/security.php $container->loadFromExtension('security', array( // ... 'firewalls' => array( 'main'=> array( // ... 'switch_user' => true, ), ), ));
Чтобы переключиться на другого пользователя, просто добавьте строку запроса
с параметром _switch_user
и имя пользователя, как значения текущего URL:
1 | http://example.com/somewhere?_switch_user=thomas
|
Чтобы переключиться обратно на изначального пользователя, используйте специальное
имя пользователя _exit
:
1 | http://example.com/somewhere?_switch_user=_exit
|
Во время олицетворения, пользователю предоставляется специальная роль
под названием ROLE_PREVIOUS_ADMIN
. В шаблоне, например, эта роль
может быть использована, чтобы показать ссылку на выход из олицетворения:
- Twig
1 2 3
{% if is_granted('ROLE_PREVIOUS_ADMIN') %} <a href="{{ path('homepage', {'_switch_user': '_exit'}) }}">Exit impersonation</a> {% endif %}
- PHP
1 2 3 4 5 6 7
<?php if ($view['security']->isGranted('ROLE_PREVIOUS_ADMIN')): ?> <a href="<?php echo $view['router']->path('homepage', array( '_switch_user' => '_exit', )) ?>"> Exit impersonation </a> <?php endif ?>
В некоторых случаях, вам может понадобиться получить объект, который представляет
не олицетворённого пользователя, а олицетворяющего. Используйте следующий отрезок
кода, чтобы выполнить перебор ролей пользователя до тех пор, пока вы не найдёте
роль с объектом SwitchUserRole
:
use Symfony\Component\Security\Core\Role\SwitchUserRole;
$authChecker = $this->get('security.authorization_checker');
$tokenStorage = $this->get('security.token_storage');
if ($authChecker->isGranted('ROLE_PREVIOUS_ADMIN')) {
foreach ($tokenStorage->getToken()->getRoles() as $role) {
if ($role instanceof SwitchUserRole) {
$impersonatingUser = $role->getSource()->getUser();
break;
}
}
}
Конечно же, эта функция должна быть доступна только маленькой группе пользователей.
По умолчанию, доступ имеется только у пользователей с ролью ROLE_ALLOWED_TO_SWITCH
.
Имя этой роли можно изменить через настройку role
. Для дополнительной безопасности
вы также можете изменить имя параметра запроса через настройку parameter
:
- YAML
1 2 3 4 5 6 7 8
# app/config/security.yml security: # ... firewalls: main: # ... switch_user: { role: ROLE_ADMIN, parameter: _want_to_be_this_user }
- XML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
<!-- app/config/security.xml --> <?xml version="1.0" encoding="UTF-8"?> <srv:container xmlns="http://symfony.com/schema/dic/security" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:srv="http://symfony.com/schema/dic/services" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> <config> <!-- ... --> <firewall name="main"> <!-- ... --> <switch-user role="ROLE_ADMIN" parameter="_want_to_be_this_user" /> </firewall> </config> </srv:container>
- PHP
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// app/config/security.php $container->loadFromExtension('security', array( // ... 'firewalls' => array( 'main'=> array( // ... 'switch_user' => array( 'role' => 'ROLE_ADMIN', 'parameter' => '_want_to_be_this_user', ), ), ), ));
События¶
Брандмауэр развёртывает событие security.switch_user
сразу после того, как будет
выполнено олицетворение. SwitchUserEvent
передаётся слушателю и вы можете использовать его, чтобы получить пользователя, которого
вы олицетворяете.
Статья Making the Locale “Sticky” during a User’s Session не обновляет локаль, когда вы олицетваоряете пользователя. Если вы хотите быть уверенными в обновлении локали при переключении между пользователями, добавьте подписчика событий к этому событию:
// src/AppBundle/EventListener/SwitchUserListener.php
namespace AppBundle\EventListener;
use Symfony\Component\Security\Http\Event\SwitchUserEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Security\Http\SecurityEvents;
class SwitchUserSusbcriber implements EventSubscriberInterface
{
public function onSwitchUser(SwitchUserEvent $event)
{
$event->getRequest()->getSession()->set(
'_locale',
// предполагая, что ваш User имеет некоторый метод getLocale()
$event->getTargetUser()->getLocale()
);
}
public static function getSubscribedEvents()
{
return array(
// константа для security.switch_user
SecurityEvents::SWITCH_USER => 'onSwitchUser',
);
}
}
Вот и всё! Если вы используете конфигурацию services.yml по умолчанию,
то Symfony автоматически обнаружит ваш сервис и вызовет onSwitchUser
, когда
произойдёт смена пользователей.
Чтобы узнать больше деталей о подписчиках событий, смотрите Events and Event Listeners.
Эта документация является переводом официальной документации Symfony и предоставляется по свободной лицензии CC BY-SA 3.0.