使用auth_kerb侦听器进行身份验证时无法模拟用户

时间:2015-06-18 02:09:21

标签: symfony symfony-2.6 switch-user mod-auth-kerb

我正在尝试在使用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';
  }

}

1 个答案:

答案 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

1 services.yml

设置account.security_listener,security.authentication.switchuser_listener和security.authentication.listener.remote_user_switch

这是预期用户提供商的补充。

2 security.yml

使用此安全提供程序

secured_area:
  switch_user: { role: ROLE_ALLOWED_TO_SWITCH, parameter: _masquerade }
  pattern:    ^/

  remote_user_switch:
    provider: webservice_user_provider

3检查用户提供商是否为您的用户加载了支持数据。

4安装安全文件。

  • RemoteUserSwitchFactory.php:定义要处理的侦听器 认证事件。
  • PreAuthenticatedWithSwitchUserListener.php: 我们的特殊认证逻辑。 SwitchUserListener.php:处理 切换用户事件。
  • PreAuthenticatedSwitchUserToken.php:令牌到 将登录用户存储为辅助数据。
  • WebserviceUser.php:我们的 用户数据实体
  • WebserviceUserProvider.php:查询用户数据。