我正在尝试在使用Apache的auth_kerb进行身份验证的应用程序上设置switch_user功能。 REMOTE_USER
正确返回并且能够登录。但是当我尝试伪装成另一个用户时,我无法登录。我希望切换到的用户确实存在于应用程序中。尝试切换用户但再次调用预认证并加载初始REMOTE_USER
。
有关如何使用remote_user和自定义用户提供程序使switch_user正常工作的任何想法?
security.yml
security:
providers:
webservice_user_provider:
id: webservice_user_provider
...
firewalls:
secured_area:
switch_user: { role: ROLE_ALLOWED_TO_SWITCH, parameter: _masquerade }
pattern: ^/
remote_user:
provider: webservice_user_provider
...
services.yml
parameters:
account.security_listener.class: Acme\MyUserBundle\Listener\SecurityListener
services:
account.security_listener:
class: %account.security_listener.class%
arguments: ['@security.authorization_checker', '@session', '@doctrine.orm.entity_manager', '@router', '@event_dispatcher']
tags:
- { name: kernel.event_listener, event: security.authentication.failure, method: onAuthenticationFailure }
- { name: kernel.event_listener, event: security.interactive_login, method: onSecurityInteractiveLogin }
- { name: kernel.event_listener, event: security.switch_user, method: onSecuritySwitchUser }
webservice_user_provider:
class: Acme\MyUserBundle\Security\User\WebserviceUserProvider
calls:
- [setEntityManager , ['@logger', '@doctrine.orm.entity_manager']]
...
SecurityListener.php
namespace Acme\MyUserBundle\Listener;
use ...
/**
* Class SecurityListener
* @package Acme\MyUserBundle\Listener
*/
class SecurityListener {
protected $session;
protected $security;
protected $em;
protected $router;
protected $dispatcher;
public function __construct(
AuthorizationCheckerInterface $security,
Session $session,
EntityManager $em,
UrlGeneratorInterface $router,
EventDispatcherInterface $dispatcher
// TraceableEventDispatcher $dispatcher
// ContainerAwareEventDispatcher $dispatcher
) {
$this->security = $security;
$this->session = $session;
$this->em = $em;
$this->router = $router;
$this->dispatcher = $dispatcher;
}
/**
*
* @param AuthenticationFailureEvent $event
* @throws AuthenticationException
*/
public function onAuthenticationFailure(AuthenticationFailureEvent $event) {
throw new AuthenticationException($event->getAuthenticationException());
}
/**
* @param InteractiveLoginEvent $event
*/
public function onSecurityInteractiveLogin(InteractiveLoginEvent $event) {
// set some defaults...
}
/**
* @param SwitchUserEvent $event
*/
public function onSecuritySwitchUser(SwitchUserEvent $event) {
$this->dispatcher->addListener(KernelEvents::RESPONSE, array($this, 'onSwitchUserResponse'));
}
/**
* @param FilterResponseEvent $event
*/
public function onSwitchUserResponse(FilterResponseEvent $event) {
$response = new RedirectResponse($this->router->generate('acme_mybundle_default_index'));
$event->setResponse($response);
}
}
WebServiceProvider.php
namespace Acme\MyUserBundle\Security\User;
use ...
class WebserviceUserProvider implements UserProviderInterface {
protected $entityManager;
protected $logger;
/**
*
* @param LoggerInterface $logger
* @param EntityManager $em
*/
public function setEntityManager(LoggerInterface $logger, EntityManager $em) {
$this->logger = $logger;
$this->entityManager = $em;
}
/**
*
* @param string $username
* @return Person
* @throws UsernameNotFoundException
*/
public function loadUserByUsername($username) {
# Find the person
$person = $this->entityManager->getRepository('AcmeMyBundle:Person')
->find($username);
if ($person) {
$this->logger->debug("Logged in, finding person: " . $person->getUsername());
return $person;
}
throw new UsernameNotFoundException(
sprintf('Username "%s" does not exist.', $username)
);
}
/**
*
* @param \Symfony\Component\Security\Core\User\UserInterface $person
* @throws \Symfony\Component\Security\Core\Exception\UnsupportedUserException
* @internal param \Symfony\Component\Security\Core\User\UserInterface $user
* @return Person
*/
public function refreshUser(UserInterface $person) {
if (!$person instanceof Person) {
throw new UnsupportedUserException(
sprintf('Instances of "%s" are not supported.', get_class($person))
);
}
return $this->loadUserByUsername($person->getUsername());
}
/**
*
* @param type $class
* @return type
*/
public function supportsClass($class) {
return $class === 'Acme\MyBundle\Entity\Person';
}
}
答案 0 :(得分:0)
此修复涉及调整AbstractPreAuthenticatedListener以检查是否存在与登录用户匹配的标准令牌,如果不是已存储登录用户但已附加到“切换到”用户ID的自定义令牌。 / p>
这是代码的重要(非复制)部分:
if (null !== $token = $this->securityContext->getToken()) {
if ($token instanceof PreAuthenticatedToken && $this->providerKey == $token->getProviderKey() && $token->isAuthenticated() && $token->getUsername() === $user) {
return;
}
// Switch user token. Check the original token.
if ($token instanceof PreAuthenticatedSwitchUserToken && $this->providerKey == $token->getProviderKey() && $token->isAuthenticated() && $token->getOriginalUsername() === $user) {
return;
}
}
令牌存储登录用户并使用getOriginalUsername返回它。
存储现有的身份验证数据(在$ preAuthenticatedData中传递)
/**
* Constructor.
*/
public function __construct($ user,$ credentials,$ providerKey,array $ roles = array(),$ preAuthenticatedData) { 父:: __构建体($角色);
if (empty($providerKey)) {
throw new \InvalidArgumentException('$providerKey must not be empty.');
}
$this->setUser($user);
$this->credentials = $credentials;
$this->providerKey = $providerKey;
if (!is_array($preAuthenticatedData) && count($preAuthenticatedData) > 0) {
throw new \InvalidArgumentException('No preauthenticated data. Must have the server login credentials.');
}
$this->original_username = $preAuthenticatedData[0];
if ($roles) {
$this->setAuthenticated(true);
}
}
吸气剂
public function getOriginalUsername() {
return $this->original_username;
}
藏匿更改
/**
* {@inheritdoc}
*/
public function serialize()
{
return serialize(array($this->credentials, $this->providerKey, $this->original_username, parent::serialize()));
}
/**
* {@inheritdoc}
*/
public function unserialize($str)
{
list($this->credentials, $this->providerKey, $this->original_username, $parentStr) = unserialize($str);
parent::unserialize($parentStr);
}
这些更改适合Symfony安全系统更广泛的自定义环境。其源代码位于github。
设置account.security_listener,security.authentication.switchuser_listener和security.authentication.listener.remote_user_switch
这是预期用户提供商的补充。
使用此安全提供程序
secured_area:
switch_user: { role: ROLE_ALLOWED_TO_SWITCH, parameter: _masquerade }
pattern: ^/
remote_user_switch:
provider: webservice_user_provider