Spring Security CORS Filter

We have added Spring Security

to our existing project.

From now on, we receive a 401 error No 'Access-Control-Allow-Origin' header is present on the requested resource

from our server.

This is because there is no header attached to the answer Access-Control-Allow-Origin

. To fix this, we added a custom filter that is chained Filter

before the exit filter, but the filter is not applied to our queries.

Our mistake:

XMLHttpRequest cannot load http://localhost:8080/getKunden

. The requested resource does not have an "Access-Control-Allow-Origin" header. Origin http://localhost:3000

is therefore not allowed. The response was HTTP 401 status code.

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] osbwservlet.FilterRegistrationBean: Display filter: 'myFilter' to: [/ *]

Our generated filter chain:

2016-11-04 09: 19: 52.729 INFO 9704 --- [ost-startStop-1] ossweb.DefaultSecurityFilterChain: Create filter chain: org.springframework.security.web.util.matcher.AnyRequestMatcher @ 1, [org.springframework .secu rity.web.context.request.async.WebAsyncManagerIntegrationFilter @ 5d8c5a8a, org.spring framework.security.web.context.SecurityContextPersistenceFilter @ 7d6938f, org.springframework.security.web.header.HeaderWriterFilter@72aa8 9c, 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.springfram ework.security.web.authentication.AnonymousAuthenticationFilter @ 2357120f, o rg.springframework.security.web.session.Session @ ManagementFramework.security.web.session.Session @ManagementFramework. E xceptionTranslationFilter @ 4b8bf1fb, org.springfr amework.security.web.access.intercept.FilterSecurityInterceptor @ 42063cf1]

Answer: Answer headers

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

Edit 1:

Tried the solution from @Piotr Sołtysiak. The cors filter is not 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] ossweb.DefaultSecurityFilterChain: Create filter chain: org.springframework.security.web.util.matcher.AnyRequestMatcher @ 1, [org.springframework .secu rity.web.context.request.async.WebAsyncManagerIntegrationFilter @ 4c191377, org.spring framework.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.s ecurity.web.authentication.UsernamePasswordAuthenticationFilter @3990c331, org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter @ 1e8d4ac1, org.springfram ework.security.web.authentication.www.BasicAuthenticationFilter @ 2d61d2a4, org.sp ringframework.security.web.savedrequest.RequestCacheAwareFilter @ 380d9a9b, lderAwareRequestFilter @ abf2de3 org.springframework.security.web.servletapi.SecurityContextHo, org.springfram ework.security.web.authentication.AnonymousAuthenticationFilter @ 2a5c161b, o rg.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.!

+20


source to share


9 replies


Ok, after more than two days of searching, we have finally fixed the issue. We removed all our filters and configurations and used these 5 lines of code in the application class instead.



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

      

+16


source


Since Spring Security 4.1, this is the correct way to make Spring CORS security support (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 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 the following, which is the wrong way to solve the problem:

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

  • web.ignoring().antMatchers(HttpMethod.OPTIONS);

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

+61


source


Since I had problems with other solutions (especially for it to work in all browsers, eg edge does not recognize "*" as a valid value for "Access-Control-Allow-Methods") I had to use a custom filter component which eventually 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
    }

}

      

+15


source


  1. You do not need:

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

    @EnableWebSecurity

    is already there @Configuration

    and I can't imagine why you put there @ComponentScan

    .

  2. About the CORS filter, I would simply say:

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

    Into the SecurityConfiguration class and remove the preference and configure the global methods. You don't need to set permissive organizations, headers and methods twice. Especially if you set different properties in your filter and spring security config :)

  3. According to the above, your "MyFilter" class is redundant.

  4. You can also remove those:

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

    From the application class.

  5. In the end, a little advice - not related to the question. You don't want to put verbs in the URI. Instead, http://localhost:8080/getKunden

    you should use the HTTP GET method for the resource http://localhost:8080/kunden

    . You can learn RESTful API best practices here: http://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api

+7


source


Using Spring Security in Spring Boot 2 to configure CORS globally (by enabling all development requests, for example), you can do the following:

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

      

+5


source


According to the CORS Filters documentation :

"Spring MVC provides fine-grained support for CORS configuration through annotations on controllers. However, when used with Spring Security, it is recommended to rely on the built-in CorsFilter , which must be ordered in front of the Spring Filter Security Chain."

Something like this will allow GET

access to /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;
    }
}

      

Of course, your SpringSecurity configuration must provide access to the URI with the listed methods. See @ Hendy Irawan's answer.

+3


source


In my case, I just added this class and used @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");
}

      

}

+2


source


In many places I see an answer that should add this code:

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

      

but in my case it throws an unexpected class type exception. corsFilter()

the bean corsFilter()

needs a type CorsFilter

, so I CorsFilter

changed these and put this bean definition in my config and now everything is fine.

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

      

+2


source


For programs that are already deployed and cannot afford code changes (like add / update spring security), adding a simple proxy is one of the solutions: fooobar.com/questions/28291 / ...

0


source







All Articles