Pregunta

Estoy usando fosrestbundle para mi API REST y hasta ahora ha sido una gran herramienta. Utilizo HTTP Basic Auth y en la mayoría de los casos funciona bien. Sin embargo, tengo problemas con el comportamiento de excepción del paquete cuando se envían malas credenciales. Al manipular las excepciones (a través de los controladores de autenticación integrados o la configuración de mapeo de excepciones), el paquete siempre me da una respuesta con el estado HTTP correcto y el contenido JSON/XML similar al de este:

{
   "code": 401,
   "message": "You are not authenticated"
}

Esto está bien, también funciona cuando Sin autenticacion La información se envía en absoluto. Sin embargo, al enviar malas credenciales (por ejemplo, el nombre de usuario desconocido o la contraseña incorrecta) Obtengo el código HTTP 401 credenciales malas (lo cual está bien) con un cuerpo de mensaje vacío. En cambio, hubiera esperado algo similar al JSON anterior.

¿Es un error o un problema de configuración de mi lado? También me encantaría saber cómo el paquete manejan exactamente este tipo de errores de autenticación, desde que anuló el BadCredentialsException'S Código de estado en el codes La sección de la sección de configuración de excepción del paquete parece ser ignorada.

¡Gracias!

¿Fue útil?

Solución

Muy bien, después de profundizar en el código del paquete un poco más, lo descubrí. El problema resulta de la forma en que las credenciales malas se manejan por la impementación de autenticación básica HTTP de Symfony. los 401 Bad Credentials La respuesta es una respuesta personalizada creada por BasicAuthenticationEntryPoint, que es llamado por el BasicAuthenticationListener's handle función, inmediatamente después de un AuthenticationException ha sido arrojado en la misma función. Por lo tanto, no hay forma de atrapar esta excepción con un oyente:

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

    if (false === $username = $request->headers->get('PHP_AUTH_USER', false)) {
        return;
    }

    if (null !== $token = $this->securityContext->getToken()) {
        if ($token instanceof UsernamePasswordToken && $token->isAuthenticated() && $token->getUsername() === $username) {
            return;
        }
    }

    if (null !== $this->logger) {
        $this->logger->info(sprintf('Basic Authentication Authorization header found for user "%s"', $username));
    }

    try {
        $token = $this->authenticationManager->authenticate(new UsernamePasswordToken($username, $request->headers->get('PHP_AUTH_PW'), $this->providerKey));
        $this->securityContext->setToken($token);
    } catch (AuthenticationException $failed) {
        $this->securityContext->setToken(null);

        if (null !== $this->logger) {
            $this->logger->info(sprintf('Authentication request failed for user "%s": %s', $username, $failed->getMessage()));
        }

        if ($this->ignoreFailure) {
            return;
        }

        $event->setResponse($this->authenticationEntryPoint->start($request, $failed));
    }
}

El punto de entrada start La función crea la respuesta personalizada, sin excepciones involucradas:

public function start(Request $request, AuthenticationException $authException = null)
{
    $response = new Response();
    $response->headers->set('WWW-Authenticate', sprintf('Basic realm="%s"', $this->realmName));
    $response->setStatusCode(401, $authException ? $authException->getMessage() : null);

    return $response;
}

El puño if-Lausar en el handle La función anterior también explica por qué funciona en el caso de "no hay credenciales de usuario", ya que en ese caso, el oyente simplemente deja de intentar autenticar al usuario y, por lo tanto, la excepción será lanzada por los oyentes de firewall de Symfony (no estoy seguro de dónde exactamente), así que fosrestbundle's AccessDeniedListener es capaz de atrapar el AuthenticationException y hacer lo suyo.

Otros consejos

Puedes extender AccessDeniedListener y dígale a fosrestbundle que use su propio oyente con el parámetro %fos_rest.access_denied_listener.class%. (definición de servicio)

parameters:
    fos_rest.access_denied_listener.class: Your\Namespace\For\AccessDeniedListener

Luego agregue una verificación adicional para BadCredentialsException y emmit un HttpException con el código/mensaje deseado similar a la comprobación de autenticaciónxception en Línea 70.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top