2014-02-20 43 views
4

你好Symfony2的展示,而不是我的AuthenticationException的

「令牌未在SecurityContext中找到」我試圖建立某種形式的WSSE身份驗證爲我Symfony2的API。但是當測試未經授權的調用時,我得到了一個AuthenticationCredentialsNotFoundException,而不是獲得我的自定義AuthenticationException,它的狀態碼是500。

任何想法爲什麼會發生這種情況?這裏是我的代碼:

WsseListener.php

<?php 
namespace KrugerCorp\VOIPBundle\Security\Firewall; 

use Symfony\Component\Config\Definition\Exception\Exception; 
use Symfony\Component\HttpFoundation\Response; 
use Symfony\Component\HttpKernel\Event\GetResponseEvent; 
use Symfony\Component\HttpKernel\Log\LoggerInterface; 
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 KrugerCorp\VOIPBundle\Security\Authentication\Token\WsseTenantToken; 

class WsseListener implements ListenerInterface 
{ 
    protected $securityContext; 
    protected $authenticationManager; 
    protected $logger; 

    public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager, LoggerInterface $logger) 
    { 
     $this->securityContext = $securityContext; 
     $this->authenticationManager = $authenticationManager; 
     $this->logger = $logger; 
    } 

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

     $wsseRegex = '/UsernameToken Username="([^"]+)", PasswordDigest="([^"]+)", Nonce="([^"]+)", Created="([^"]+)"/'; 
     if (!$request->headers->has('x-wsse') || 1 !== preg_match($wsseRegex, $request->headers->get('x-wsse'), $matches)) 
      return; 

     $token = new WsseTenantToken(); 
     $token->setUser($matches[1]); 

     $token->digest = $matches[2]; 
     $token->nonce = $matches[3]; 
     $token->created = $matches[4]; 

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

      return; 
     } catch (AuthenticationException $e) { 
      $failedMessage = 'WSSE login failed for '.$token->getUsername()-'. Why? '.$e->getMessage(); 
      $this->logger->error($failedMessage); 

      $response = new Response(); 
      $response->setStatusCode(403); 
      $response->setContent($failedMessage); 
      $event->setResponse($response); 
      return; 
     } 

     $response = new Response(); 
     $response->setStatusCode(403); 
     $event->setResponse($response); 
    } 
} 

WsseProvider.php

<?php 

namespace KrugerCorp\VOIPBundle\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 KrugerCorp\VOIPBundle\Security\Authentication\Token\WsseTenantToken; 

class WsseProvider implements AuthenticationProviderInterface { 

    private $tenantProvider; 
    private $cacheDir; 

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

    public function authenticate(TokenInterface $token) 
    { 
     $tenant = $this->tenantProvider->loadUserByUsername($token->getUsername()); 

     if (!$tenant) 
      throw new AuthenticationException("Bad credentials."); 

     if ($tenant && $this->validateDigest($token->digest, $token->nonce, $token->created, $tenant->getPassword())) 
     { 
      $authenticatedToken = new WsseTenantToken($tenant->getRoles()); 
      $authenticatedToken->setUser($tenant); 

      return $authenticatedToken; 
     } 

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

    protected function validateDigest($digest, $nonce, $created, $secret) 
    { 
     if (strtotime($created) > time()) 
      throw new AuthenticationException('The provided WSSE timestamp is in the future. Nice try.'); 

     if (time() - strtotime($created) > 300) 
      throw new AuthenticationException('The timestamp is outdated.'); 

     if (file_exists($this->cacheDir.'/'.$nonce) && file_get_contents($this->cacheDir.'/'.$nonce) + 300 > time()) 
      throw new NonceExpiredException('Previously used nonce detected'); 

     if (!is_dir($this->cacheDir)) 
      mkdir($this->cacheDir, 0777, true); 

     file_put_contents($this->cacheDir.'/'.$nonce, time()); 

     $expected = base64_encode(sha1(base64_decode($nonce).$created.$secret, true)); 

     if ($digest !== $expected) 
      throw new AuthenticationException('Bad credentials. Digest is not as expected.'); 

     return true; 
    } 

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

WsseFactory.php

<?php 

namespace KrugerCorp\VOIPBundle\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 WsseFactory implements SecurityFactoryInterface 
{ 
    public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint) 
    { 
     $providerId = 'security.authentication.provider.wsse.'.$id; 
     $container 
      ->setDefinition($providerId, new DefinitionDecorator('wsse.security.authentication.provider')) 
      ->replaceArgument(0, new Reference($userProvider)); 

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

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

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

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

    public function addConfiguration(NodeDefinition $node) 
    { 
    } 
} 

我的防火牆

wsse_secured: 
     pattern: ^/api/.* 
     stateless: true 
     wsse:  true 
     anonymous: false 

我的服務

wsse.security.authentication.provider: 
    class: KrugerCorp\VOIPBundle\Security\Authentication\Provider\WsseProvider 
    arguments: ["", "%kernel.cache_dir%/security/nonces"] 

wsse.security.authentication.listener: 
    class: KrugerCorp\VOIPBundle\Security\Firewall\WsseListener 
    arguments: ["@security.context", "@security.authentication.manager", "@logger"] 
    tags: 
     - { name: monolog.logger, channel: wsse } 

和MU包類

<?php 

namespace KrugerCorp\VOIPBundle; 

use Symfony\Component\HttpKernel\Bundle\Bundle; 
use KrugerCorp\VOIPBundle\DependencyInjection\Security\Factory\WsseFactory; 
use Symfony\Component\DependencyInjection\ContainerBuilder; 

class KrugerCorpVOIPBundle extends Bundle 
{ 
    public function build(ContainerBuilder $container) 
    { 
     parent::build($container); 

     $extension = $container->getExtension('security'); 
     $extension->addSecurityListenerFactory(new WsseFactory()); 
    } 
} 
+0

這是否 - > tenantProvider-> loadUserByUsername($ token-> getUsername());拋出任何異常? – Jekis

回答

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

    return; 
} catch (AuthenticationException $e) { 
    // ... 
} 

您正趕上的AuthenticationException而已!

$this->authenticationManager->authenticate($token); 

也會引發NonceExpiredException不會被逮住。

而我的代碼審查...閱讀評論。

// I guess loadUserByUsername throws UsernameNotFoundException. 
// Wrap it in try catch and throw new AuthenticationException("Bad credentials."); 
$tenant = $this->tenantProvider->loadUserByUsername($token->getUsername()); 

// You will not need this... 
if (!$tenant) 
    throw new AuthenticationException("Bad credentials."); 

// $tenant always true here. 
if ($tenant && $this->validateDigest($token->digest, $token->nonce, $token->created, $tenant->getPassword())) 
{ 
    $authenticatedToken = new WsseTenantToken($tenant->getRoles()); 
    $authenticatedToken->setUser($tenant); 

    return $authenticatedToken; 
} 
相關問題