1

我正在尝试在使用 Apache 的 auth_kerb 进行身份验证的应用程序上设置 switch_user 功能。REMOTE_USER正确返回并能够登录。但是,当我尝试伪装成其他用户时,我无法登录。我希望切换到的用户确实存在于应用程序中。发生切换用户的尝试,但再次调用预身份验证并REMOTE_USER加载初始身份验证。

关于如何使用 remote_user 和自定义用户提供程序让 switch_user 工作的任何想法?

安全.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
    ...

服务.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']]

    ...

安全监听器.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';
  }

}
4

1 回答 1

0

此修复涉及调整 AbstractPreAuthenticatedListener 以检查是否存在与登录用户匹配的标准令牌,如果不是已存储登录用户但附加到“切换到”用户 ID 的自定义令牌。

这是代码的重要(非复制)部分:

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.
 */

公共函数 __construct($user, $credentials, $providerKey, array $roles = array(), $preAuthenticatedData) { parent::__construct($roles);

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 服务.yml

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

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

2 安全.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:查询用户数据。
于 2015-06-25T06:16:38.753 回答