Arreglando 401s con CORS Preflights y Spring Security

1. Información general

En este breve tutorial, aprenderemos cómo resolver el error "La respuesta para la verificación previa tiene un código de estado HTTP no válido 401", que puede ocurrir en aplicaciones que admiten la comunicación entre orígenes y utilizan Spring Security.

Primero, veremos qué son las solicitudes de origen cruzado y luego arreglaremos un ejemplo problemático.

2. Solicitudes de origen cruzado

Las solicitudes de origen cruzado, en resumen, son solicitudes HTTP donde el origen y el destino de la solicitud son diferentes. Este es el caso, por ejemplo, cuando una aplicación web se sirve desde un dominio y el navegador envía una solicitud AJAX a un servidor en otro dominio.

Para administrar las solicitudes de origen cruzado, el servidor debe habilitar un mecanismo particular conocido como CORS o Intercambio de recursos de origen cruzado.

El primer paso en CORS es una solicitud de OPCIONES para determinar si el destino de la solicitud lo admite. Esto se denomina solicitud previa al vuelo.

Luego, el servidor puede responder a la solicitud previa al vuelo con una colección de encabezados:

  • Access-Control-Allow-Origin : Define qué orígenes pueden tener acceso al recurso. Un '*' representa cualquier origen
  • Access-Control-Allow-Methods : indica los métodos HTTP permitidos para solicitudes de origen cruzado
  • Access-Control-Allow-Headers : indica los encabezados de solicitud permitidos para solicitudes de origen cruzado
  • Access-Control-Max-Age : define el tiempo de vencimiento del resultado de la solicitud de verificación previa en caché

Por lo tanto, si la solicitud previa al vuelo no cumple las condiciones determinadas a partir de estos encabezados de respuesta, la solicitud de seguimiento real arrojará errores relacionados con la solicitud de origen cruzado.

Es fácil agregar soporte CORS a nuestro servicio impulsado por Spring, pero si se configura incorrectamente, esta solicitud previa al vuelo siempre fallará con un 401.

3. Creación de una API REST habilitada para CORS

Para simular el problema, primero creemos una API REST simple que admita solicitudes de origen cruzado:

@RestController @CrossOrigin("//localhost:4200") public class ResourceController { @GetMapping("/user") public String user(Principal principal) { return principal.getName(); } }

La anotación @CrossOrigin asegura que nuestras API sean accesibles solo desde el origen mencionado en su argumento.

4. Protección de nuestra API REST

Aseguremos ahora nuestra API REST con Spring Security:

@EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .anyRequest().authenticated() .and() .httpBasic(); } }

En esta clase de configuración, hemos aplicado la autorización a todas las solicitudes entrantes. Como resultado, rechazará todas las solicitudes sin un token de autorización válido.

5. Hacer una solicitud previa al vuelo

Ahora que hemos creado nuestra API REST, intentemos una solicitud previa al vuelo usando curl :

curl -v -H "Access-Control-Request-Method: GET" -H "Origin: //localhost:4200" -X OPTIONS //localhost:8080/user ... < HTTP/1.1 401 ... < WWW-Authenticate: Basic realm="Realm" ... < Vary: Origin < Vary: Access-Control-Request-Method < Vary: Access-Control-Request-Headers < Access-Control-Allow-Origin: //localhost:4200 < Access-Control-Allow-Methods: POST < Access-Control-Allow-Credentials: true < Allow: GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH ...

A partir de la salida de este comando, podemos ver que la solicitud fue denegada con un 401.

Dado que este es un comando curl , no veremos el error "La respuesta para la verificación previa tiene un código de estado HTTP no válido 401" en la salida.

Pero podemos reproducir este error exacto creando una aplicación de interfaz que consume nuestra API REST de un dominio diferente y ejecutándola en un navegador.

6. La solución

No hemos excluido explícitamente las solicitudes de verificación previa de la autorización en nuestra configuración de Spring Security . Recuerde que Spring Security protege todos los puntos finales de forma predeterminada.

Como resultado, nuestra API también espera un token de autorización en la solicitud OPTIONS.

Spring proporciona una solución lista para usar para excluir las solicitudes de OPCIONES de las verificaciones de autorización:

@EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { // ... http.cors(); } }

El método cors () agregará el CorsFilter proporcionado por Spring al contexto de la aplicación que a su vez omite las verificaciones de autorización para las solicitudes OPTIONS.

Ahora podemos probar nuestra aplicación nuevamente y ver que está funcionando.

7. Conclusión

En este breve artículo, hemos aprendido cómo corregir el error "La respuesta para la verificación previa tiene un código de estado HTTP no válido 401" que está vinculado con Spring Security y solicitudes de origen cruzado.

Tenga en cuenta que, con el ejemplo, el cliente y la API deben ejecutarse en diferentes dominios o puertos para recrear el problema. Por ejemplo, podemos asignar el nombre de host predeterminado al cliente y la dirección IP de la máquina a nuestra API REST cuando se ejecuta en una máquina local.

Como siempre, el ejemplo que se muestra en este tutorial se puede encontrar en Github.