当我使用AuthTokenAuthenticator(symfony 3)时返回JsonResponse

时间:2018-11-04 10:44:24

标签: json api symfony token

我指定从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(array(“ 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 :(得分:2)

您可以创建自己的错误处理程序。是事件监听器或订阅者,它监听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提供了它自己的异常侦听器,它是更高级的,如果需要“更好的”异常响应,可以进行研究。