Spring Boot Security CORS
Solution 1
Instead of using the CorsRegistry you can write your own CorsFilter and add it to your security configuration.
Custom CorsFilter class:
public class CorsFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) servletResponse;
HttpServletRequest request= (HttpServletRequest) servletRequest;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "GET,POST,DELETE,PUT,OPTIONS");
response.setHeader("Access-Control-Allow-Headers", "*");
response.setHeader("Access-Control-Allow-Credentials", true);
response.setHeader("Access-Control-Max-Age", 180);
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
}
}
Security config class:
@Configuration
@EnableWebSecurity
public class OAuth2SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Bean
CorsFilter corsFilter() {
CorsFilter filter = new CorsFilter();
return filter;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.addFilterBefore(corsFilter(), SessionManagementFilter.class) //adds your custom CorsFilter
.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint).and()
.formLogin()
.successHandler(ajaxSuccessHandler)
.failureHandler(ajaxFailureHandler)
.loginProcessingUrl("/authentication")
.passwordParameter("password")
.usernameParameter("username")
.and()
.logout()
.deleteCookies("JSESSIONID")
.invalidateHttpSession(true)
.logoutUrl("/logout")
.logoutSuccessUrl("/")
.and()
.csrf().disable()
.anonymous().disable()
.authorizeRequests()
.antMatchers("/authentication").permitAll()
.antMatchers("/oauth/token").permitAll()
.antMatchers("/admin/*").access("hasRole('ROLE_ADMIN')")
.antMatchers("/user/*").access("hasRole('ROLE_USER')");
}
}
Solution 2
Option 1 (Use WebMvcConfigurer bean):
The CORS configuration that you started with is not the proper way to do it with Spring Boot. You need to register a WebMvcConfigurer
bean. Reference here.
Example Spring Boot CORS configuration:
@Configuration
@Profile("dev")
public class DevConfig {
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("http://localhost:4200");
}
};
}
}
This will provide the CORS configuration for a basic (no security starter) Spring Boot application. Note that CORS support exists independent of Spring Security.
Once you introduce Spring Security, you need to register CORS with your security configuration. Spring Security is smart enough to pick up your existing CORS configuration.
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors().and()
....
Option 2 (Use CorsConfigurationSource bean):
The first option I described is really from the perspective of adding Spring Security to an existing application. If you are adding Spring Security from the get-go, the way that is outlined in the Spring Security Docs involves adding a CorsConfigurationSource bean.
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
// by default uses a Bean by the name of corsConfigurationSource
.cors().and()
...
}
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("https://example.com"));
configuration.setAllowedMethods(Arrays.asList("GET","POST"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
Solution 3
This is quite clean and doesn't require any extra configurations. Pass asterisks where you want all option to be valid (like I did in setAllowedHeaders).
@EnableWebSecurity
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.cors().configurationSource(request -> {
var cors = new CorsConfiguration();
cors.setAllowedOrigins(List.of("http://localhost:4200", "http://127.0.0.1:80", "http://example.com"));
cors.setAllowedMethods(List.of("GET","POST", "PUT", "DELETE", "OPTIONS"));
cors.setAllowedHeaders(List.of("*"));
return cors;
}).and()...
}
}
Solution 4
I have a React
based web client, and my backend REST API is running Spring Boot
Ver 1.5.2
I wanted to quickly enable CORS
on all controller route requests from my client running on localhost:8080
. Inside my security configuration, I simply added a @Bean
of type FilterRegistrationBean
and got it working easily.
Here is the code:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class AuthConfiguration extends WebSecurityConfigurerAdapter {
....
....
@Bean
public FilterRegistrationBean corsFilter() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin(corsAllowedOrigin); // @Value: http://localhost:8080
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
bean.setOrder(0);
return bean;
}
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll() // **permit OPTIONS call to all**
....
}
....
....
}
You can refer Spring Boot docs here
Solution 5
Currently the OPTIONS requests are blocked by default if security is enabled.
Just add an additional bean and preflight requests will be handled correctly:
@Bean
public IgnoredRequestCustomizer optionsIgnoredRequestsCustomizer() {
return configurer -> {
List<RequestMatcher> matchers = new ArrayList<>();
matchers.add(new AntPathRequestMatcher("/**", "OPTIONS"));
configurer.requestMatchers(new OrRequestMatcher(matchers));
};
}
Please note that depending on your application this may open it for potential exploits.
Opened issue for a better solution: https://github.com/spring-projects/spring-security/issues/4448
user1935987
Updated on March 25, 2021Comments
-
user1935987 about 3 years
I have a problem with CORS filter on spring security URL's. It doesn't set
Access-Control-Allow-Origin
and other exposed header on URL's belonging to spring sec (login/logout) or filtered by Spring Security.Here are the configurations.
CORS:
@Configuration @EnableWebMvc public class MyWebMvcConfig extends WebMvcConfigurerAdapter { ********some irrelevant configs************ @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/*").allowedOrigins("*").allowedMethods("GET", "POST", "OPTIONS", "PUT") .allowedHeaders("Content-Type", "X-Requested-With", "accept", "Origin", "Access-Control-Request-Method", "Access-Control-Request-Headers") .exposedHeaders("Access-Control-Allow-Origin", "Access-Control-Allow-Credentials") .allowCredentials(true).maxAge(3600); } }
Security:
@Configuration @EnableWebSecurity public class OAuth2SecurityConfiguration extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .exceptionHandling().authenticationEntryPoint(authenticationEntryPoint).and() .formLogin() .successHandler(ajaxSuccessHandler) .failureHandler(ajaxFailureHandler) .loginProcessingUrl("/authentication") .passwordParameter("password") .usernameParameter("username") .and() .logout() .deleteCookies("JSESSIONID") .invalidateHttpSession(true) .logoutUrl("/logout") .logoutSuccessUrl("/") .and() .csrf().disable() .anonymous().disable() .authorizeRequests() .antMatchers("/authentication").permitAll() .antMatchers("/oauth/token").permitAll() .antMatchers("/admin/*").access("hasRole('ROLE_ADMIN')") .antMatchers("/user/*").access("hasRole('ROLE_USER')"); } }
So, if I make a request to the url's which are not listened by security - CORS headers are set. Spring security URL's - not set.
Spring boot 1.4.1