Reputation: 877
I am working in a Spring Boot 2.2.5 application with an Angular 9 frontend.
I have been trying to configure a CORS filter on the Spring Boot backend to allow any origin with any headers for any request.
Based on my research, what I currently have implemented in my main Application.java
file should work:
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("OPTIONS");
config.addAllowedMethod("GET");
config.addAllowedMethod("POST");
config.addAllowedMethod("PUT");
config.addAllowedMethod("DELETE");
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
However, what I am experiencing is that this is only allowing me to execute GET
call from the frontend to my controller on the backend. Any other call to POST
or PUT
fails with the following error:
Access to XMLHttpRequest at 'http://localhost:8080/spring-boot-starter-seed/default-theme/save/cyan' from origin 'http://localhost:8083' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
I know im somewhat on the right track because if I remove the @Bean
I implemented, then all calls from frontend to backend fail due to that error. But when implementing that Bean, for some reason its only working for GET
requests.
Is there something I have missed?
***** Update *****
I dont know if this will be helpful, but, I am using Azure AD for authentication with this application. I have a WebSecurityConfig
that looks like this:
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public JwtAuthenticationConverter jwtAuthenticationConverter() {
JwtGrantedAuthoritiesConverter grantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
grantedAuthoritiesConverter.setAuthoritiesClaimName("roles");
grantedAuthoritiesConverter.setAuthorityPrefix("ROLE_");
JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter);
return jwtAuthenticationConverter;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/actuator/**", "/heartbeat/**", "/register", "/unregister").permitAll()
.anyRequest().authenticated()
.and()
.oauth2ResourceServer()
.jwt()
.jwtAuthenticationConverter(this.jwtAuthenticationConverter());
}
}
Earlier in my post I said that GET
requests are working, but that seems to be somewhat false.
GET /heartbeat
works. However...
GET /default-theme
does not work and fails with the same No Access-Control-Allow-Origin error.
The only difference between these two Controller paths is that the /default-theme
is not included in the excluded antMatchers, and it is protected by @PreAuthorize(value = "hasRole('ROLE_access')")
Upvotes: 2
Views: 2771
Reputation: 6963
Inside your WebSecurityConfig
class you need to configure cors
as given below
@Override
protected void configure(HttpSecurity http) throws Exception {
http
...
.and()
.cors().configurationSource(corsConfigurationSource())
.and()
...
}
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList(
"list of domains here"
)
);
configuration.setAllowedMethods(Arrays.asList("DELETE", "GET", "POST", "PATCH", "PUT", "OPTIONS"));
configuration.setAllowCredentials(true);
configuration.setAllowedHeaders(
Arrays.asList(
"Access-Control-Allow-Headers",
"Access-Control-Allow-Origin",
"Access-Control-Request-Method",
"Access-Control-Request-Headers",
"Origin", "Cache-Control",
"Content-Type",
"Authorization"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
Upvotes: 1
Reputation: 877
So, as it turns out, my update on my original post was on the right track to assume that Spring Security and my WebSecurityConfig
was part of the cause.
I found the real solution on an answer here.
I needed to update my HttpSecurity
config to include the .cors()
first:
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and()
.authorizeRequests()
.antMatchers("/actuator/**", "/heartbeat", "/register", "/unregister").permitAll()
.anyRequest().authenticated()
.and()
.oauth2ResourceServer()
.jwt()
.jwtAuthenticationConverter(this.jwtAuthenticationConverter());
}
After doing this, all other CORS configurations were unnecessary and could be removed from my app. Just add the @CrossOrigin
annotation to any relevant controllers.
With this in place, calling my GET /default-theme
controller, protected by the @PreAuthorize(value = "hasRole('ROLE_access')")
, got rid of the No Access-Control-Allow-Origin error, and resulted in the error I expected which was a 401 response.
The underlying reason for all of this was because I was tasked with migrating this application from LDAP auth to Azure AD auth.
Hopefully this will help someone in the future.
Upvotes: 0
Reputation: 526
try this class to implement a filter as well :
public class CORSFilter extends GenericFilterBean implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chaine)
throws IOException, ServletException {
HttpServletResponse httpResponse = (HttpServletResponse)response;
httpResponse.setHeader("Access-Control-Allow-Origin", "*");
httpResponse.setHeader("Access-Control-Allow-Methods", "*");
httpResponse.setHeader("Access-Control-Allow-Headers", "*");
httpResponse.setHeader("Access-Control-Allow-Credentials", "false");
httpResponse.setHeader("Access-Control-Max-Age", "3600");
System.out.println("****************** CORS Configuration Completed *******************");
chaine.doFilter(request, response);
}
}
Then inject the bean in your main application class or every where :
@Bean
public FilterRegistrationBean crosFilterRegistration(){
FilterRegistrationBean registrationBean = new FilterRegistrationBean(new CORSFilter());
registrationBean.setName("CORS Filter");
registrationBean.addUrlPatterns("/*");
registrationBean.setOrder(1);
return registrationBean;
}
Hope this would be helpfull
Upvotes: 1