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函数,但只发送用户名参数。
所以基本上,我不知道该怎么做。我真的想弄明白如何让这件事发挥作用。
感谢您的帮助!
答案 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
}