2013-03-26 99 views
0

我正在實現一個自定義身份驗證提供程序,以便在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; 
    } 
} 

回答

1

我已經解決修改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登錄現在可用!

相關問題