Symfony2自定义身份验证提供程序的凭据不正确

问题描述:

我正在实现一个自定义身份验证提供程序,以便在symfony网站上大致使用一本外部API。 它几乎正确地工作,监听器正确地侦听登录表单,然后它调用authenticate函数返回经过身份验证的令牌,问题是即使我设置了securityContextInterface的身份验证令牌,系统也会返回到登录页面错误的凭据。 根据我使用的代码 它可能是什么?Symfony2自定义身份验证提供程序的凭据不正确

security.yml

firewalls: 
    dev: 
     pattern: ^/(_(profiler|wdt)|css|images|js)/ 
     security: false 

    login: 
     pattern: ^/app/login$ 
     security: false 

    api_secured: 
     provider: in_memory 
     pattern: ^/app 
     form_login: 
      login_path: /app/login 
      check_path: /app/login_check 
     logout: 
      path: /app/logout 
      target:/
     api: true 

services.yml

api.security.authentication.provider: 
    class: Manuel\Myapp\MyAppBundle\Security\Authentication\Provider\ApiProvider 
    arguments: ['', %kernel.cache_dir%/security/nonces] 
api.security.authentication.listener: 
    class: Manuel\Myapp\MyAppBundle\Security\Firewall\ApiListener 
    arguments: [@security.context, @security.authentication.manager, %api.url%] 

ApiFactory.php

namespace Manuel\Myapp\MyAppBundle\DependencyInjection\Security\Factory; 

use Symfony\Component\DependencyInjection\ContainerBuilder; 
use Symfony\Component\DependencyInjection\Reference; 
use Symfony\Component\DependencyInjection\DefinitionDecorator; 
use Symfony\Component\Config\Definition\Builder\NodeDefinition; 
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SecurityFactoryInterface; 

class ApiFactory implements SecurityFactoryInterface 
{ 
    public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint) 
    { 
     $providerId = 'security.authentication.provider.api.'.$id; 
     $container 
      ->setDefinition($providerId, new DefinitionDecorator('api.security.authentication.provider')) 
      ->replaceArgument(0, new Reference($userProvider)) 
     ; 

     $listenerId = 'security.authentication.listener.api.'.$id; 
     $listener = $container->setDefinition($listenerId, new DefinitionDecorator('api.security.authentication.listener')); 

     return array($providerId, $listenerId, $defaultEntryPoint); 
    } 

    public function getPosition() 
    { 
     return 'pre_auth'; 
    } 

    public function getKey() 
    { 
     return 'api'; 
    } 

    public function addConfiguration(NodeDefinition $node) 
    { 
    } 
} 

ApiListener.php

namespace Manuel\Myapp\MyAppBundle\Security\Firewall; 

use Symfony\Component\HttpFoundation\Response; 
use Symfony\Component\HttpKernel\Event\GetResponseEvent; 
use Symfony\Component\Security\Http\Firewall\ListenerInterface; 
use Symfony\Component\Security\Core\Exception\AuthenticationException; 
use Symfony\Component\Security\Core\SecurityContextInterface; 
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; 
use Manuel\Myapp\MyAppBundle\Security\Authentication\Token\ApiUserToken; 
use Httpful\Request; 

class ApiListener implements ListenerInterface { 
    protected $securityContext; 
    protected $authenticationManager; 
    protected $container; 

    public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager, $api) 
    { 
     $this->securityContext = $securityContext; 
     $this->authenticationManager = $authenticationManager; 
     //Prendo l'url delle api 
     //Viene passato da services.yml alla classe 
     $this->api = $api; 
    } 

    public function handle(GetResponseEvent $event) 
    { 
     $request = $event->getRequest(); 
     $data = $request->request->all(); 

     //Esiste username e password ? 
     if(!array_key_exists('_username', $data) || !array_key_exists('_password', $data)) { 
      //Ritorna alla pagina di login con bad credentials 
      $this->securityContext->setToken(null); 
      return; 
     } 

     //Autentico in remoto 
     $username = $data['_username']; 
     $password = $data['_password']; 

     $response = Request::post($this->api."/token/new.json") 
        ->body(array(
         'username'=> $username, 
         'password'=> $password)) 
        ->expectsJson() 
        ->sendsForm() 
        ->send(); 
     $decode = json_decode($response); 

     //Se esiste allora vado avanti se no muoio 
     if(!$decode->success) { 
      $this->securityContext->setToken(null); 
      return; 
     } 

     $token = new ApiUserToken(); 
     $token->setUser(''.$decode->user); 
     $token->token = $decode->token; 

     try { 
      $authToken = $this->authenticationManager->authenticate($token); 
      $this->securityContext->setToken($authToken); 

     } catch (AuthenticationException $failed) { 
      // ... si potrebbe loggare qualcosa in questo punto 
      // Per negare l'autenticazione, pulire il token. L'utente sarà rinviato alla pagina di login. 
      $this->securityContext->setToken(null); 
      return; 

      // Negare l'autenticazione con una risposta HTTP '403 Forbidden' 
      //$response = new Response(); 
      //$response->setStatusCode(403); 
      //$event->setResponse($response); 

     } 
    } 
} 

如果我写:

$authToken = $this->authenticationManager->authenticate($token); 
var_dump($authToken); die(); 
$this->securityContext->setToken($authToken); 

结果是:

object(Manuel\Myapp\MyAppBundle\Security\Authentication\Token\ApiUserToken)#4780 (5) {["user":"Symfony\Component\Security\Core\Authentication\Token\AbstractToken":private]=> object(Symfony\Component\Security\Core\User\User)#4782 (7) { ["username":"Symfony\Component\Security\Core\User\User":private]=> string(4) "user" ["password":"Symfony\Component\Security\Core\User\User":private]=> string(15) "10dmao!?postino" ["enabled":"Symfony\Component\Security\Core\User\User":private]=> bool(true) ["accountNonExpired":"Symfony\Component\Security\Core\User\User":private]=> bool(true) ["credentialsNonExpired":"Symfony\Component\Security\Core\User\User":private]=> bool(true) ["accountNonLocked":"Symfony\Component\Security\Core\User\User":private]=> bool(true) ["roles":"Symfony\Component\Security\Core\User\User":private]=> array(1) { [0]=> string(9) "ROLE_USER" } } ["roles":"Symfony\Component\Security\Core\Authentication\Token\AbstractToken":private]=> array(1) { [0]=> object(Symfony\Component\Security\Core\Role\Role)#4779 (1) { ["role":"Symfony\Component\Security\Core\Role\Role":private]=> string(9) "ROLE_USER" } } ["authenticated":"Symfony\Component\Security\Core\Authentication\Token\AbstractToken":private]=> bool(true) ["attributes":"Symfony\Component\Security\Core\Authentication\Token\AbstractToken":private]=> array(0) { } } 

所以它是正确的。

ApiUserToken.php

namespace Manuel\Myapp\MyAppBundle\Security\Authentication\Token; 

use Symfony\Component\Security\Core\Authentication\Token\AbstractToken; 

class ApiUserToken extends AbstractToken 
{ 
    public $token; 

    public function __construct(array $roles = array()) 
    { 
     parent::__construct($roles); 

     // If the user has roles, consider it authenticated 
     $this->setAuthenticated(true); 
    } 

    public function getCredentials() 
    { 
     return ''; 
    } 
} 

ApiProvider.php

namespace Manuel\Myapp\MyAppBundle\Security\Authentication\Provider; 

use Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface; 
use Symfony\Component\Security\Core\User\UserProviderInterface; 
use Symfony\Component\Security\Core\Exception\AuthenticationException; 
use Symfony\Component\Security\Core\Exception\NonceExpiredException; 
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; 
use Manuel\Myapp\MyAppBundle\Security\Authentication\Token\ApiUserToken; 

class ApiProvider implements AuthenticationProviderInterface 
{ 
    private $userProvider; 
    private $cacheDir; 

    public function __construct(UserProviderInterface $userProvider, $cacheDir) 
    { 
     $this->userProvider = $userProvider; 
     $this->cacheDir  = $cacheDir; 
    } 

    public function authenticate(TokenInterface $token) 
    { 

     //Devo aggiungere utente 
     $user = $this->userProvider->loadUserByUsername("user"); 

     if ($user) { 
      $authenticatedToken = new ApiUserToken($user->getRoles()); 
      $authenticatedToken->setUser($user); 

      return $authenticatedToken; 
     } 

     throw new AuthenticationException('The API authentication failed.'); 
    } 

    public function supports(TokenInterface $token) { 
     return $token instanceof ApiUserToken; 
    } 
} 

我已经解决修改security.yml

firewalls: 
     dev: 
      pattern: ^/(_(profiler|wdt)|css|images|js)/ 
      security: false 

     login: 
      pattern: ^/app/login$ 
      security: false 

     secured_area: 
      pattern: ^/app 
      api: true 
      logout: 
       path: /app/logout 
       target:/

和ApiListener.php

public function handle(GetResponseEvent $event) { 

     if($this->securityContext->getToken()){ 
      return; 
     } 

因为在防火墙的每个URL(应用程序/ *)的symfony叫我的听众的handle方法,如果用户已经登录安全令牌已经设置好的,我回到

和check_login功能

public function securityCheckAction() { 
     // The security layer will NOT intercept this request 
     return $this->redirect($this->generateUrl('manuel_myapp_index_after_login')); 

check_login是登录表单的动作,check_login动作在防火墙下,所以,如果凭证正确(使用我的外部API),我的Listerner的句柄方法将首次被调用。我强制symfony使用in_memory用户用于登录,并且将执行check_login操作。 然后,当用户访问防火墙下的另一个页面时,将调用句柄方法,但鉴定令牌已经设置,因此句柄方法将返回并且全部正常工作

外部api登录现在可用!