java spring spring-security spring-boot cors

java - Filtro de seguridad de primavera CORS



spring spring-security (9)

Agregamos Spring Security a nuestro proyecto existente.

A partir de este momento obtenemos un No ''Access-Control-Allow-Origin'' header is present on the requested resource 401 No ''Access-Control-Allow-Origin'' header is present on the requested resource error de No ''Access-Control-Allow-Origin'' header is present on the requested resource de nuestro servidor.

Esto se debe a que no se adjunta ningún encabezado Access-Control-Allow-Origin a la respuesta. Para solucionar esto, agregamos nuestro propio filtro que se encuentra en la cadena Filter antes del filtro de cierre de sesión, pero el filtro no se aplica a nuestras solicitudes.

Nuestro error:

XMLHttpRequest no puede cargar http://localhost:8080/getKunden . No hay encabezado ''Access-Control-Allow-Origin'' presente en el recurso solicitado. Origen http://localhost:3000 por lo tanto, no se permite el acceso. La respuesta tenía el código de estado HTTP 401.

Nuestra configuración de seguridad:

@EnableWebSecurity @Configuration @ComponentScan("com.company.praktikant") public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Autowired private MyFilter filter; @Override public void configure(HttpSecurity http) throws Exception { final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); final CorsConfiguration config = new CorsConfiguration(); config.addAllowedOrigin("*"); config.addAllowedHeader("*"); config.addAllowedMethod("GET"); config.addAllowedMethod("PUT"); config.addAllowedMethod("POST"); source.registerCorsConfiguration("/**", config); http.addFilterBefore(new MyFilter(), LogoutFilter.class).authorizeRequests() .antMatchers(HttpMethod.OPTIONS, "/*").permitAll(); } @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { } }

Nuestro filtro

@Component public class MyFilter extends OncePerRequestFilter { @Override public void destroy() { } private String getAllowedDomainsRegex() { return "individual / customized Regex"; } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { final String origin = "http://localhost:3000"; response.addHeader("Access-Control-Allow-Origin", origin); response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS"); response.setHeader("Access-Control-Allow-Credentials", "true"); response.setHeader("Access-Control-Allow-Headers", "content-type, x-gwt-module-base, x-gwt-permutation, clientid, longpush"); filterChain.doFilter(request, response); } }

Nuestra aplicación

@SpringBootApplication public class Application { public static void main(String[] args) { final ApplicationContext ctx = SpringApplication.run(Application.class, args); final AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(); annotationConfigApplicationContext.register(CORSConfig.class); annotationConfigApplicationContext.refresh(); } }

Nuestro filtro está registrado desde spring-boot:

2016-11-04 09: 19: 51.494 INFO 9704 --- [ost-startStop-1] osbwservlet.FilterRegistrationBean: Filtro de mapeo: ''myFilter'' a: [/ *]

Nuestra cadena de filtros generada:

2016-11-04 09: 19: 52.729 INFO 9704 --- [ost-startStop-1] ossweb.DefaultSecurityFilterChain: Creando cadena de filtros: org.springframework.security.web.util.matcher.AnyRequestMatcher@1, [org.springframework .security.web.context.request.async.WebAsyncManagerIntegrationFilter @ 5d8c5a8a, org.springframework.security.web.context.SecurityContextPersistenceFilter@7d6938f, org.springframework.security.web.header.Heater. .csrf.CsrfFilter @ 4af4df11, com.company.praktikant.MyFilter@5ba65db2, org.springframework.security.web.authentication.logout.LogoutFilter@2330834f, org.springframework.security.web.savedrequest.RequestCategoría. .security.web.servletapi.SecurityContextHolderAwareRequestFilter @ 4fc0f1a2, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@2357120f, org.springframework.security.web.session.SessionManagement. nslationFilter @ 4b8bf1fb, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@42063cf1]

La respuesta: encabezados de respuesta

¡También probamos la solución desde la primavera pero no funcionó! La anotación @CrossOrigin en nuestro controlador tampoco ayudó.

Editar 1:

Probé la solución de @Piotr Sołtysiak. El filtro cors no aparece en la cadena de filtros generada y todavía recibimos el mismo error.

2016-11-04 10: 22: 49.881 INFO 8820 --- [ost-startStop-1] ossweb.DefaultSecurityFilterChain: Creando una cadena de filtro: org.springframework.security.web.util.matcher.AnyRequestMatcher@1, [org.springframework .security.web.context.request.async.WebAsyncManagerIntegrationFilter @ 4c191377, org.springframework.security.web.context.SecurityContextPersistenceFilter@28bad32a, org.springframework.security.web.header.HeaderWriter. .csrf.CsrfFilter @ 288460dd, org.springframework.security.web.authentication.logout.LogoutFilter@1c9cd096, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@3990.33.f.ejemplo.ejemplo. @ 1e8d4ac1, org.springframework.security.web.authentication.www.BasicAuthenticationFilter@2d61d2a4, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@380d9a9b, org.springframework.security.sepi. questFilter @ abf2de3, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@2a5c161b, org.springframework.security.web.session.SessionManagementFilter@3c1fd3e5, org.springframework.acf. security.web.access.intercept.FilterSecurityInterceptor@5d27725a]

Por cierto, estamos utilizando Spring-Security versión 4.1.3.!


  1. No necesitas:

    @Configuration @ComponentScan("com.company.praktikant")

    @EnableWebSecurity ya tiene @Configuration , y no puedo imaginar por qué pones a @ComponentScan allí.

  2. Sobre el filtro CORS, simplemente pondría esto:

    @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(0); return bean; }

    En SecurityConfiguration clase y eliminar configurar y configurar métodos globales. No necesita configurar orgins, encabezados y métodos allowde dos veces. Especialmente si pones diferentes propiedades en el filtro y la configuración de seguridad de primavera :)

  3. Según lo anterior, su clase "MyFilter" es redundante.

  4. También puedes eliminar esos:

    final AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(); annotationConfigApplicationContext.register(CORSConfig.class); annotationConfigApplicationContext.refresh();

    De la clase de aplicación.

  5. Al final, un pequeño consejo: no está relacionado con la pregunta. No quieres poner verbos en URI. En lugar de http://localhost:8080/getKunden , debe usar el método HTTP GET en http://localhost:8080/kunden resource. Puede obtener información sobre las mejores prácticas para el diseño RESTful api aquí: http://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api


Con Spring Security en Spring Boot 2 para configurar CORS a nivel mundial (p. Ej., Todas las solicitudes de desarrollo habilitadas) puede hacer:

@Bean protected CorsConfigurationSource corsConfigurationSource() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues()); return source; } @Override protected void configure(HttpSecurity http) throws Exception { http.cors() .and().authorizeRequests() .anyRequest().permitAll() .and().csrf().disable(); }


Dado que tuve problemas con las otras soluciones (especialmente para que funcione en todos los navegadores, por ejemplo, edge no reconoce "*" como un valor válido para "Access-Control-Allow-Methods"), tuve que usar un personalizado componente de filtro, que al final funcionó para mí e hizo exactamente lo que quería lograr.

@SpringBootApplication public class Application { public static void main(String[] args) { final ApplicationContext ctx = SpringApplication.run(Application.class, args); } @Bean public WebMvcConfigurer corsConfigurer() { return new WebMvcConfigurerAdapter() { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**").allowedOrigins("http://localhost:3000"); } }; } }


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 haga nada de lo siguiente, 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, acabo de agregar esta clase y uso @EnableAutConfiguration

package com.package.filter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import org.springframework.web.filter.GenericFilterBean; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @Component public class SimpleCORSFilter extends GenericFilterBean { /** * The Logger for this class. */ private final Logger logger = LoggerFactory.getLogger(this.getClass()); @Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { logger.info("> doFilter"); HttpServletResponse response = (HttpServletResponse) resp; response.setHeader("Access-Control-Allow-Origin", "*"); response.setHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE"); response.setHeader("Access-Control-Max-Age", "3600"); response.setHeader("Access-Control-Allow-Headers", "Authorization, Content-Type"); //response.setHeader("Access-Control-Allow-Credentials", "true"); chain.doFilter(req, resp); logger.info("< doFilter"); }

}


En muchos lugares, veo la respuesta que necesita agregar este código:

@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(0); return bean; }

pero en mi caso, arroja una excepción de tipo de clase inesperada. corsFilter() requiere el tipo CorsFilter , así que he hecho estos cambios y puse esta definición de bean en mi configuración y todo está bien ahora.

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


Ok, después de más de 2 días de búsqueda, finalmente solucionamos el problema. Eliminamos todos nuestros filtros y configuraciones y, en su lugar, utilizamos estas 5 líneas de código en la clase de aplicación.

@Component @Order(Ordered.HIGHEST_PRECEDENCE) public class CorsFilter implements Filter { public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletResponse response = (HttpServletResponse) res; HttpServletRequest request = (HttpServletRequest) req; response.setHeader("Access-Control-Allow-Origin", "*"); response.setHeader("Access-Control-Allow-Credentials", "true"); response.setHeader("Access-Control-Allow-Methods", "ACL, CANCELUPLOAD, CHECKIN, CHECKOUT, COPY, DELETE, GET, HEAD, LOCK, MKCALENDAR, MKCOL, MOVE, OPTIONS, POST, PROPFIND, PROPPATCH, PUT, REPORT, SEARCH, UNCHECKOUT, UNLOCK, UPDATE, VERSION-CONTROL"); response.setHeader("Access-Control-Max-Age", "3600"); response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Key, Authorization"); if ("OPTIONS".equalsIgnoreCase(request.getMethod())) { response.setStatus(HttpServletResponse.SC_OK); } else { chain.doFilter(req, res); } } public void init(FilterConfig filterConfig) { // not needed } public void destroy() { //not needed } }


Para los programas ya implementados y que no pueden permitirse los cambios de código (por ejemplo, agregar / actualizar la seguridad de primavera), agregar un proxy simple es una solución: https://.com/a/49827300/1758194


Según la documentación del filtro CORS :

"Spring MVC proporciona soporte preciso para la configuración de CORS a través de anotaciones en los controladores. Sin embargo, cuando se usa con Spring Security, es recomendable confiar en el CorsFilter incorporado que debe pedirse antes de la cadena de filtros de Spring Security".

Algo como esto permitirá el acceso GET a /ajaxUri :

import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.http.HttpMethod; import org.springframework.stereotype.Component; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfigurationSource; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; import java.util.Arrays; @Component @Order(Ordered.HIGHEST_PRECEDENCE) public class AjaxCorsFilter extends CorsFilter { public AjaxCorsFilter() { super(configurationSource()); } private static UrlBasedCorsConfigurationSource configurationSource() { CorsConfiguration config = new CorsConfiguration(); // origins config.addAllowedOrigin("*"); // when using ajax: withCredentials: true, we require exact origin match config.setAllowCredentials(true); // headers config.addAllowedHeader("x-requested-with"); // methods config.addAllowedMethod(HttpMethod.OPTIONS); config.addAllowedMethod(HttpMethod.GET); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/startAsyncAuthorize", config); source.registerCorsConfiguration("/ajaxUri", config); return source; } }

Por supuesto, su configuración de SpringSecurity debe permitir el acceso al URI con los métodos enumerados. Ver la respuesta de @Hendy Irawan.