Symfony 2项目中的双向身份验证

时间:2014-06-05 17:12:54

标签: php security symfony authentication

我需要根据此算法在我的一个Symfony 2项目中实现双向身份验证过程:

  1. 用户在身份验证表单中输入用户名和密码并提交。
  2. 系统首先以默认方式检查其用户名和密码(所有用户都使用Doctrine ORM存储)。
  3. 如果上一步失败,请调用外部API并将其传递给用户名md5(password)
  4. 如果上一步成功,则创建一个新的用户实体并将其用作经过身份验证的用户。
  5. 如果步骤#3失败,则认为认证失败。
  6. 我已经有一个服务可以使用外部API通过他的用户名和密码来调用用户,我只是想在验证过程中使用它。

    实现此行为的最简单方法是什么?我只需指出正确的方向。

    更新

    正在实施" custom authenticator"这个问题是一个很好的解决方案吗?或者有更好的方法吗?

    查看文档,我将在自定义身份验证器中实现两个身份验证步骤。是否有可能只实施额外的步骤?

1 个答案:

答案 0 :(得分:0)

是的,这是要走的路。如果您的Web服务只能通过用户名为您提供用户,那么您只能在UserProvider中执行此操作,因为在其范围内您只有用户名。如果您必须通过un / pw查询,那么您必须在身份验证器中进行查询,因为在该范围内您有密码。因此,使用简单的形式,它将看起来像这样

public function authenticateToken(TokenInterface $token, UserProviderInterface $userProvider, $providerKey)
{
    try {
        $user = $userProvider->loadUserByUsername($token->getUsername());
    } catch (UsernameNotFoundException $e) {
        throw new AuthenticationException('Invalid username or password');
    }

    $encoder = $this->encoderFactory->getEncoder($user);
    $passwordValid = $encoder->isPasswordValid(
        $user->getPassword(),
        $token->getCredentials(),
        $user->getSalt()
    );

    if ($passwordValid) {

        return new UsernamePasswordToken(
            $user,
            $user->getPassword(),
            $providerKey,
            $user->getRoles()
        );
    }

    // remote users fallback

    $webUser = $this->externalUserService->getByUsernamePassword(
        $token->getUsername(),
        $token->getCredentials()

    );

    if ($webUser) {
        return new UsernamePasswordToken(
            $webUser,
            $token->getCredentials(),
            $providerKey,
            $webUser->getRoles()
        );
    }

    throw new AuthenticationException('Invalid username or password');
}

Ofc在这个类中有太多的ifs并且它负责多于一件事,所以要整洁你可以应用复合模式并且有3个验证器,一个通用复合器,第二个本地db验证器和第三个外部服务验证器,以及从像这样的服务配置构建它。

# services.yml

my_app.authenticator.main:
    class: MyApp/Security/Core/Authentication/CompisiteAuthenticator
    calls:
        - [ add, [@my_app.authenticator.locale]]
        - [ add, [@my_app.authenticator.remote]]

my_app.authenticator.locale:
    class: MyApp/Security/Core/Authentication/LocalAuthenticator
    arguments: [@security.encoder_factory]

my_app.authenticator.remote:
    class: MyApp/Security/Core/Authentication/RemoteAuthenticator
    arguments: [@my_app.remote_user_service]

复合

<?php

namespace MyApp/Security/Core/;

class CompositeAuthenticator implements SimpleFormAuthenticatorInterface
{
    /** @var SimpleFormAuthenticatorInterface[] */
    protected $children = array();

    public function add(SimpleFormAuthenticatorInterface $authenticator)
    {
        $this->children[] = $authenticator;
    }

    public function createToken(Request $request, $username, $password, $providerKey)
    {
        return new UsernamePasswordToken($username, $password, $providerKey);
    }

    public function supportsToken(TokenInterface $token, $providerKey)
    {
        return $token instanceof UsernamePasswordToken
            && $token->getProviderKey() === $providerKey;
    }

    public function authenticateToken(TokenInterface $token, UserProviderInterface $userProvider, $providerKey)
    {
        $result = null;
        foreach ($this->children as $authenticator)
        {
            $result = $authenticator->authenticateToken($token, $userProvider, $providerKey);
            if ($result) {

                return $result;
            }
        }

        throw new AuthenticationException('Invalid username or password');
    }
}

我认为本地和远程身份验证器是微不足道的