Symfony Custom UserProvider Auth + Parse SDK

时间:2016-12-16 06:28:41

标签: php symfony parse-platform silex

Heyo!

我知道人们在使用自定义提供程序和Web服务身份验证时遇到问题是一个常见问题。我花了好几个小时试图弄清楚如何做到这一点,但我几乎吓坏了。

所以,问题是:我正在使用带有自定义UserProvider和AbstractGuardAuthenticator的Symfony防火墙。问题出在UserProvider实现中的loadUserByUsername($username)函数中。

出于安全原因,我无法从Parse(我的网络服务)检索用户密码,loadUserByUsername($username)功能要求提供。即使在关于how to create a custom user provider using web services的文档中,他们也在从数据库中检索用户密码。

那么在这种情况下解决方案是什么?当我无法访问用户密码时,我该怎么办?

我目前的代码是这样的:

$app['app.authenticator'] = function () {
    return new Authenticator($app);
};


$app['security.firewalls'] = array(
    'login' => array(
        'pattern' => '^/login/$',
    ),
    'secured' => array(
        'pattern' => '^.*$',
        'form' => array('login_path' => '/login/', 'check_path' => '/login/auth/'),
        'logout' => array('logout_path' => '/logout/', 'invalidate_session' => true),
        'guard'          => array(
            'authenticators'  => array(
                'app.authenticator'
            ),
        ),
        'users' => function () use ($app) {
            return new UserProvider($app);
        },
    )
);

Authenticator.php是一个非常大的代码,因为它扩展了AbstractGuardAuthenticator类。但我是来自Symfony docs的basically using this one。唯一的问题是我正在向UserProvider类发送用户名和密码,因为这样我可以检查用户和密码是否正确。像这样:

public function getUser($credentials, UserProviderInterface $userProvider) {
    return $userProvider->loadUserByUsername($credentials);
}

我的UserProvider类是默认类,如果从我的Authenticator提交的凭据是正确的,我只是在loadUserByUsername函数内部进行检查。像这样:

public function loadUserByUsername($credentials) {
    $encoder = new BCryptPasswordEncoder(13);

    try {
        $user = ParseUser::logIn($credentials['username'], $credentials['password']);
    } catch (ParseException $error) {
        throw new UsernameNotFoundException(sprintf('Invalid Credentials.'));
    }

    return new User($credentials['username'], $encoder->encodePassword($credentials['password'], ''), explode(',', 'ROLE_USER'), true, true, true, true);
}

问题是:登录后(登录的所有内容都正常工作),Silex在需要保护的每个页面中调用loadUserByUsername函数,但只发送用户名参数。

所以基本上,我不知道该怎么做。我真的想弄明白如何让这件事发挥作用。

感谢您的帮助!

1 个答案:

答案 0 :(得分:0)

我有类似的实现,这个问题是众所周知的。在我的用户提供程序中,我有方法loadUserByUsername($ username)和refreshUser(UserInterface $ user)。由于我遇到了和你一样的问题,我不会在loadUserByUsername中检查用户,而是简单地返回一个只包含用户名的新Object,以免扰乱流程。 loadUserByUsername对外部API没有意义,所以我只是跳过它。方法refreshUser要么在每个请求上调用,这很有用。

在您的AbstractGuardAuthenticator中,您使用createAuthenticatedToken方法,该方法返回一个标记。你应该有完整的身份验证用户:

abstract class AbstractGuardAuthenticator implements GuardAuthenticatorInterface
{
    /**
     * Shortcut to create a PostAuthenticationGuardToken for you, if you don't really
     * care about which authenticated token you're using.
     *
     * @param UserInterface $user
     * @param string        $providerKey
     *
     * @return PostAuthenticationGuardToken
     */
    public function createAuthenticatedToken(UserInterface $user, $providerKey)
    {

        //do login stuff
        //save password in user

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

然后,我不会使用loadUserByUsername而是使用refreshUser。每次请求都会调用它们:

/**
 * Don't use
 * @codeCoverageIgnore
 * @param string $username
 * @return User
 */
public function loadUserByUsername($username)
{
    return new User($username,  null, '', ['ROLE_USER'], '');
}

/**
 * Refresh user on every subrequest after login
 * @param UserInterface $user
 * @return User
 */
public function refreshUser(UserInterface $user)
{

    $password = $user->getPassword();
    $username = $user->getUsername();
    //login check
}