Symfony 3 FosUserBundle非唯一用户名登录

时间:2017-03-09 12:18:56

标签: php symfony fosuserbundle

我们希望在我们的应用程序中实现的目标是:

  • 非唯一用户名[完成]
  • 唯一的用户名和电子邮件组合
  • FosUserBundle将使用给定的用户名获取所有用户(在用户登录时),并检查是否有任何用户具有给定的密码(使用bcrypt进行哈希处理)。找到用户后,会将用户登录。

只需覆盖用户ORM中的用户名字段,即可使用户名非唯一。但我们仍然坚持如何着手实现最后两点。我们已经开始创建自定义用户提供程序,但似乎Symfony Security只能处理一个用户(名称)。

有没有经验可以帮助我们的人?如果您需要更多信息或代码段,请询问。提前谢谢!

1 个答案:

答案 0 :(得分:1)

因此,在浏览了很多Symfony Security模块的文档后,我们发现了它。

我们在User模型中添加了一个额外的字段(displayname),因为Symfony完全围绕用户名为Unique的事实构建。它始终使用给定的用户名获取第一个用户,这不是我们想要的。

所以我们开始编写自己的Guard身份验证系统,尽管我们不得不进行一些调整,但这非常简单。 这一切都运行良好,但我们遇到了内置UsernamePasswordFormAuthenticationListener的问题,这个监听器仍然从登录表单中获取显示名称。我们实际上想要唯一的用户名,以便Symfony知道要使用哪个用户。

我们创建了一个自定义侦听器,它扩展了标准侦听器,并确保没有从登录表单中提取用户名,而是从用户令牌中提取。

所以我们的流程现在是这样的:用户填写他的用户名(实际上是他的显示名称)和密码,系统会使用该displayname获取所有用户。然后我们循环这些用户并检查是否有人拥有该密码。如果是,请对用户进行身份验证。 在用户创建管理员填充displayname,系统将自动增加为用户名。 (admin_1,admin_2,...)。

我们必须监控@kero所说的是否属实,但是对于Bcrypt来说,即使使用像“123”这样的简单密码,它也会为每个用户产生不同的哈希值。

唯一剩下的就是在displayname和email的唯一组合上使用UniqueConstraint。如果有人知道如何在我们的orm.xml和表单中实现这一点,谢谢。

http://symfony.com/doc/current/security/guard_authentication.html

Custom Guard Authenticator

class Authenticator extends AbstractGuardAuthenticator
{
    private $encoderFactory;
    private $userRepository;
    private $tokenStorage;
    private $router;

public function __construct(EncoderFactoryInterface $encoderFactory, UserRepositoryInterface $userRepository, TokenStorageInterface $tokenStorage, Router $router)
{
    $this->encoderFactory = $encoderFactory;
    $this->userRepository = $userRepository;
    $this->tokenStorage = $tokenStorage;
    $this->router = $router;
}

/**
 * Called on every request. Return whatever credentials you want,
 * or null to stop authentication.
 */
public function getCredentials(Request $request)
{
    $encoder = $this->encoderFactory->getEncoder(new User());
    $displayname = $request->request->get('_username');
    $password = $request->request->get('_password');

    $users = $this->userRepository->findByDisplayname($displayname);

    if ($users !== []) {
        foreach ($users as $user) {
            if ($encoder->isPasswordValid($user->getPassword(), $password, $user->getSalt())) {
                return ['username' => $user->getUsername(), 'password' => $user->getPassword()];
            }
        }
    } else {
        if ($this->tokenStorage->getToken() !== null) {
            $user = $this->tokenStorage->getToken()->getUser();

            return ['username' => $user->getUsername(), 'password' => $user->getPassword()];
        }
    }

    return null;
}

public function getUser($credentials, UserProviderInterface $userProvider)
{
    if ($credentials !== null) {
        return $userProvider->loadUserByUsername($credentials["username"]);
    }

    return null;
}

public function checkCredentials($credentials, UserInterface $user)
{
    if ($user !== null) {
        return true;
    } else {
        return false;
    }
}

public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
    return null;
}

public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
    $exclusions = ['/login'];

    if (!in_array($request->getPathInfo(), $exclusions)) {
        $request->getSession()->set(Security::AUTHENTICATION_ERROR, $exception);
        throw $exception;
    }
}

/**
 * Called when authentication is needed, but it's not sent
 */
public function start(Request $request, AuthenticationException $authException = null)
{
    $data = array(
        // you might translate this message
        'message' => 'Authentication Required'
    );

    return new JsonResponse($data, Response::HTTP_UNAUTHORIZED);
}

public function supportsRememberMe()
{
    return false;
}
}

自定义侦听器

class CustomAuthListener extends UsernamePasswordFormAuthenticationListener
{
    private $csrfTokenManager;
    private $tokenStorage;

public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager, SessionAuthenticationStrategyInterface $sessionStrategy, HttpUtils $httpUtils, $providerKey, AuthenticationSuccessHandlerInterface $successHandler, AuthenticationFailureHandlerInterface $failureHandler, array $options = array(), LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null, CsrfTokenManagerInterface $csrfTokenManager = null)
{
    parent::__construct($tokenStorage, $authenticationManager, $sessionStrategy, $httpUtils, $providerKey, $successHandler, $failureHandler, array_merge(array(
        'username_parameter' => '_username',
        'password_parameter' => '_password',
        'csrf_parameter' => '_csrf_token',
        'csrf_token_id' => 'authenticate',
        'post_only' => true,
    ), $options), $logger, $dispatcher);

    $this->csrfTokenManager = $csrfTokenManager;
    $this->tokenStorage = $tokenStorage;
}

/**
 * {@inheritdoc}
 */
protected function attemptAuthentication(Request $request)
{
    if ($user = $this->tokenStorage->getToken() !== null) {
        $user = $this->tokenStorage->getToken()->getUser();
        $username = $user->getUsername();

        if ($this->options['post_only']) {
            $password = ParameterBagUtils::getParameterBagValue($request->request, $this->options['password_parameter']);
        } else {
            $password = ParameterBagUtils::getRequestParameterValue($request, $this->options['password_parameter']);
        }

        if (strlen($username) > Security::MAX_USERNAME_LENGTH) {
            throw new BadCredentialsException('Invalid username.');
        }

        $request->getSession()->set(Security::LAST_USERNAME, $username);

        return $this->authenticationManager->authenticate(new UsernamePasswordToken($username, $password, $this->providerKey));
    } else {
        return null;
    }
}
}

听众服务

<service id="security.authentication.listener.form" class="Your\Path\To\CustomAuthListener" parent="security.authentication.listener.abstract" abstract="true" />