Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring security CORS Filter

We added Spring Security to our existing project.

From this moment on we get a 401 No 'Access-Control-Allow-Origin' header is present on the requested resource error from the our server.

That's because no Access-Control-Allow-Origin header is attached to the response. To fix this we added our own filter which is in the Filter chain before the logout filter, but the filter does not apply for our requests.

Our Error:

XMLHttpRequest cannot load http://localhost:8080/getKunden. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin http://localhost:3000 is therefore not allowed access. The response had HTTP status code 401.

Our Security configuration:

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

Our filter

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

Our Application

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

Our filter is registered from spring-boot:

2016-11-04 09:19:51.494 INFO 9704 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'myFilter' to: [/*]

Our generated filterchain:

2016-11-04 09:19:52.729 INFO 9704 --- [ost-startStop-1] o.s.s.web.DefaultSecurityFilterChain : Creating filter chain: 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.HeaderWriterFilter@72aa89c, org.springframework.security.web.csrf.CsrfFilter@4af4df11, com.company.praktikant.MyFilter@5ba65db2, org.springframework.security.web.authentication.logout.LogoutFilter@2330834f, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@396532d1, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@4fc0f1a2, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@2357120f, org.springframework.security.web.session.SessionManagementFilter@10867bfb, org.springframework.security.web.access.ExceptionTranslationFilter@4b8bf1fb, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@42063cf1]

The Response: Response headers

We tried the solution from spring as well but it didn't work! The annotation @CrossOrigin in our controller didn't help either.

Edit 1:

Tried the solution from @Piotr Sołtysiak. The cors filter isn't listed in the generated filter chain and we still get the same error.

2016-11-04 10:22:49.881 INFO 8820 --- [ost-startStop-1] o.s.s.web.DefaultSecurityFilterChain : Creating filter chain: 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.HeaderWriterFilter@3c3ec668, org.springframework.security.web.csrf.CsrfFilter@288460dd, org.springframework.security.web.authentication.logout.LogoutFilter@1c9cd096, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@3990c331, org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter@1e8d4ac1, org.springframework.security.web.authentication.www.BasicAuthenticationFilter@2d61d2a4, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@380d9a9b, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@abf2de3, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@2a5c161b, org.springframework.security.web.session.SessionManagementFilter@3c1fd3e5, org.springframework.security.web.access.ExceptionTranslationFilter@3d7055ef, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@5d27725a]

Btw we are using spring-security version 4.1.3.!

like image 853
Mace Avatar asked Nov 04 '16 08:11

Mace


2 Answers

Since Spring Security 4.1, this is the proper way to make Spring Security support CORS (also needed in 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");     } } 

and:

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

Do not do any of below, which are the wrong way to attempt solving the problem:

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

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

like image 88
Hendy Irawan Avatar answered Sep 22 '22 00:09

Hendy Irawan


Since i had problems with the other solutions (especially to get it working in all browsers, for example edge doesn't recognize "*" as a valid value for "Access-Control-Allow-Methods"), i had to use a custom filter component, which in the end worked for me and did exactly what i wanted to achieve.

@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     }  } 
like image 24
David Eibensteiner Avatar answered Sep 21 '22 00:09

David Eibensteiner