Вернуть JsonResponse, когда я использую AuthTokenAuthenticator (symfony 3) - PullRequest
0 голосов
/ 04 ноября 2018

Я указываю, что я начинаю с Symfony. Я хочу создать API (без FOSRestBundle) с токеном в качестве средства аутентификации.

Я следовал различным учебникам для этой установки. Я хотел бы, чтобы при возникновении ошибки, обнаруженной классом «AuthTokenAuthenticator», он возвращал json, а не представление html.

Вот мой сценарий:

AuthTokenAuthenticator

namespace AppBundle\Security;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Authentication\Token\PreAuthenticatedToken;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
use Symfony\Component\Security\Http\Authentication\SimplePreAuthenticatorInterface;
use Symfony\Component\Security\Http\HttpUtils;
use Symfony\Component\HttpFoundation\JsonResponse;

class AuthTokenAuthenticator implements 
SimplePreAuthenticatorInterface, AuthenticationFailureHandlerInterface
{

const TOKEN_VALIDITY_DURATION = 12 * 3600;

protected $httpUtils;

public function __construct(HttpUtils $httpUtils)
{
    $this->httpUtils = $httpUtils;
}

public function createToken(Request $request, $providerKey)
{

    //$targetUrlToken = '/auth-tokens'; // login
    //$targetUrlUser = '/users/create'; // create account

    /*if ($request->getMethod() === "POST" && $this->httpUtils->checkRequestPath($request, $targetUrlUser) || $request->getMethod() === "POST" && $this->httpUtils->checkRequestPath($request, $targetUrlToken) ) {
        return;
    }*/

    $authTokenHeader = $request->headers->get('X-Auth-Token');

    if (!$authTokenHeader) {
        //return new JsonResponse(array("error" => 1, "desc" => "INVALID_TOKEN", "message" => "X-Auth-Token header is required"));
       throw new BadCredentialsException('X-Auth-Token header is required');


    }

    return new PreAuthenticatedToken(
        'anon.',
        $authTokenHeader,
        $providerKey
        );
}

public function authenticateToken(TokenInterface $token, UserProviderInterface $userProvider, $providerKey)
{
    if (!$userProvider instanceof AuthTokenUserProvider) {
        throw new \InvalidArgumentException(
            sprintf(
                'The user provider must be an instance of AuthTokenUserProvider (%s was given).',
                get_class($userProvider)
                )
            );
    }

    $authTokenHeader = $token->getCredentials();
    $authToken = $userProvider->getAuthToken($authTokenHeader);

    if (!$authToken || !$this->isTokenValid($authToken)) {
        throw new BadCredentialsException('Invalid authentication token');
    }

    $user = $authToken->getUser();
    $pre = new PreAuthenticatedToken(
        $user,
        $authTokenHeader,
        $providerKey,
        $user->getRoles()
        );

    $pre->setAuthenticated(true);

    return $pre;
}

public function supportsToken(TokenInterface $token, $providerKey)
{
    return $token instanceof PreAuthenticatedToken && $token->getProviderKey() === $providerKey;
}

/**
 * Vérifie la validité du token
 */
private function isTokenValid($authToken)
{
    return (time() - $authToken->getCreatedAt()->getTimestamp()) < self::TOKEN_VALIDITY_DURATION;
}

public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{

    throw $exception;
}
}

Вот возвращаемая ошибка, когда я не сообщаю токену:

<!DOCTYPE html>
<html>
<head>
<title>    X-Auth-Token header is required (500 Internal Server Error)

Как я могу получить ответ JSON? Если я пытаюсь сделать возврат нового JsonResponse (массив ("test" => "KO")) (простой пример), я получаю эту ошибку:

<title>    Type error: Argument 1 passed to Symfony\Component\Security\Core\Authentication\AuthenticationProviderManager::authenticate() must be an instance of Symfony\Component\Security\Core\Authentication\Token\TokenInterface, instance of Symfony\Component\HttpFoundation\JsonResponse given, called in /Users/mickaelmercier/Desktop/workspace/api_monblocrecettes/vendor/symfony/symfony/src/Symfony/Component/Security/Http/Firewall/SimplePreAuthenticationListener.php on line 101 (500 Internal Server Error)

1 Ответ

0 голосов
/ 04 ноября 2018

Вы можете создать свой собственный обработчик ошибок. Это прослушиватель событий или подписчик, который прослушивает kernel.exception, когда он добавляет ответ на событие, распространение события останавливается, поэтому обработчик ошибок по умолчанию не сработает.

Это может выглядеть примерно так:

<?php declare(strict_types = 1);

namespace App\EventSubsbscriber;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Symfony\Component\HttpKernel\KernelEvents;

final class ExceptionToJsonResponseSubscriber implements EventSubscriberInterface
{
    public static function getSubscribedEvents()
    {
        return [
            KernelEvents::EXCEPTION => 'onKernelException',
        ];
    }

    public function onKernelException(GetResponseForExceptionEvent $event): void
    {
        // Skip if request is not an API-request
        $request = $event->getRequest();
        if (strpos($request->getPathInfo(), '/api/') !== 0) {
            return;
        }
        $exception = $event->getException();
        $error = [
            'type' => $this->getErrorTypeFromException($exception),
            // Warning! Passing the exception message without checks is insecure.
            // This will potentially leak sensitive information.
            // Do not use this in production!
            'message' => $exception->getMessage(),
        ];
        $response = new JsonResponse($error, $this->getStatusCodeFromException($exception));
        $event->setResponse($response);
    }

    private function getStatusCodeFromException(\Throwable $exception): int
    {
        if ($exception instanceof HttpException) {
            return $exception->getStatusCode();
        }

        return 500;
    }

    private function getErrorTypeFromException(\Throwable $exception): string
    {
        $parts = explode('\\', get_class($exception));

        return end($parts);
    }
}

ApiPlatform предоставляет свой собственный прослушиватель исключений, такой как этот, более продвинутый, на который вы могли бы обратить внимание, если вам нужны "лучшие" ответы на исключения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...