Problema con CORS: no hay ningún encabezado ‘Access-Control-Allow-Origin’ en el recurso solicitado

Creé dos aplicaciones web: aplicaciones de cliente y servicio.
La interacción entre el cliente y las aplicaciones de servicio va bien cuando se implementan en la misma instancia de Tomcat.
Pero cuando las aplicaciones se implementan en instancias separadas de Tomcat (máquinas diferentes), recibo el siguiente error cuando solicito la aplicación de servicio enviada.

Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8080' is therefore not allowed access. The response had HTTP status code 401 

Mi aplicación de cliente usa JQuery, HTML5 y Bootstrap.

La llamada AJAX se realiza para el servicio como se muestra a continuación:

 var auth = "Basic " + btoa({usname} + ":" + {password}); var service_url = {serviceAppDomainName}/services; if($("#registrationForm").valid()){ var formData = JSON.stringify(getFormData(registrationForm)); $.ajax({ url: service_url+action, dataType: 'json', async: false, type: 'POST', headers:{ "Authorization":auth }, contentType: 'application/json', data: formData, success: function(data){ //success code }, error: function( jqXhr, textStatus, errorThrown ){ alert( errorThrown ); }); } 

Mi aplicación de servicio utiliza Spring MVC, Spring Data JPA y Spring Security.

He incluido la clase CorsConfiguration como se muestra a continuación:

CORSConfig.java :

 @Configuration @EnableWebMvc public class CORSConfig extends WebMvcConfigurerAdapter { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("*"); } } 

SecurityConfig.java :

 @Configuration @EnableGlobalMethodSecurity(prePostEnabled = true) @EnableWebSecurity @ComponentScan(basePackages = "com.services", scopedProxy = ScopedProxyMode.INTERFACES) public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired @Qualifier("authenticationService") private UserDetailsService userDetailsService; @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService); auth.authenticationProvider(authenticationProvider()); } @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/login").permitAll() .anyRequest().fullyAuthenticated(); http.httpBasic(); http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); http.csrf().disable(); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean public DaoAuthenticationProvider authenticationProvider() { DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider(); authenticationProvider.setUserDetailsService(userDetailsService); authenticationProvider.setPasswordEncoder(passwordEncoder()); return authenticationProvider; } } 

Dependencias de Spring Spring:

   org.springframework.security spring-security-config 3.2.3.RELEASE   org.springframework.security spring-security-web 3.2.3.RELEASE  

Estoy usando el servidor Apache Tomcat para la implementación.

La solicitud de verificación previa de CORS utiliza OPTIONS HTTP sin credenciales, consulte el Intercambio de recursos de origen cruzado :

De lo contrario, realice una solicitud de verificación previa . Obtenga la URL de solicitud del origen de origen de origen utilizando origen de referencia para anular la fuente de referencia con el indicador de redirección manual y el indicador de cookies de locking establecido, utilizando el método OPCIONES y con las siguientes restricciones adicionales:

  • Incluya un encabezado de Access-Control-Request-Method con un valor de campo de encabezado como método de solicitud (incluso si es un método simple).
  • Si los encabezados de solicitud de autor no están vacíos, incluya un encabezado Access-Control-Request-Headers con un campo de encabezado como una lista separada por comas de los nombres de campo de encabezado de los encabezados de solicitud de autor en orden lexicográfico, cada uno convertido a minúscula ASCII (incluso cuando uno o más son un encabezado simple).
  • Excluya los encabezados de solicitud del autor.
  • Excluir credenciales de usuario
  • Excluir el cuerpo de entidad de solicitud.

Debe permitir el acceso anónimo para OPTIONS HTTP.

Su código modificado (y simplificado):

 @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .andMatchers(HttpMethod.OPTIONS, "/**").permitAll() .antMatchers("/login").permitAll() .anyRequest().fullyAuthenticated() .and() .httpBasic() .and() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .csrf().disable(); } 

Desde Spring Security 4.2.0 puede usar el soporte integrado, consulte Spring Security Reference :

19. CORS

Spring Framework proporciona soporte de primera clase para CORS. CORS debe procesarse antes de Spring Security porque la solicitud previa al vuelo no contendrá ninguna cookie (es decir, el JSESSIONID ). Si la solicitud no contiene cookies y Spring Security es el primero, la solicitud determinará que el usuario no está autenticado (ya que no hay cookies en la solicitud) y la rechazará.

La forma más fácil de garantizar que CORS se maneje primero es usar CorsFilter . Los usuarios pueden integrar CorsFilter con Spring Security al proporcionar un CorsConfigurationSource usando lo siguiente:

 @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http // by default uses a Bean by the name of corsConfigurationSource .cors().and() ... } @Bean CorsConfigurationSource corsConfigurationSource() { CorsConfiguration configuration = new CorsConfiguration(); configuration.setAllowedOrigins(Arrays.asList("https://example.com")); configuration.setAllowedMethods(Arrays.asList("GET","POST")); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", configuration); return source; } } 

Desde Spring Security 4.1, esta es la forma correcta de hacer que Spring Security sea compatible con CORS (también se necesita en Spring Boot 1.4 / 1.5):

 @Configuration public class WebConfig extends WebMvcConfigurerAdapter { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedMethods("HEAD", "GET", "PUT", "POST", "DELETE", "PATCH"); } } 

y:

 @Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { // http.csrf().disable(); http.cors(); } @Bean public CorsConfigurationSource corsConfigurationSource() { final CorsConfiguration configuration = new CorsConfiguration(); configuration.setAllowedOrigins(ImmutableList.of("*")); configuration.setAllowedMethods(ImmutableList.of("HEAD", "GET", "POST", "PUT", "DELETE", "PATCH")); // setAllowCredentials(true) is important, otherwise: // The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. configuration.setAllowCredentials(true); // setAllowedHeaders is important! Without it, OPTIONS preflight request // will fail with 403 Invalid CORS request configuration.setAllowedHeaders(ImmutableList.of("Authorization", "Cache-Control", "Content-Type")); final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", configuration); return source; } } 

No hagas ninguna de las siguientes, que son la forma incorrecta de intentar resolver el problema:

  • http.authorizeRequests().antMatchers(HttpMethod.OPTIONS, "/**").permitAll();
  • web.ignoring().antMatchers(HttpMethod.OPTIONS);

Referencia: http://docs.spring.io/spring-security/site/docs/4.2.x/reference/html/cors.html

En mi caso, tengo el servidor de recursos con seguridad OAuth habilitada y ninguna de las soluciones anteriores no funcionó. Después de algunas depuraciones y googlear averiguaron por qué.

 @Bean public FilterRegistrationBean corsFilter() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); CorsConfiguration config = new CorsConfiguration(); config.setAllowCredentials(true); config.addAllowedOrigin("*"); config.addAllowedHeader("*"); config.addAllowedMethod("*"); source.registerCorsConfiguration("/**", config); FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source)); bean.setOrder(Ordered.HIGHEST_PRECEDENCE); return bean; } 

Básicamente en este ejemplo Ordered.HIGHEST_PRECEDENCE es la clave!

https://github.com/spring-projects/spring-security-oauth/issues/938

Varias dependencias pom agregan diferentes tipos de filtros y, por lo tanto, podríamos tener problemas basados ​​en el orden.