Spring Data Rest e Cors

Sto sviluppando un’applicazione Spring Boot con un’interfaccia Rest e un dardo fronted.

XMLHttpRequest esegue una richiesta OPTIONS che viene gestita completamente corretta. Dopo questo, la richiesta finale GET (“/ products”) viene emessa e non riesce:

Nessuna intestazione ‘Access-Control-Allow-Origin’ è presente sulla risorsa richiesta. L” http: // localhost: 63343 ‘ di origine non è quindi consentito l’accesso.

Dopo alcune operazioni di debug, ho trovato quanto segue: AbstractHandlerMapping.corsConfiguration è popolato per tutte le sottoclassi tranne RepositoryRestHandlerMapping. In RepositoryRestHandlerMapping nessun corsConfiguration è presente / impostato al momento della creazione e quindi non verrà riconosciuto come cors percorso / risorsa.
=> Nessuna intestazione CORS collegata
Potrebbe essere il problema? Come posso impostarlo?

Classi di configurazione:

@Configuration public class RestConfiguration extends RepositoryRestMvcConfiguration { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**").allowCredentials(false).allowedOrigins("*").allowedMethods("PUT", "POST", "GET", "OPTIONS", "DELETE").exposedHeaders("Authorization", "Content-Type"); } ... } 

Ho anche provato a impostare il Cors per annotazione:

 @CrossOrigin( methods = RequestMethod.GET, allowCredentials = "false") public interface ProductRepository extends CrudRepository { } 

Intestazioni di richieste non elaborate:

 GET /products HTTP/1.1 Host: localhost:8080 Connection: keep-alive Cache-Control: max-age=0 authorization: Basic dXNlcjpwYXNzd29yZA== User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/43.0.2357.130 Chrome/43.0.2357.130 Safari/537.36 Content-Type: application/json Accept: */* Referer: http://localhost:63343/inventory-web/web/index.html Accept-Encoding: gzip, deflate, sdch Accept-Language: de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4 

Intestazioni di risposta non elaborate:

 HTTP/1.1 200 OK Server: Apache-Coyote/1.1 Content-Type: application/hal+json;charset=UTF-8 Transfer-Encoding: chunked Date: Thu, 30 Jul 2015 15:58:03 GMT 

Versioni utilizzate: Spring Boot 1.3.0.M2 Spring 4.2.0.RC2

Cosa mi manca?

Infatti, prima di Spring Data REST 2.6 (Ingalls) solo HandlerMapping istanze di HandlerMapping create da Spring MVC WebMvcConfigurationSupport e i controller annotati con @CrossOrigin erano CORS consapevoli.

Ma ora che DATAREST-573 è stato corretto, RepositoryRestConfiguration espone ora un getCorsRegistry() per l’installazione globale e @CrossOrigin annotazioni @CrossOrigin sui repository vengono riconosciute, quindi questo è l’approccio consigliato. Vedi https://stackoverflow.com/a/42403956/1092077 risposta per esempi concreti.

Per le persone che devono attenersi a Spring Data REST 2.5 (Hopper) o versioni precedenti, penso che la soluzione migliore sia utilizzare un approccio basato su filtri. Ovviamente potresti utilizzare Tomcat, Jetty o questo , ma CorsFilter che Spring Framework 4.2 fornisce anche un CorsFilter che utilizza la stessa logica di elaborazione CORS che si @CrossOrigin e addCorsMappings(CorsRegistry registry) . UrlBasedCorsConfigurationSource un’istanza UrlBasedCorsConfigurationSource al parametro del costruttore CorsFilter , è ansible ottenere facilmente qualcosa di potente come il supporto globale CORS nativo di Spring.

Se stai usando Spring Boot (che supporta i bean Filter ), potrebbe essere qualcosa del tipo:

 @Configuration public class RestConfiguration { @Bean public FilterRegistrationBean corsFilter() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); CorsConfiguration config = new CorsConfiguration().applyPermitDefaultValues(); source.registerCorsConfiguration("/**", config); FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source)); bean.setOrder(0); return bean; } } 

Da quando il treno Ingalls è stato realizzato , il supporto di CORS in Spring Data è ora attivo . Ci sono due modi per affrontare:

  1. L’annotazione @CrossOrigin con l’indicazione delle origins , dei methods e dei allowedHeaders su un’interfaccia @RepositoryRestResource .

     @CrossOrigin(...) @RepositoryRestResource public interface PageRepository extends CrudRepository { ... } 
  2. Una configurazione globale con RepositoryRestConfiguration all’interno di una class @Configuration . Contrassegnare i repository con @CrossOrigin non è necessario.

     @Configuration public class GlobalRepositoryRestConfigurer extends RepositoryRestConfigurerAdapter { @Override public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) { config.getCorsRegistry() .addMapping(CORS_BASE_PATTERN) .allowedOrigins(ALLOWED_ORIGINS) .allowedHeaders(ALLOWED_HEADERS) .allowedMethods(ALLOWED_METHODS); } } 

Per qualche motivo l’approccio suggerito nella risposta accettata sopra non ha funzionato per me dopo l’aggiornamento da Spring Boot 1.5.2 a 1.5.6.

Come sottolineato anche dal commento di @ BigDong, l’eccezione che ho ottenuto è stata:

BeanInstantiationException: Imansible istanziare [javax.servlet.Filter]: il metodo Factory ‘springSecurityFilterChain’ ha gettato un’eccezione; l’eccezione annidata è org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean denominato ‘corsFilter’ dovrebbe essere di tipo ‘org.springframework.web.filter.CorsFilter’, ma in realtà era di tipo ‘org.springframework.boot.web .servlet.FilterRegistrationBean

Ecco quindi cosa mi è venuto in mente per ottenere una configurazione CORS “globale” per tutti gli endpoint nella nostra API REST, indipendentemente dal fatto che siano implementati utilizzando Spring Data Rest o Spring MVC, con tutti gli endpoint protetti da Spring Security.

Non ero in grado di colbind un CorsFilter alla pipeline di richieste al punto giusto, quindi ho configurato SDR e MVC separatamente, tuttavia utilizzando la stessa configurazione per CorsRegistry tramite questo helper:

 public static void applyFullCorsAllowedPolicy(CorsRegistry registry) { registry.addMapping("/**") // .allowedOrigins("*") // .allowedMethods("OPTIONS", "HEAD", "GET", "PUT", "POST", "DELETE", "PATCH") // .allowedHeaders("*") // .exposedHeaders("WWW-Authenticate") // .allowCredentials(true) .maxAge(TimeUnit.DAYS.toSeconds(1)); } 

E poi per MVC:

 @Configuration @EnableWebSecurity(debug = true) @EnableGlobalMethodSecurity(prePostEnabled = true) public class CustomWebSecurityConfiguration extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { // enables CORS as per // https://docs.spring.io/spring-security/site/docs/current/reference/html/cors.html#cors http.cors() .and() // ... } @Bean public WebMvcConfigurer corsConfigurer() { return new WebMvcConfigurerAdapter() { @Override public void addCorsMappings(CorsRegistry registry) { applyFullCorsAllowedPolicy(registry); } }; } } 

E poi per SDR:

 public class CustomRepositoryRestMvcConfiguration extends RepositoryRestConfigurerAdapter { @Override public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) { config.setReturnBodyOnCreate(true); config.setReturnBodyForPutAndPost(true); config.setReturnBodyOnUpdate(true); config.setMaxPageSize(250); config.setDefaultPageSize(50); config.setDefaultMediaType(MediaTypes.HAL_JSON); config.useHalAsDefaultJsonMediaType(true); CustomWebSecurityConfiguration.applyFullCorsAllowedPolicy(config.getCorsRegistry()); } 

Ecco qualche ulteriore riferimento sull’argomento che mi ha aiutato a trovare questa risposta: