65

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.!

Community
  • 1
  • 1
Mace
  • 1,049
  • 1
  • 12
  • 14
  • 1
    There is an issue with Chrome it does not support localhost to go through the Access-Control-Allow-Origin. Try with another browser – Issam El-atif Nov 04 '16 at 09:35
  • We tried with Edge and it is working... but firefox isn't working as well. – Mace Nov 04 '16 at 09:40
  • I was having the same issue i solve it by adding `127.0.0.1 localhost local.net` to `/etc/hosts` then call http://local.net:8080/getKunden – Issam El-atif Nov 04 '16 at 09:46
  • see http://stackoverflow.com/questions/28547288/no-access-control-allow-origin-header-is-present-on-the-requested-resource-err it could help – Issam El-atif Nov 04 '16 at 10:07

17 Answers17

121

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

Hendy Irawan
  • 20,498
  • 11
  • 103
  • 114
  • 1
    That works for me but I had to keep `the http.csrf().disable()`, because once it is deleted I got the error: `Could not verify the provided CSRF token because your session was not found` – Strider Jul 03 '17 at 10:53
  • 1
    Perfect answer... Though for me I had to uncomment http.csrf().disable(); – Amos Kosgei Jul 12 '17 at 13:13
  • 5
    If you aren't using Guava, you can always do this: `Collections.unmodifiableList(Arrays.asList("HEAD",...))` – Brian Dec 11 '17 at 11:45
  • 1
    This does not work with Firefox clients if your app is operating in a 2-way SSL environment using x509 certificates. Spring Security still expects credentials on OPTIONS requests, which is in violation of the CORS spec: https://fetch.spec.whatwg.org/#cors-preflight-fetch, and unfortunately Firefox strictly adheres to this spec and rips out credentials in preflight requests. – heez Mar 14 '18 at 16:40
  • 9
    Please note that WebMvcConfigurerAdapter is deprecated now. – priteshbaviskar Aug 05 '18 at 09:10
  • 1
    Type mismatch: cannot convert from UrlBasedCorsConfigurationSource to CorsConfigurationSource – Manish Sharma Oct 23 '18 at 13:58
  • 5
    **Don't** remove the CSRF without knowing what you are doing! https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF) – Eran Medan Feb 19 '19 at 03:26
  • 2
    Still doesn't work with this solution, cause preflight requests are blocked. – Vortilion Mar 13 '19 at 08:11
  • 7
    The type `WebMvcConfigurerAdapter` is deprecated. Since Spring 5 you just need to implement the interface `WebMvcConfigurer` – Filomat Jan 19 '20 at 12:10
  • For `configuration.setAllowedOrigins()` I had to change "*" to a specific url like: http://localhost:3000 and it works just fine. Thanks :) – Pollongz May 24 '21 at 21:55
31

Ok, after over 2 days of searching we finally fixed the problem. We deleted all our filter and configurations and instead used this 5 lines of code in the application class.

@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");
            }
        };
    }
}
Community
  • 1
  • 1
Mace
  • 1,049
  • 1
  • 12
  • 14
29

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
    }

}
  • Same here. Had to replace Authorization with X-Authorization though. – Marcel Apr 18 '19 at 15:10
  • Why I chose this solution: Because I have AuthorizationFilter which responds before CorsBean, hence all Cors preflighted requests are answered by AuthFilter as opposed to Spring Cors config. Hence I have replaced CorsConfig bean with CorsFilter mentioned in this solution; now Cors pre-flight requests are answered by CorsFilter as intended. @David Thank you. – Srinivasan Thoyyeti Jul 06 '20 at 12:17
17

There's 8 hours of my life I will never get back...

Make sure that you set both Exposed Headers AND Allowed Headers in your CorsConfiguration

@Bean
CorsConfigurationSource corsConfigurationSource() {
    CorsConfiguration configuration = new CorsConfiguration();
    configuration.setAllowedOrigins(Collections.singletonList("http://localhost:3000"));
    configuration.setAllowedMethods(Arrays.asList("GET","POST", "PUT", "DELETE", "PATCH", "OPTIONS"));
    configuration.setExposedHeaders(Arrays.asList("Authorization", "content-type"));
    configuration.setAllowedHeaders(Arrays.asList("Authorization", "content-type"));
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", configuration);
    return source;
}
mljohns89
  • 887
  • 1
  • 11
  • 16
16

With Spring Security in Spring Boot 2 to configure CORS globally (e.g. enabled all request for development) you can do:

@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();
}
Grigory Kislin
  • 16,647
  • 10
  • 125
  • 197
  • I'm getting Compiler error `cannot convert from UrlBasedCorsConfigurationSource to CorsConfigurationSource` – Ori Marko Apr 11 '22 at 11:51
11
  1. You don't need:

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

    @EnableWebSecurity already has @Configuration in it, and I cannot imagine why you put @ComponentScan there.

  2. About CORS filter, I would just put this:

    @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 SecurityConfiguration class and remove configure and configure global methods. You don't need to set allowde orgins, headers and methods twice. Especially if you put different properties in filter and spring security config :)

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

  4. You can also remove those:

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

    From Application class.

  5. At the end small advice - not connected to the question. You don't want to put verbs in URI. Instead of http://localhost:8080/getKunden you should use HTTP GET method on http://localhost:8080/kunden resource. You can learn about best practices for design RESTful api here: http://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api

JimHawkins
  • 4,843
  • 8
  • 35
  • 55
Piotr Sołtysiak
  • 998
  • 5
  • 13
7

Class WebMvcConfigurerAdapter is deprecated as of 5.0 WebMvcConfigurer has default methods and can be implemented directly without the need for this adapter. For this case:

@Configuration
@EnableWebMvc
public class WebMvcConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**").allowedOrigins("http://localhost:3000");
    }
}

See also: Same-Site flag for session cookie

6

According the CORS filter documentation:

"Spring MVC provides fine-grained support for CORS configuration through annotations on controllers. However when used with Spring Security it is advisable to rely on the built-in CorsFilter that must be ordered ahead of Spring Security’s chain of filters"

Something like this will allow GET access to the /ajaxUri:

@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 allow access to the URI with the listed methods. See @Hendy Irawan answer.

Community
  • 1
  • 1
lmiguelmh
  • 3,074
  • 1
  • 37
  • 53
6

This solution unlock me after couple of hours of research :

In the configuration initialize the core() option

@Override
public void configure(HttpSecurity http) throws Exception {
http
    .cors()
    .and()
    .etc
}

Initialize your Credential, Origin, Header and Method as your wish in the corsFilter.

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

I didn't need to use this class:

@Bean
public CorsConfigurationSource corsConfigurationSource() {
}
Theo Dury
  • 141
  • 2
  • 9
5

In many places, I see the answer that needs to 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() bean requires CorsFilter type, so I have done this changes and put this definition of bean in my config and all is OK now.

@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);
}
CKE
  • 1,533
  • 19
  • 18
  • 29
Garik Kalashyan
  • 129
  • 1
  • 4
4

In my case, I just added this class and use @EnableAutConfiguration:

@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");
    }
}
Community
  • 1
  • 1
Alex Fernandez
  • 1,892
  • 14
  • 17
3

With SpringBoot 2 Spring Security, The code below perfectly resolved Cors issues

@Bean
public CorsConfigurationSource corsConfigurationSource() {
    CorsConfiguration configuration = new CorsConfiguration();
    configuration.setAllowedOrigins(Collections.singletonList("*")); // <-- you may change "*"
    configuration.setAllowedMethods(Arrays.asList("HEAD", "GET", "POST", "PUT", "DELETE", "PATCH"));
    configuration.setAllowCredentials(true);
    configuration.setAllowedHeaders(Arrays.asList(
            "Accept", "Origin", "Content-Type", "Depth", "User-Agent", "If-Modified-Since,",
            "Cache-Control", "Authorization", "X-Req", "X-File-Size", "X-Requested-With", "X-File-Name"));
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", configuration);
    return source;
}

@Bean
public FilterRegistrationBean<CorsFilter> corsFilterRegistrationBean() {
    FilterRegistrationBean<CorsFilter> bean = new FilterRegistrationBean<>(new CorsFilter(corsConfigurationSource()));
    bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
    return bean;
}

Then for the WebSecurity Configuration, I added this

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.headers().frameOptions().disable()
            .and()
            .authorizeRequests()
            .antMatchers("/oauth/tokeen").permitAll()
            .antMatchers(HttpMethod.GET, "/").permitAll()
            .antMatchers(HttpMethod.POST, "/").permitAll()
            .antMatchers(HttpMethod.PUT, "/").permitAll()
            .antMatchers(HttpMethod.DELETE, "/**").permitAll()
            .antMatchers(HttpMethod.OPTIONS, "*").permitAll()
            .anyRequest().authenticated()
            .and().cors().configurationSource(corsConfigurationSource());
}
Community
  • 1
  • 1
Nandom Gusen
  • 51
  • 1
  • 5
3

To enable CORS Globally you need to make changes in two places, if you are also using spring security with boot:

1. Spring Boot:

@Configuration
public class CorsConfiguration extends WebMvcConfigurationSupport {  
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**").allowedOrigins("*").allowedMethods("*")
        .allowCredentials(true);
    }
}

You can do the same in WebMvcConfigurerAdapter, or create bean of WebMvcConfigurer.

2. Spring Security

@Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().and()
                .authorizeRequests()
                .antMatchers(HttpMethod.OPTIONS).permitAll() //Permits your preflight request
}

This enables all methods, all paths and origins. So, use with caution and only in development. Works as on Spring Boot 2.3.3.RELEASE

Deb
  • 5,163
  • 7
  • 30
  • 45
0

I had a similar problem but with the specific requirement to have the CORS header set from decorators in our endpoints, like this:

@CrossOrigin(origins = "*", allowCredentials = "true")
@PostMapping(value = "/login")

Or

@CrossOrigin(origins = "*")
@GetMapping(value = "/verificationState")

So simply intercepting the request, setting the CORS header manually and sending 200 back was not an option, because allowCredentials was needed to be true in some cases and wildcards are not allowed then. Sure, a CORS registry would have helped, but since our clients are angular in capacitor on android and iOS, there is no specific domain to register. So the clean way to do - in my opinion - is to pipe preflights directly to the endpoints to let them handle it. I solved it with this:

@Component
public class PreflightFilter extends OncePerRequestFilter {

    private static final Logger logger = LoggerFactory.getLogger(PreflightFilter.class);

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {

        if (CorsUtils.isPreFlightRequest(request)) {
            logger.info("Preflight request accepted");
            SecurityContextHolder.getContext().setAuthentication(createPreflightToken(request));
        }
        chain.doFilter(request, response);
    }

    private UsernamePasswordAuthenticationToken createPreflightToken(HttpServletRequest request) {
        UserDetails userDetails = new User("Preflight", "",
                true, true, true, true,
                Stream.of(new SimpleGrantedAuthority("AppUser")).collect(Collectors.toSet()));
        UsernamePasswordAuthenticationToken preflightToken =
                new UsernamePasswordAuthenticationToken(
                        userDetails, null, userDetails.getAuthorities());
        preflightToken
                .setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
        return preflightToken;
    }
}

Keep in mind that the endpoint decorators don't work like wildcards here as one would expect!

Community
  • 1
  • 1
0

This ALLOWS ANY origin WITH CREDENTIALS for CORS:

@Component
public class FilterChainConfig implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        if (servletResponse instanceof HttpServletResponse){
            HttpServletResponse response = (HttpServletResponse)servletResponse;
            HttpServletRequest request = (HttpServletRequest) servletRequest;
            String requestOrigin = request.getHeader("Origin");
            response.setHeader("Access-Control-Allow-Origin", requestOrigin);
            response.setHeader("Access-Control-Allow-Credentials", "true");
            //response.setHeader("Access-Control-Allow-Methods", "*");
            response.setHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS, HEAD");
            response.setHeader("Access-Control-Max-Age", "3600");
            response.setHeader("Access-Control-Allow-Headers", "*");
            filterChain.doFilter(request, response);
        }
    }
}
Ermac
  • 21
  • 4
0
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

    
    public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    .....  

    @Override
    protected void configure(HttpSecurity http) throws Exception {
      http.authorizeRequests().antMatchers("/api/**").authenticated().and().httpBasic().and().csrf().disable();

    }
    
    @Bean
    protected CorsConfigurationSource corsConfigurationSource() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
        return source;
    }
    ......
    
  }
Mohsin Ejaz
  • 316
  • 3
  • 7
0

Use below sample

@Configuration
@EnableConfigurationProperties(BasicAuthConfigProperties.class)
@EnableWebSecurity

public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

private final BasicAuthConfigProperties basicAuth;

public SecurityConfiguration(BasicAuthConfigProperties basicAuth) {
    this.basicAuth = basicAuth;
}

protected void configure(HttpSecurity http) throws Exception {
    http.cors();
}
}