2012-10-05 4 views
5

요청에 유효한 인증 헤더가없는 경우 내 API에 대한 액세스를 거부하는 맞춤 유권자를 생성했습니다. 그것은이 개 요리 책 항목의 조합을 기반으로 : 및 http://symfony.com/doc/current/cookbook/security/custom_authentication_provider.html유권자가 403이 아닌 500 오류를 발생했습니다.

<?php 

namespace Acme\RestBundle\Security\Authorization\Voter; 

use Symfony\Component\DependencyInjection\ContainerInterface; 
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; 
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; 
use Symfony\Component\Security\Core\Exception\AuthenticationException; 
use Symfony\Component\Security\Core\Exception\NonceExpiredException; 
use Monolog\Logger; 

class ApiClientVoter implements VoterInterface 
{ 
    protected $container; 
    protected $nonceCacheDir; 

    /** 
    * We inject the full service container due to scope issues when 
    * injecting the request. 
    * 
    * @param ContainerInterface $container 
    * @param string $nonceCacheDir 
    */ 
    public function __construct(ContainerInterface $container, $nonceCacheDir) 
    { 
     $this->container  = $container; 
     $this->nonceCacheDir = $nonceCacheDir; 
    } 

    public function supportsAttribute($attribute) 
    { 
     // we won't check against a user attribute, so we return true 
     return true; 
    } 

    public function supportsClass($class) 
    { 
     // our voter supports all type of token classes, so we return true 
     return true; 
    } 

    public function vote(TokenInterface $token, $object, array $attributes) 
    { 
     if ($this->authenticate()) { 
      return VoterInterface::ACCESS_ABSTAIN; 
     } 

     return VoterInterface::ACCESS_DENIED; 
    } 


    /** 
    * Checks for an authentication header in the request and confirms 
    * the client is valid. 
    * 
    * @return bool 
    */ 
    protected function authenticate() 
    { 
     $request = $this->container->get('request'); 

     if ($request->headers->has('x-acme-auth')) { 

      $authRegex = '/ApiKey="([^"]+)", ApiDigest="([^"]+)", Nonce="([^"]+)", Created="([^"]+)"/'; 

      if (preg_match($authRegex, $request->headers->get('x-acme-auth'), $matches)) { 
       $apiClient = $this->container->get('in_memory_user_provider')->loadUserByUsername($matches[1]); 

       if ($apiClient && $this->validateDigest($matches[2], $matches[3], $matches[4], $apiClient->getPassword())) { 
        return true; 
       } 
      } 
     } else { 
      $this->container->get('logger')->err('no x-acme-auth header present in request'); 
     } 

     return false; 
    } 

    /** 
    * Performs checks to prevent replay attacks and to validate 
    * digest against a known client. 
    * 
    * @param string $digest 
    * @param string $nonce 
    * @param string $created 
    * @param string $secret 
    * @return bool 
    * @throws AuthenticationException 
    * @throws NonceExpiredException 
    */ 
    protected function validateDigest($digest, $nonce, $created, $secret) 
    { 
     // Expire timestamp after 5 minutes 
     if (time() - strtotime($created) > 300) { 
      $this->container->get('logger')->err('Timestamp expired'); 

      return false; 
     } 

     if (!is_dir($this->nonceCacheDir)) { 
      mkdir($this->nonceCacheDir, 0777, true); 
     } 

     // Validate nonce is unique within 5 minutes 
     if (file_exists($this->nonceCacheDir.'/'.$nonce) && file_get_contents($this->nonceCacheDir.'/'.$nonce) + 300 > time()) { 
      $this->container->get('logger')->err('Previously used nonce detected'); 

      return false; 
     } 

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

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

     return $digest === $expected; 
    } 
} 

난 데 문제는 내 유권자가 ACCESS_DENIED를 반환 할 때 방화벽이 인증 항목 포인트로 리디렉션 할 때, 나는 500 오류를 얻을 수 있습니다 :

[2012-10-05 11:09:16] app.ERROR: no x-acme-auth header present in request [] [] 
[2012-10-05 11:09:16] event.DEBUG: Notified event "kernel.exception" to listener "Symfony\Component\Security\Http\Firewall\ExceptionListener::onKernelException". [] [] 
[2012-10-05 11:09:16] security.DEBUG: Access is denied (user is not fully authenticated) by "/home/phil/projects/acme/vendor/symfony/symfony/src/Symfony/Component/Security/Http/Firewall/AccessListener.php" at line 70; redirecting to authentication entry point [] [] 
[2012-10-05 11:09:16] event.DEBUG: Notified event "kernel.exception" to listener "Symfony\Component\HttpKernel\EventListener\ProfilerListener::onKernelException". [] [] 
[2012-10-05 11:09:16] event.DEBUG: Notified event "kernel.exception" to listener "Symfony\Component\HttpKernel\EventListener\ExceptionListener::onKernelException". [] [] 
[2012-10-05 11:09:16] request.CRITICAL: Symfony\Component\Security\Core\Exception\InsufficientAuthenticationException: Full authentication is required to access this resource. (uncaught exception) at /home/phil/projects/acme/vendor/symfony/symfony/src/Symfony/Component/Security/Http/Firewall/ExceptionListener.php line 109 

내가 실제로 원하는 것은 403 응답을 반환하는 것입니다. 이것을 할 수 있습니까?

답변

1

커널이 발견 한 예외는 항상 HTTP 500을 반환합니다 (code).

인증이 유효하지 않은 경우 사용자가 직접 응답해야합니다.

use Symfony\Component\HttpFoundation\Response; 
$response = new Response(); 

$response->setContent('<html><body><h1>Bad Credentials</h1></body></html>'); 
$response->setStatusCode(403); 
$response->headers->set('Content-Type', 'text/html'); 

// prints the HTTP headers followed by the content 
$response->send(); 
관련 문제