我正在创建一个自定义身份验证提供程序。我编写了自己的身份验证提供程序,监听程序,令牌和所有内容。它基于表单登录,我已经逐步完成了代码,所有内容似乎都配置正确。一切都按正确的顺序调用,我的身份验证提供程序被完美地调用。身份验证提供程序成功验证用户身份,并返回经过身份验证的令牌。我扩展 AbstractAuthenticationListener ,在 handle 方法中,它将设置安全上下文。
用户似乎已登录,但在调试工具栏中,未设置令牌,我看到“您未经过身份验证”和“无令牌”。
我是否缺少任何配置设置?为什么用户将登录,身份验证提供程序成功返回,使用经过身份验证的令牌,在安全上下文中设置但仍未进行身份验证?关于如何调试这个的任何提示?
(我会根据需要发布代码。)
编辑:令牌定义:
这很简单,只是从AbstractToken扩展而来:
class UserToken extends AbstractToken
{
private $username;
private $password;
private $domain;
private $providerKey;
public function __construct($username, $password, $domain, $provider_key, array $roles = array('ROLE_USER'))
{
parent::__construct($roles);
$this->username = $username;
$this->password = $password;
$this->domain = $domain;
$this->providerKey = $provider_key;
}
public function getCredentials()
{
return '';
}
function getUsername() {
return $this->username;
}
function getDomain() {
return $this->domain;
}
function getPassword() {
return $this->password;
}
function getProviderKey(){
return $this->providerKey;
}
}
身份验证侦听器:
class Listener extends AbstractAuthenticationListener
{
protected $authenticationManager;
public function __construct(
SecurityContextInterface $securityContext,
AuthenticationManagerInterface $authenticationManager,
SessionAuthenticationStrategyInterface $sessionStrategy,
HttpUtils $httpUtils,
$providerKey,
AuthenticationSuccessHandlerInterface $successHandler,
AuthenticationFailureHandlerInterface $failureHandler,
array $options = array(),
LoggerInterface $logger = null,
EventDispatcherInterface $dispatcher = null
//CsrfProviderInterface $csrfProvider = null
) {
parent::__construct(
$securityContext,
$authenticationManager,
$sessionStrategy,
$httpUtils,
$providerKey,
$successHandler,
$failureHandler,
array_merge(
array(
'username_parameter' => '_username',
'password_parameter' => '_password',
'domain_parameter' => '_domain',
'csrf_parameter' => '_csrf_token',
'intention' => 'authenticate',
'post_only' => true,
),
$options
),
$logger,
$dispatcher
);
}
/**
* Performs authentication.
*
* @param Request $request A Request instance
*
* @return TokenInterface|Response|null The authenticated token, null if full authentication is not possible, or a Response
*
* @throws AuthenticationException if the authentication fails
*/
protected function attemptAuthentication(Request $request)
{
// Create initial unauthenticated token and pass data to the authentication manager.
// TODO validate request data.
$username = trim($request->request->get($this->options['username_parameter'], null, true));
$password = $request->request->get($this->options['password_parameter'], null, true);
$domain = $request->request->get($this->options['domain_parameter'], null, true);
$token = $this->authenticationManager->authenticate(new UserToken($username, $password, $domain, $this->providerKey));
return $token;
}
}
上面的代码将通过AuthenticationManager调用提供程序上的auth函数:
//This is from the AuthenticationProvider
public function authenticate(TokenInterface $token) {
$loginHandler = new LoginAuthenticationHandler($token->getUsername(), $token->getPassword(), $token->getDomain());
//This code just calls our web service and authenticates. I removed some business logic here, but this shows the gist of it.
if(!$boAuthenticationToken = $loginHandler->authenticate())
{
throw new AuthenticationException('Bad credentials');
}
else{
$user = $this->userProvider->loadUserByUsername($token->getUsername());
//$user = $this->userProvider->getUser($token, $boAuthenticationToken);
// Set the user which will be invoked in the controllers.
$token->setUser($user);
$token->setAuthenticated(true);
return $token;
}
}
Bundle Services.yml
parameters:
services:
ws.security.authentication.provider:
#http://blog.vandenbrand.org/2012/06/19/symfony2-authentication-provider-authenticate-against-webservice/
class: Aurora\OurCustomBundle\Security\Authentication\Provider\Provider
arguments: ["bo_remove_this_with_bo_auth_service", "", "@security.user_checker", "", "@security.encoder_factory"]
ws.security.authentication.listener:
class: Aurora\OurCustomBundle\Security\Firewall\Listener
parent: security.authentication.listener.abstract
abstract: true
#arguments: []
arguments: ["@security.context", "@security.authentication.manager", "@security.authentication.session_strategy", "@security.http_utils", "ws.user_provider", "@security.authentication.customized_success_handler", "@main.cas.rest.user.authentication.failure.service"]
ws.user_provider:
class: Aurora\OurCustomBundle\Security\User\UserProvider
最后,UserProvider
class UserProvider implements UserProviderInterface
{
public function loadUserByUsername($username)
{
//Just return a simple user for now.
return new User($username, array('ROLE_USER'));
}
public function refreshUser(UserInterface $user)
{
if (!$user instanceof User) {
throw new UnsupportedUserException(
sprintf('Instances of "%s" are not supported.', get_class($user))
);
}
return $this->loadUserByUsername($user->getUsername());
}
public function supportsClass($class)
{
return $class === 'Aurora\OurCustomBundle\Security\User\User';
}
}
答案 0 :(得分:0)
经过几个小时的拔毛,我发现了问题!
令牌实现不正确。由于我实现了自己的Token,它从AbstractToken扩展而来,我还需要实现serialize()和unserialize()函数。
一旦我这样做,代码就可以了。更新后的Token类如下所示:
class UserToken extends AbstractToken
{
private $username;
private $password;
private $domain;
private $providerKey;
public function __construct($username, $password, $domain, $provider_key, array $roles = array('ROLE_USER'))
{
parent::__construct($roles);
$this->username = $username;
$this->password = $password;
$this->domain = $domain;
$this->providerKey = $provider_key;
}
public function getCredentials()
{
return '';
}
function getUsername() {
return $this->username;
}
function getDomain() {
return $this->domain;
}
function getPassword() {
return $this->password;
}
function getProviderKey(){
return $this->providerKey;
}
function serialize(){
return serialize(array($this->username, $this->password, $this->domain, parent::serialize()));
}
function unserialize($serialized){
list($this->username, $this->password, $this->domain, $parentSerialization) = unserialize($serialized);
parent::unserialize($parentSerialization);
}
}