La solicitud CORS POST falla en Chrome, Safari y Firefox

Tengo un Backend completo implementado con seguridad Spring ejecutándose en localhost: 8080, un filtro de inicio de sesión responde a las solicitudes de inicio de sesión con un token colocado en los encabezados, ni siquiera implementé un método de punto final para eso. Todo está hecho por la magia de seguridad de Spring. Por la siguiente línea de código:

protected void configure(HttpSecurity http) throws Exception { // disable caching http.headers().cacheControl(); http.csrf().disable() // disable csrf for our requests. .authorizeRequests() .antMatchers("/").permitAll() .antMatchers("/login").permitAll() .anyRequest().authenticated() .and() // We filter the api/login requests .addFilterBefore(new JWTLoginFilter("/login", authenticationManager()), UsernamePasswordAuthenticationFilter.class) … 

La interfaz es un proyecto de Angularjs Nodejs NPM Bower que ejecuta un servidor http estático en localhost: 8000. En la interfaz hago una solicitud POST simple de la siguiente manera:

  $scope.login = function () { var data = { "username":$scope.username, "password":$scope.password } $http({ url: baseUrl, method: "POST", data: data }).then(function (response) { console.log("###### SUCCESS #####"); var headers = response.headers(); console.log("headers: "+JSON.stringify(headers)); var token = headers.authorization.split(" ")[1]; console.log("token: "+token); $http.defaults.headers.common['Authorization'] = token; $location.path("/view1"); }, function (responseData) { // called asynchronously if an error occurs // or server returns responseData with an error status. console.log("###### FAIL #####"); console.log("Response: "+JSON.stringify(responseData)); $window.alert("Failed to Login"); }); 

Esto funciona como un hechizo en IE (también con solicitudes de curl, wget y python) pero falla miserablemente en Chrome y Safary. Sé que esos navegadores están bloqueando CORS POSTs, dejando la solicitud vacía tan pronto como llegan al backend, de hecho, no veo ningún dato cuando cierro la sesión desde el backend. Probé todas las combinaciones posibles de:

Lado frontal:

1) $ http (método: POST)

2) $ http.post (

3) Agregar indicadores: Access-Control-Allow-Origin, Access-Control-Expose, etc.

4) Agregando todas las combinaciones de encabezado posibles: ‘Contenido – Tipo’: ‘aplicación /

Lado del navegador:

1) Iniciar chrome con la bandera: –disable-web-security

2) Instalación de la extensión de Chrome CORS

Lado de fondo:

1) Spring Security Disable csfr

2) Permiso de seguridad de spring todos

3) Spring Security HttpMethod.OPTION

Nada, NHOTING funcionó para mí!

¿Se me escapa algo?

¿Hay otra forma de enviar solicitudes POST?

EDITAR

Como se discutió, modifiqué las clases de la siguiente manera:

WebSecurityConfig:

  .antMatchers("/login").permitAll() .anyRequest().authenticated() .and() // We filter the api/login requests .addFilterBefore(new JWTLoginFilter("/login", authenticationManager()), UsernamePasswordAuthenticationFilter.class) .addFilterBefore(new CORSFilter(), BasicAuthenticationFilter.class) 

e implementó el CORSfiltro como sugerencia.

También agrego la clase WebConfig como se sugiere:

 @Configuration @EnableWebMvc public class WebConfig extends WebMvcConfigurerAdapter { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**").allowedOrigins("http://localhost:8000") .allowedMethods("PUT", "POST"); } } 

Debido a la cadena vacía que lanza el filtro de inicio de sesión:

com.fasterxml.jackson.databind.JsonMappingException: no hay contenido para asignar debido al final de la entrada

Esto será reparado por la seguridad de spring que negó el acceso.

También intenté mover el servidor frontend a otros puertos y luego a 8000 (4200, 7000, etc.) sin éxito.

Necesitas habilitar el soporte Cors en spring. En su WebConfig puede anular addCorsMappings

 @Configuration @EnableWebMvc public class WebConfig extends WebMvcConfigurerAdapter { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**").allowedOrigins("http://localhost:4200"); //url of where angular is running. } } 

Esto permite cors para toda la aplicación. También puede ser más específico con sus asignaciones, permitiendo encabezados y métodos específicos.

 @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/api/**") .allowedOrigins("http://domain2.com") .allowedMethods("PUT", "DELETE") .allowedHeaders("header1", "header2", "header3"); } 

También puede permitir al usuario @CrossOrgin a nivel de clase y método

 @CrossOrigin(origin = "http://domain2.com", maxAge = 3600) public class ApiController { } 

http://docs.spring.io/spring/docs/current/spring-framework-reference/html/cors.html

https://spring.io/guides/gs/rest-service-cors/

He usado un filtro CORS antes, y funcionó bien:

 public class CORSFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { response.addHeader("Access-Control-Allow-Origin", "*"); if (request.getHeader("Access-Control-Request-Method") != null && "OPTIONS".equals(request.getMethod())) { response.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE"); response.addHeader("Access-Control-Allow-Headers", "Content-Type"); response.addHeader("Access-Control-Max-Age", "1"); } filterChain.doFilter(request, response); } } 

y luego agregue esto a su configuración:

 .addFilterBefore(new CORSFilter()), BasicAuthenticationFilter.class)