Spring Data Rest y Cors

Estoy desarrollando una aplicación Spring Boot con una interfaz Rest y un dardo frontal.

XMLHttpRequest ejecuta una solicitud de OPCIONES que se maneja totalmente correcta. Después de esto, se emite la solicitud final GET (“/ productos”) y falla:

Ningún encabezado ‘Access-Control-Allow-Origin’ está presente en el recurso solicitado. El origen ‘ http: // localhost: 63343 ‘ no está, por lo tanto, permitido.

Después de algunas depuraciones, he encontrado lo siguiente: AbstractHandlerMapping.corsConfiguration se completa para todas las Subclases, excepto RepositoryRestHandlerMapping. En RepositoryRestHandlerMapping no corsConfiguration está presente / configurado en el momento de la creación y por lo tanto no se reconocerá como cors path / resource.
=> No hay encabezados CORS adjuntos
¿Podría ser el problema? ¿Cómo puedo configurarlo?

Clases de configuración:

@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"); } ... } 

Incluso traté de establecer el Cors por anotación:

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

Encabezados de solicitud sin procesar:

 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 

Encabezados de respuesta sin formato:

 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 

Versiones utilizadas: Spring Boot 1.3.0.M2 Spring 4.2.0.RC2

¿Qué extraño?

De hecho, antes de Spring Data REST 2.6 (Ingalls), solo HandlerMapping instancias de HandlerMapping creadas por Spring MVC WebMvcConfigurationSupport y los controladores anotados con @CrossOrigin eran conscientes de CORS.

Pero ahora que se ha corregido el DATAREST-573 , RepositoryRestConfiguration ahora expone un getCorsRegistry() para la configuración global y @CrossOrigin anotaciones @CrossOrigin en los repositorys también se reconocen, por lo que este es el enfoque recomendado. Ver https://stackoverflow.com/a/42403956/1092077 respuesta para ejemplos concretos.

Para las personas que tienen que apegarse a Spring Data REST 2.5 (Hopper) o versiones anteriores, creo que la mejor solución es usar un enfoque basado en filtros. Obviamente, puede utilizar Tomcat, Jetty o este , pero tenga en cuenta que Spring Framework 4.2 también proporciona un CorsFilter que utiliza la misma lógica de procesamiento CORS que se @CrossOrigin y addCorsMappings(CorsRegistry registry) . Al pasar una instancia de CorsFilter parámetro constructor de CorsFilter , puede obtener fácilmente algo tan poderoso como el soporte global CORS nativo de Spring.

Si está utilizando Spring Boot (que es compatible con Filter beans), podría ser algo como:

 @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; } } 

Desde que se realizó el tren de Ingalls , ahora está activado el soporte de CORS en Spring Data. Hay dos formas de tratar:

  1. La anotación @CrossOrigin con la especificación de origins , methods y allowedHeaders en una interfaz @RepositoryRestResource .

     @CrossOrigin(...) @RepositoryRestResource public interface PageRepository extends CrudRepository { ... } 
  2. Una configuración global con RepositoryRestConfiguration dentro de una clase @Configuration . Marcar repositorys por @CrossOrigin no es necesario entonces.

     @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); } } 

Por alguna razón, el enfoque sugerido en la respuesta aceptada anteriormente no funcionó para mí después de actualizar de Spring Boot 1.5.2 a 1.5.6.

Como también señaló el comentario de @ BigDong, la excepción que obtuve fue:

BeanInstantiationException: error al instanciar [javax.servlet.Filter]: el método de fábrica ‘springSecurityFilterChain’ arrojó una excepción; la excepción anidada es org.springframework.beans.factory.BeanNotOfRequiredTypeException: se espera que Bean llamado ‘corsFilter’ sea del tipo ‘org.springframework.web.filter.CorsFilter’ pero en realidad era del tipo ‘org.springframework.boot.web .servlet.FilterRegistrationBean

Así que esto es lo que surgió para obtener una configuración CORS “global” para todos los puntos finales en nuestra API REST, ya sea que se implementen utilizando Spring Data Rest o Spring MVC, con todos los puntos finales protegidos por Spring Security.

No pude conectar un CorsFilter a la tubería de solicitud en el punto correcto, así que en lugar de eso configuré SDR y MVC por separado, sin embargo, CorsRegistry la misma configuración para su CorsRegistry través de este ayudante:

 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)); } 

Y luego para 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); } }; } } 

Y luego para 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()); } 

Aquí hay alguna referencia más sobre el tema que me ayudó a llegar a esta respuesta: