如何假扮一个用户

3.4 版本
维护中的版本

有时,能够从一个用户切换到另一个用户却毋须登出和再次登入,是有必要的(例如,在你调试或者尝试搞清一个“某用户可见,你却不能重现”的bug时)。

Caution

模拟用户不可兼容 预认证防火墙。原因是,模拟过程的认证状态,需要被保持在服务器端,而预认证信息(SSL_CLIENT_S_DN_EmailREMOTE_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>
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,
        ),
    ),
));

要切到其他用户,只需为当前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
{% if is_granted('ROLE_PREVIOUS_ADMIN') %}
    <a href="{{ path('homepage', {'_switch_user': '_exit'}) }}">Exit impersonation</a>
{% endif %}
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 (