Global CORS configuration breaks when migrating to Spring Boot 2.0.x - spring-boot

Why is my 'Access-Control-Allow-Credentials' no longer being sent in response to preflight calls (OPTIONS) under Spring Boot 2.0.x (2.0.1.RELEASE in my case)? Here is my Global CORS Configuration that works fine under Spring Boot 1.5.6:
#Configuration
public class CorsConfig {
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins(
"http://localhost:3000",..)
.allowedMethods("GET", "POST", "PUT", "DELETE", "HEAD");
}
};
}}
My pom dependencies (I am doing my own security and avoiding Spring Security):
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
My service call to the REST endpoints fails the preflight:
Failed to load http://localhost:8080/api/v5/sec/auth: Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Credentials' header in the response is '' which must be 'true' when the request's credentials mode is 'include'. Origin 'http://localhost:3000' is therefore not allowed access.
I have verified that 'Access-Control-Allow-Credentials' header is indeed present in the case of Spring Boot 1.5.6 and missing under Spring Boot 2.0.1.
All the documentation I can find, including the latest on spring.io here, says my global configuration is still correct, even though WebMvcConfigurerAdapter appears to be deprecated now.
UPDATE:
Here are the response headers before and after the migrate:
Before Migrate (Spring Boot 1.5.6):
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: http://localhost:3000
Content-Type: application/json;charset=UTF-8
Date: Day, dd Mon yyyy hh:mm:ss GMT
Transfer-Encoding: chunked
Vary: Origin
After Migrate (Spring Boot 2.0.1 - Access-Control-Allow-Credentials header missing, but others changed/added):
Access-Control-Allow-Headers: content-type
Access-Control-Allow-Methods: GET,HEAD,POST <-- My specified methods ignored
Access-Control-Allow-Origin: * <-- My specified origin ignored
Access-Control-Max-Age: 1800
Content-Length: 0
Date: Day, dd Mon yyyy hh:mm:ss GMT
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers

This was missing from the Spring doc and many examples but the answer was very easy. I just saw the allowCredentials() method on CorsRegistry and added .allowCredentials(true) to the registry method chain and that added the Access-Control-Allow-Credentials header back in.
Also, I no longer use the deprecated WebMvcConfigurerAdapter, but now implement WebMvcConfigurer and override the addCorsMappings() method.
#Configuration
public class CorsConfig implements WebMvcConfigurer {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins(
"http://localhost:3000",..)
.allowedMethods("GET", "POST", "PUT", "DELETE", "HEAD")
.allowCredentials(true)
;
}
}

If you are using Spring Boot 2.0.x
CORS support is disabled by default and is only enabled once the management.endpoints.web.cors.allowed-origins property has been set. The following configuration permits GET and POST calls from the example.com domain:
management.endpoints.web.cors.allowed-origins=http://example.com
management.endpoints.web.cors.allowed-methods=GET,POST
For more information refer

I'm using spring boot 2.0.2. I have the same issue, but I use the following code to fix it. Does anybody have the best way?
// Miss `Access-Control-Allow-Origin` header in response using this bean.
// #Bean
// CorsConfigurationSource corsConfigurationSource() {
// CorsConfiguration configuration = new CorsConfiguration();
// configuration.setAllowCredentials(true);
// configuration.setAllowedHeaders(Arrays.asList("Authorization", "Cache-Control", "Content-Type"));
// configuration.addAllowedMethod("*");
// configuration.setAllowedOrigins(this.getAllowedOrigins());
// UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
// source.registerCorsConfiguration("/**", configuration);
// return source;
// }
#Bean
public FilterRegistrationBean<CorsFilter> initCorsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.setAllowedHeaders(Arrays.asList("Authorization", "Cache-Control", "Content-Type"));
config.addAllowedMethod("*");
config.setAllowedOrigins(this.getAllowedOrigins());
source.registerCorsConfiguration("/**", config);
FilterRegistrationBean<CorsFilter> bean = new FilterRegistrationBean<>(new CorsFilter(source));
bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return bean;
}

Step 1 : Spring already has a CorsFilter even though You can just register your own CorsFilter as a bean to provide your own cofiguration.
#Bean
public CorsFilter corsFilter() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
final CorsConfiguration config = new CorsConfiguration();
config.setAllowedOrigins(Collections.singletonList("http://localhost:3000")); // Provide list of origins if you want multiple origins
config.setAllowedHeaders(Arrays.asList("Origin", "Content-Type", "Accept"));
config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "OPTIONS", "DELETE", "PATCH"));
config.setAllowCredentials(true);
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
Step 2 : Annotate the controller with #CrossOrigin annotation.

This works for me (Kotlin):
#Configuration
class CorsConfig : WebMvcConfigurer {
override fun addCorsMappings(registry: CorsRegistry) {
registry.addMapping("/**")
}
}

Related

Spring Authorization Server: Access to XMLHttpRequest has been blocked by CORS policy

I have a basic Spring Authorization Server set up as a Spring Boot application. I am attempting to access this server via an angular application using angular-auth-oidc-client.
When I attempt to log in, I get this error:
Access to XMLHttpRequest at 'http://localhost:9000/mydomain/.well-known/openid-configuration' from origin 'http://localhost:4200' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
I've made multiple attempts to fix this issue, but have been unsuccessful.
Relevant parts of the Configuration for the authorization server are below:
// #formatter:off
#Bean
public RegisteredClientRepository registeredClientRepository() {
// removed
}
// #formatter:on
#Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowedOrigins(Arrays.asList("*"));
config.setAllowedMethods(Arrays.asList("HEAD", "GET", "PUT", "POST", "DELETE", "PATCH"));
config.setAllowedHeaders(Arrays.asList("*"));
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("GET");
config.addAllowedMethod("POST");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return source;
}
// #formatter:off
#Bean
#Order(Ordered.HIGHEST_PRECEDENCE)
public SecurityFilterChain authServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
http
.formLogin(Customizer.withDefaults())
.cors().configurationSource(corsConfigurationSource());
return http.build();
}
// #formatter:on
That CORS Configuration seems to be wide open, but I'm still seeing the issue.
Am I just making a stupid mistake that I'm not seeing?
Edit: yes, I've configured that port and domain in application.yml:
server:
port: 9000
servlet:
context-path: /mydomain

CORS issue between Angular 10 and Spring Boot 1.5

I just can't solve a CORS issue between my angular 10 application and spring boot 1.5.
I've tried everything that i can and it's still not working.
I'm trying to set a global CORS config to allow everything
My current implementation:
#Configuration
public class DevConfig {
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://localhost:4200")
.allowedHeaders("*");
}
};
}
}
And I'm getting this on the frontend side:
Access to XMLHttpRequest at 'http://localhost:8080/test' from origin 'http://localhost:4200' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource
Version
<properties>
<java.version>1.8</java.version>
<commons-io.version>2.7</commons-io.version>
<spring.version>1.5.9.RELEASE</spring.version>
</properties>
[EDIT]
I've changed that to this:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import java.util.Arrays;
#Configuration
public class AppConfig {
#Bean
CorsConfigurationSource corsConfigurationSource()
{
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("http://localhost:4200"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "OPTIONS", "DELETE", "PUT", "PATCH"));
configuration.setAllowedHeaders(Arrays.asList("X-Requested-With", "Origin", "Content-Type", "Accept", "Authorization"));
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
And it's still not working
Add #CrossOrigin(origins="http://localhost:4200") in the method or controller level in your RestController. Or,
If you want to add global configuration, configure this bean in your configuration file:
#Bean
CorsConfigurationSource corsConfigurationSource()
{
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("http://localhost:4200"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "OPTIONS", "DELETE", "PUT", "PATCH"));
configuration.setAllowedHeaders(Arrays.asList("X-Requested-With", "Origin", "Content-Type", "Accept", "Authorization"));
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
If you're using spring security, you should add configuration.setAllowCredentials(true); here too and don't forget to add http.cors() in spring security configuration.
Edit:
When the browser is making a cross-origin request, the browser adds an Origin header with the current origin (scheme, host, and port). On the server side, when a server sees this header, and wants to allow access, it needs to add an Access-Control-Allow-Origin header to the response specifying the requesting origin (or * to allow any origin.). When the browser sees this response with an appropriate Access-Control-Allow-Origin header, the browser allows the response data to be shared with the client site.
If your backend is sending the header, this might likely be that Chrome does not support localhost to go through the Access-Control-Allow-Origin. To have Chrome send Access-Control-Allow-Origin in the header, just alias your localhost in your /etc/hosts file to some other domain, like:
127.0.0.1 localhost yourdomain.com
Then if you'd access your api using yourdomain.com instead of localhost, the call should succeed.
If the problem still exist, You need to check whether any header is sent by the backend. If not, add that to response headers (that's nasty). Let me know what actually happened.

Spring Boot Cors "Access-Control-Max-Age" header is ignored by browsers

What I am expecting from this header is; browser sending only one options pre-flight request for the max-age duration per resource.
However, every browser that I tried keeps sending options pre-flight request for every request even the ones that are sent before.
I tried disabling no-cache header but nothing changed. I shared my cors config code below. By the way, my spring boot is not configured for https/ssl but domain and front-end react app is, could that be the problem?
#Bean
public WebMvcConfigurer corsConfigurer(){
return new WebMvcConfigurer() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedHeaders("*")
.allowedMethods("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS")
.allowCredentials(true)
.allowedOrigins(ALLOWED_ORIGINS)
.exposedHeaders(AuthorizationController.AUTHENTICATION_KEY_NAME,
RequestInterceptor.FAILURE_REASON_HEADER_KEY_NAME,
RequestInterceptor.CONTENT_DISPOSITION_HEADER_KEY_NAME)
.maxAge(36000);
}
};
}
#Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Collections.singletonList(ALLOWED_ORIGINS));
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"));
configuration.setAllowedHeaders(Collections.singletonList("*"));
configuration.setExposedHeaders(Arrays.asList(AuthorizationController.AUTHENTICATION_HEADER_NAME,
RequestInterceptor.ERROR_DESCRIPTION_HEADER_KEY_NAME,
RequestInterceptor.CONTENT_DISPOSITION_HEADER_KEY_NAME));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
I am also adding below headers to every request in OncePerRequestFilter.
httpServletResponse.setHeader("Access-Control-Max-Age","36000");
httpServletResponse.setHeader("Access-Control-Allow-Origin",ALLOWED_ORIGINS);
httpServletResponse.setHeader("Access-Control-Allow-Headers","*");
httpServletResponse.setHeader("Access-Control-Allow-Methods","GET, PUT, POST, DELETE, HEAD, OPTIONS");
httpServletResponse.setHeader("Cache-Control","no-cache, no-store, max-age=36000")
you need to white list the domain/host from which you are hitting the back-end/

App Engine Standard with Spring Boot CORS not working

I am implementing a Spring Boot application that is hosted on a Google App Engine Standard Environment.
I have configured CORS like this, following the official guide:
#Configuration
#EnableWebSecurity
class WebSecurityConfigurer : WebSecurityConfigurerAdapter() {
#Throws(Exception::class)
override fun configure(http: HttpSecurity) {
http.csrf()
.disable()
.cors()
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().authorizeRequests().antMatchers("/api/**").permitAll()
}
#Bean
fun corsConfigurationSource(): CorsConfigurationSource {
val configuration = CorsConfiguration()
configuration.allowedOrigins = listOf("*")
configuration.allowedMethods = listOf("GET", "POST", "OPTIONS", "PUT", "DELETE", "HEAD")
val source = UrlBasedCorsConfigurationSource()
source.registerCorsConfiguration("/**", configuration)
return source
}
executing the following cURL I receive the AllowedOrigins header as it is necessary:
curl -H "Access-Control-Request-Method: GET" -H "Origin: http://foo" -X OPTIONS "localhost:8080/api/abc/list?lang=de"
Response:
HTTP/1.1 200
Access-Control-Allow-Origin: *
Now when I have deployed my Spring App to AppEngine, I can also cURL successfully.
HTTP/2 200
access-control-allow-origin: https://myfrontend.com
access-control-allow-methods: GET
access-control-allow-credentials: true
Unfortunately, my Frontend Application gets blocked with a 403
Access to fetch at 'https://mybackend.com/api/abc/list?lang=de' from origin 'https://myfrontend.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
Any tips?
Thanks.
I think you missed the important part setting headers in cors.
Add this line
configuration.allowedHeaders = listOf("*")
I used this code :
Ref : https://blogs.ashrithgn.com/disable-cors-in-spring-boot/
I have two GAE project
Python based using angular 11
Spring boot based micro services std env
Imp : No other changes in app.yaml or as mentioned by spring at
https://spring.io/guides/gs/rest-service-cors/ will make any difference
#Configuration
public class CorsConfig {
#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;
}

Spring CORS Filter problem in light of Spring Cloud microservices with Zuul

Given: angular frontend application sends requests to backend microservice through gateway microservice. The backend is in Spring Cloud.
Question: how to correctly configure CORS filters to get rid of the following error:
Access to XMLHttpRequest at 'http://gateway-service:5555/api/useful-service/myentities/' from origin 'http://localhost:4200' has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header contains multiple values 'http://localhost:4200, http://localhost:4200', but only one is allowed.
That's what I have written so far:
Gateway Service
My main class in the gateway serivice has 3 annotations: #SpringBootApplication, #EnableZuulProxy and #Configuration. So as I don't confgigured any security thing I presume that the Spring Security is not being used therefore I need to configure Spring MVC's CorsFilter. I do that like this (comments are for future searchers):
#Bean
public CorsFilter corsFilter() {
CorsConfiguration corsConfig = new CorsConfiguration();
corsConfig.setAllowCredentials(true);
//corsConfig.addAllowedOrigin("http://localhost:4200");
corsConfig.addAllowedOrigin("*"); //wildcard that will simply copy the value of the request's Origin header
// into the value of the Response's Access-Control-Allow-Origin header, effectively allowing all origins.
// You can add specific origins instead if you wish to limit them.
corsConfig.addAllowedHeader("*");
corsConfig.addAllowedMethod("OPTIONS");
corsConfig.addAllowedMethod("HEAD");
corsConfig.addAllowedMethod("GET");
corsConfig.addAllowedMethod("POST");
corsConfig.addAllowedMethod("PUT");
corsConfig.addAllowedMethod("DELETE");
corsConfig.addAllowedMethod("PATCH");
UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource();
configSource.registerCorsConfiguration("/**", corsConfig);
return new CorsFilter(configSource);
}
Useful Service
The main class over here is annotated with #EnableResourceServer and #SpringBootApplication. According to my "business rules" I would like to have Spring authorization (url security, and in the future the method security also) so as I configured Spring Security in general and OAuth2 and in partucular I should configure security's cors filter as well. Here's relevant security snippet that enables cors:
#Configuration
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http
.cors(); // by default uses a Bean by the name of corsConfigurationSource
}
}
And that's how I configure the cors functionality of spring security:
#Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("*"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "OPTIONS", "DELETE", "PUT", "PATCH"));
configuration.setAllowedHeaders(Arrays.asList("X-Requested-With", "Origin", "Content-Type", "Accept", "Authorization"));
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
Unfortunatelly I got the error mentioned above, if you have an idea how to fix it please tale.
It seems that this problem was fixed with a DedupeResponseHeader-filter.
See https://github.com/spring-cloud/spring-cloud-gateway/pull/866

Resources