支付宝扫一扫付款
微信扫一扫付款
(微信为保护隐私,不显示你的昵称)
有时,能够从一个用户切换到另一个用户却毋须登出和再次登入,是有必要的(例如,在你调试或者尝试搞清一个“某用户可见,你却不能重现”的bug时)。
Caution
模拟用户不可兼容 预认证防火墙。原因是,模拟过程的认证状态,需要被保持在服务器端,而预认证信息(SSL_CLIENT_S_DN_Email
,REMOTE_USER
或其他)是在每一次请求时发送的。
激活 switch_user
防火墙监听,即轻松搞定模拟用户:
1 2 3 4 5 6 7 8 | # app/config/security.yml
security:
# ...
firewalls:
main:
# ...
switch_user: true |
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> |
要切到其他用户,只需为当前URL添加一个查询字符串(query string)即 _switch_user
参数,同时把用户名作为其值:
1 | http://example.com/somewhere?_switch_user=thomas |
要切回原来的用户,使用特殊的 _exit
用户名:
1 | http://example.com/somewhere?_switch_user=_exit |
在模拟期间,用户被赋予了一个特殊的role,叫做 ROLE_PREVIOUS_ADMIN
。在模板中,比如,该角色可以用于显示一个“退出模拟用户”之连接:
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 ?> |
某些情况下,你可能需要去获取“正在模拟别人的”用户对象,而不是“正被别人模拟的”用户对象。使用以下码段去遍历用户的roles,直至找到一个(持有) SwitchUserRole
的对象:
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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的用户。这个角色的名称可以通过设置 role
来修改。为强化安全性,你还可以通过 parameter
来更改查询参数名称(query parameter name):
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 } |
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> |
firewall在用户模拟完成之后,立即派遣 security.switch_user
事件。SwitchUserEvent 被传入监听(listener),你可以用该事件来获取现在正在模拟的用户(译注:被模拟的用户)。
当你在模拟一个用户时, 把locale信息“粘连”到用户的Session周期中 一文中的locale将不再更新。下面的代码示例将展示如何改变“被粘连的locale(the sticky locale)”:
1 2 3 4 5 6 | # app/config/services.yml
services:
app.switch_user_listener:
class: AppBundle\EventListener\SwitchUserListener
tags:
- { name: kernel.event_listener, event: security.switch_user, method: onSwitchUser |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <!-- app/config/services.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services
http://symfony.com/schema/dic/services/services-1.0.xsd"
>
<services>
<service id="app.switch_user_listener"
class="AppBundle\EventListener\SwitchUserListener"
>
<tag name="kernel.event_listener"
event="security.switch_user"
method="onSwitchUser"
/>
</service>
</services>
</container> |
1 2 3 4 5 | // app/config/services.php
$container
->register('app.switch_user_listener', 'AppBundle\EventListener\SwitchUserListener')
->addTag('kernel.event_listener', array('event' => 'security.switch_user', 'method' => 'onSwitchUser'))
; |
Caution
监听器的实现过程,已经假设你 User
entity拥有一个 getLocale()
方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | // src/AppBundle/EventListener/SwitchUserListener.php
namespace AppBundle\EventListener;
use Symfony\Component\Security\Http\Event\SwitchUserEvent;
class SwitchUserListener
{
public function onSwitchUser(SwitchUserEvent $event)
{
$event->getRequest()->getSession()->set(
'_locale',
$event->getTargetUser()->getLocale()
);
}
} |
本文,包括例程代码在内,采用的是 Creative Commons BY-SA 3.0 创作共用授权。