以编程方式注销切换用户
Log out of a switched user programmatically
我有一个应用程序,其中管理员经常代表普通用户行事。流程如下:
- 管理员登录
- 管理员会看到一个非管理员用户列表
- 管理员向非管理员用户单击 link
<a href="{{ path('_main_app', {'_switch_user': user.username}) }}">
- 管理员以用户的身份做事
- 管理员点击浏览器返回,直到他们再次出现在非管理员用户列表中
此时管理页面显示一个按钮<a href="{{ path('_admin', {'_switch_user': '_exit'}) }}">Exit {{app.user.username}} </a>
但是,管理员用户经常忘记点击它,而是点击另一个非管理员用户。当他们这样做时,他们会收到一条消息 You are already switched to User XXX。显然这是有意为之,但对于管理员用户来说仍然不是很好的体验。
允许管理员以这种方式切换用户的推荐方法是什么?我认为每次他们到达普通用户列表时调用 {'_switch_user': '_exit'}
是一个不错的选择(同时强制永远不缓存此页面),但我看不到以编程方式执行此操作的方法。
另一种方法可能是访问Javascript中的URL{'_switch_user': '_exit'}
,但这似乎不太令人满意。
推荐的方法是什么?
如果您想强制不缓存响应,您可以在控制器级别执行此操作。
您可以按如下方式呈现私人回复:
$expire = new \DateTime('now');
$expire->sub(new \DateInterval('P1Y'));
$response = new Response();
$response->setPrivate();
$response->setExpires($expire);
$response->headers->add(array('Pragma' => 'no-cache'));
然后在你的控制器中使用 render 方法渲染你的视图,yan 可以作为第三个参数传递给你的私有响应
您应该看看 SwitchUserListener Class (Symfony\Component\Security\Http\Firewall\SwitchUserListener)。
这就是奇迹发生的地方(以及 "you are already switched to... message is created")。问题是它并没有给你太多选择来连接它。其实官方唯一的就是切换成功后的事件。
但是您可以用您自己的实现完全替换此监听器。
在我自己的项目中使用它来实现用户切换的 ip 限制(acls 不起作用或者我弄错了)
只需创建您自己的并在您的 services.yml
中替换它
security.authentication.switchuser_listener:
class: AppBundle\Services\Security\SwitchUserListener
public: false
abstract: true
arguments: ['@security.token_storage', '', '@security.user_checker', '', '@security.access.decision_manager', '@?logger', '_switch_user', 'ROLE_ALLOWED_TO_SWITCH', '@?event_dispatcher']
tags:
- { name: monolog.logger, channel: security }
编辑:您需要复制整个内容,因为所有相关功能都是私有的。它不是很漂亮,但我看不到任何其他方式(除了在你的用例中,当用户已经切换时可能根本不显示 switch-link。)
正如 Joe 所说,Symfony 的 SwitchUserListener especially SwitchUserListener::attemptExitUser 拥有以编程方式注销模拟用户所需的所有信息。
这是它的要点(在控制器内部使用):
$authorizationChecker = $this->get('security.authorization_checker');
if ($authorizationChecker->isGranted('ROLE_PREVIOUS_ADMIN')) {
// Code from Symfony\Component\Security\Http\Firewall\SwitchUserListener
$tokenStorage = $this->get('security.token_storage');
$originalToken = false;
if ($currentToken = $tokenStorage->getToken()) {
foreach ($currentToken->getRoles() as $role) {
if ($role instanceof SwitchUserRole) {
$originalToken = $role->getSource();
break;
}
}
}
if ($currentToken === null || $originalToken === false) {
throw new AuthenticationCredentialsNotFoundException('Could not find original Token object.');
}
if ($this->has('event_dispatcher') && $originalToken->getUser() instanceof UserInterface) {
$eventDispatcher = $this->get('event_dispatcher');
// Attention: Put your user provider here. I wasn't able to find a way to
// get the current firewall's user provider programatically.
$userProvider = $this->get('my.user_provider');
$originalUser = $userProvider->refreshUser($originalToken->getUser());
$switchEvent = new SwitchUserEvent($request, $originalUser);
$eventDispatcher->dispatch(SecurityEvents::SWITCH_USER, $switchEvent);
}
$tokenStorage->setToken($originalToken);
// Redirect to the current action so that the request's user is the original one.
return $this->redirect($request->getRequestUri());
}
请注意:
- 这在 Symfony 2.8 中进行了测试,但对于 3.x 甚至 4.x
应该没有太大区别
- 您必须将
$this->get('my.user_provider')
替换为防火墙的用户提供商。不幸的是,我无法找到以编程方式获取当前防火墙用户的方法。
我有一个应用程序,其中管理员经常代表普通用户行事。流程如下:
- 管理员登录
- 管理员会看到一个非管理员用户列表
- 管理员向非管理员用户单击 link
<a href="{{ path('_main_app', {'_switch_user': user.username}) }}">
- 管理员以用户的身份做事
- 管理员点击浏览器返回,直到他们再次出现在非管理员用户列表中
此时管理页面显示一个按钮<a href="{{ path('_admin', {'_switch_user': '_exit'}) }}">Exit {{app.user.username}} </a>
但是,管理员用户经常忘记点击它,而是点击另一个非管理员用户。当他们这样做时,他们会收到一条消息 You are already switched to User XXX。显然这是有意为之,但对于管理员用户来说仍然不是很好的体验。
允许管理员以这种方式切换用户的推荐方法是什么?我认为每次他们到达普通用户列表时调用 {'_switch_user': '_exit'}
是一个不错的选择(同时强制永远不缓存此页面),但我看不到以编程方式执行此操作的方法。
另一种方法可能是访问Javascript中的URL{'_switch_user': '_exit'}
,但这似乎不太令人满意。
推荐的方法是什么?
如果您想强制不缓存响应,您可以在控制器级别执行此操作。 您可以按如下方式呈现私人回复:
$expire = new \DateTime('now');
$expire->sub(new \DateInterval('P1Y'));
$response = new Response();
$response->setPrivate();
$response->setExpires($expire);
$response->headers->add(array('Pragma' => 'no-cache'));
然后在你的控制器中使用 render 方法渲染你的视图,yan 可以作为第三个参数传递给你的私有响应
您应该看看 SwitchUserListener Class (Symfony\Component\Security\Http\Firewall\SwitchUserListener)。
这就是奇迹发生的地方(以及 "you are already switched to... message is created")。问题是它并没有给你太多选择来连接它。其实官方唯一的就是切换成功后的事件。
但是您可以用您自己的实现完全替换此监听器。 在我自己的项目中使用它来实现用户切换的 ip 限制(acls 不起作用或者我弄错了)
只需创建您自己的并在您的 services.yml
中替换它security.authentication.switchuser_listener:
class: AppBundle\Services\Security\SwitchUserListener
public: false
abstract: true
arguments: ['@security.token_storage', '', '@security.user_checker', '', '@security.access.decision_manager', '@?logger', '_switch_user', 'ROLE_ALLOWED_TO_SWITCH', '@?event_dispatcher']
tags:
- { name: monolog.logger, channel: security }
编辑:您需要复制整个内容,因为所有相关功能都是私有的。它不是很漂亮,但我看不到任何其他方式(除了在你的用例中,当用户已经切换时可能根本不显示 switch-link。)
正如 Joe 所说,Symfony 的 SwitchUserListener especially SwitchUserListener::attemptExitUser 拥有以编程方式注销模拟用户所需的所有信息。
这是它的要点(在控制器内部使用):
$authorizationChecker = $this->get('security.authorization_checker');
if ($authorizationChecker->isGranted('ROLE_PREVIOUS_ADMIN')) {
// Code from Symfony\Component\Security\Http\Firewall\SwitchUserListener
$tokenStorage = $this->get('security.token_storage');
$originalToken = false;
if ($currentToken = $tokenStorage->getToken()) {
foreach ($currentToken->getRoles() as $role) {
if ($role instanceof SwitchUserRole) {
$originalToken = $role->getSource();
break;
}
}
}
if ($currentToken === null || $originalToken === false) {
throw new AuthenticationCredentialsNotFoundException('Could not find original Token object.');
}
if ($this->has('event_dispatcher') && $originalToken->getUser() instanceof UserInterface) {
$eventDispatcher = $this->get('event_dispatcher');
// Attention: Put your user provider here. I wasn't able to find a way to
// get the current firewall's user provider programatically.
$userProvider = $this->get('my.user_provider');
$originalUser = $userProvider->refreshUser($originalToken->getUser());
$switchEvent = new SwitchUserEvent($request, $originalUser);
$eventDispatcher->dispatch(SecurityEvents::SWITCH_USER, $switchEvent);
}
$tokenStorage->setToken($originalToken);
// Redirect to the current action so that the request's user is the original one.
return $this->redirect($request->getRequestUri());
}
请注意:
- 这在 Symfony 2.8 中进行了测试,但对于 3.x 甚至 4.x 应该没有太大区别
- 您必须将
$this->get('my.user_provider')
替换为防火墙的用户提供商。不幸的是,我无法找到以编程方式获取当前防火墙用户的方法。