Correzione dei 401 con CORS Preflight e Spring Security

1. Panoramica

In questo breve tutorial, impareremo come risolvere l'errore "La risposta per il preflight ha un codice di stato HTTP 401 non valido", che può verificarsi nelle applicazioni che supportano la comunicazione tra le origini e utilizzano Spring Security.

Per prima cosa, vedremo quali sono le richieste cross-origin e poi correggeremo un esempio problematico.

2. Richieste Cross-Origin

Le richieste cross-origin, in breve, sono richieste HTTP in cui l'origine e la destinazione della richiesta sono diverse. Questo è il caso, ad esempio, quando un'applicazione web viene servita da un dominio e il browser invia una richiesta AJAX a un server in un altro dominio.

Per gestire le richieste cross-origin, il server deve abilitare un particolare meccanismo noto come CORS o Cross-Origin Resource Sharing.

Il primo passaggio in CORS è una richiesta OPTIONS per determinare se la destinazione della richiesta la supporta. Questa è chiamata richiesta pre-volo.

Il server può quindi rispondere alla richiesta pre-volo con una raccolta di intestazioni:

  • Access-Control-Allow-Origin : definisce quali origini possono avere accesso alla risorsa. Un '*' rappresenta qualsiasi origine
  • Access-Control-Allow-Methods : indica i metodi HTTP consentiti per le richieste cross-origin
  • Access-Control-Allow-Headers : indica le intestazioni delle richieste consentite per le richieste cross-origin
  • Access-Control-Max-Age : definisce l'ora di scadenza del risultato della richiesta di verifica preliminare memorizzata nella cache

Quindi, se la richiesta pre-volo non soddisfa le condizioni determinate da queste intestazioni di risposta, l'effettiva richiesta di follow-up genererà errori relativi alla richiesta cross-origin.

È facile aggiungere il supporto CORS al nostro servizio a molla, ma se configurato in modo errato, questa richiesta pre-volo fallirà sempre con un 401.

3. Creazione di un'API REST abilitata per CORS

Per simulare il problema, creiamo prima una semplice API REST che supporti le richieste cross-origin:

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

L' annotazione @CrossOrigin assicura che le nostre API siano accessibili solo dall'origine menzionata nel suo argomento.

4. Protezione della nostra API REST

Proteggiamo ora la nostra 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(); } }

In questa classe di configurazione, abbiamo applicato l'autorizzazione a tutte le richieste in arrivo. Di conseguenza, rifiuterà tutte le richieste senza un token di autorizzazione valido.

5. Effettuare una richiesta pre-volo

Ora che abbiamo creato la nostra API REST, proviamo una richiesta pre-volo utilizzando 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 ...

Dall'output di questo comando, possiamo vedere che la richiesta è stata negata con un 401.

Poiché questo è un comando curl , non vedremo l'errore "La risposta per il preflight ha un codice di stato HTTP 401 non valido" nell'output.

Ma possiamo riprodurre questo errore esatto creando un'applicazione front-end che utilizza la nostra API REST da un dominio diverso ed eseguendola in un browser.

6. La soluzione

Non abbiamo escluso esplicitamente le richieste di verifica preliminare dall'autorizzazione nella nostra configurazione Spring Security . Ricorda che Spring Security protegge tutti gli endpoint per impostazione predefinita.

Di conseguenza, la nostra API si aspetta anche un token di autorizzazione nella richiesta OPTIONS.

Spring fornisce una soluzione pronta all'uso per escludere le richieste OPTIONS dai controlli di autorizzazione:

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

Il metodo cors () aggiungerà il CorsFilter fornito da Spring al contesto dell'applicazione, che a sua volta ignora i controlli di autorizzazione per le richieste OPTIONS.

Ora possiamo testare di nuovo la nostra applicazione e vedere che funziona.

7. Conclusione

In questo breve articolo, abbiamo imparato come correggere l'errore "La risposta per il preflight ha un codice di stato HTTP 401 non valido" che è collegato a Spring Security e alle richieste cross-origin.

Notare che, con l'esempio, il client e l'API dovrebbero essere eseguiti su domini o porte differenti per ricreare il problema. Ad esempio, possiamo mappare il nome host predefinito al client e l'indirizzo IP della macchina alla nostra API REST durante l'esecuzione su una macchina locale.

Come sempre, l'esempio mostrato in questo tutorial può essere trovato su Github.