After I added CORS support to my microservices infrastructure I faced with problem with zuul forwarding. Instead of forwarding them with POST method Zuul redirects browser requests using GET method.
When browser downloads html+js from UI server (in my case heroku-ui) one of the js script makes AJAX request(POST) to zuul service (heroku-gateway).
$.ajax({
url : GATEWAY_SERVER_URL + '/results',
type : 'POST',
data : JSON.stringify(data),
contentType : "application/json; charset=utf-8",
dataType : "json",
crossDomain: true
});
Below life-cycle of such request: http://www.plantuml.com/plantuml/png/SoWkIImgAStDuNBEAChFJLNGjLE8AYtDKR1ICDHJy2yeoSpFArO8IYqiJIqkuGB9W5H0r0BL626hAIu0qgcO1XdhAGJu1uVWDiL43oYNabcKcboYK9AlK9nQL9QPZ6nGC5W4fDGKqr5GIHGJpV2w2s6SkPw2ksi8NgCim4eGnZYavgK0mmi0
1. Preflight Request (OPTIONS) https://heroku-gatewaay.herokuapp.com/api/results
Request headers:
- Access-Control-Request-Method : POST
- Origin : https://heroku-ui.herokuapp.com
- Access-Control-Request-Headers : content-type
Response headers:
- Access-Control-Allow-Origin : *
- Access-Control-Allow-Methods : GET,PUT,POST,DELETE
- Access-Control-Allow-Headers : content-type
HTTP status - 200
3. POST Request https://heroku-gatewaay.herokuapp.com/api/results
Request headers:
- Origin : https://heroku-ui.herokuapp.com
- Content-Type : application/json; charset=UTF-8
Response headers:
- Location : https://heroku-multiplication.herokuapp.com/results
HTTP status - 302
5. Preflight Request (OPTIONS) https://heroku-multiplication.herokuapp.com/results
Request headers:
- Access-Control-Request-Method : GET
- Origin : null
Response headers:
- Access-Control-Allow-Origin : *
- Access-Control-Allow-Methods : GET,PUT,POST,DELETE
- Access-Control-Allow-Headers : content-type
HTTP status - 200
7. GET Request (expected POST) https://heroku-multiplication.herokuapp.com/results
Request headers:
- Origin : null
- Content-Type : application/json; charset=UTF-8
Response headers:
- Access-Control-Allow-Origin : *
In step 7 I expects POST request but GET is sent.
Probably root cause is HTTP status 302 of http responses https://developer.mozilla.org/en-US/docs/Web/HTTP/Redirections#attr2
[2] The specification had no intent to allow method changes, but practically there are user agents out there doing this. 307 has been created to remove the ambiguity of the behavior when using non-GET methods.
Components:
1) Zuul (spring-cloud-starter-netflix-zuul of verion 'Greenwich.M1' and spring-boot-starter of version 2.1.0.RELEASE) Configuration:
@Configuration
@EnableWebMvc
public class WebConfiguration implements WebMvcConfigurer {
@Override
public void addCorsMappings(final CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedHeaders("POST", "PUT", "GET", "DELETE")
.allowedHeaders("*");
}
}
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable()
//if X-Forwarded-Proto then use https (heroku)
.requiresChannel()
.requestMatchers(r -> r.getHeader("X-Forwarded-Proto") != null)
.requiresSecure();
}
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
//configuration.setAllowCredentials(true);
configuration.setAllowedOrigins(Arrays.asList(CorsConfiguration.ALL));
configuration.setAllowedMethods(Arrays.asList(HttpMethod.GET.name(), HttpMethod.PUT.name(),
HttpMethod.POST.name(), HttpMethod.DELETE.name()));
configuration.setAllowedHeaders(Arrays.asList(CorsConfiguration.ALL));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
2) Spring-boot microservice (spring-boot-starter of version 2.1.0.RELEASE) Configuration
@Configuration
@EnableWebMvc
public class WebConfiguration implements WebMvcConfigurer {
@Override
public void addCorsMappings(final CorsRegistry registry) {
registry.addMapping("/**");
}
}
@EnableWebSecurity
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable()
//if X-Forwarded-Proto then use https (heroku)
.requiresChannel()
.requestMatchers(r -> r.getHeader("X-Forwarded-Proto") != null)
.requiresSecure();
}
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
//configuration.setAllowCredentials(true);
configuration.setAllowedOrigins(Arrays.asList(CorsConfiguration.ALL));
configuration.setAllowedMethods(Arrays.asList(HttpMethod.GET.name(), HttpMethod.PUT.name(),
HttpMethod.POST.name(), HttpMethod.DELETE.name()));
configuration.setAllowedHeaders(Arrays.asList(CorsConfiguration.ALL));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
Comment From: spencergibb
Greenwich.M1 is a milestone, can you update your version to Greenwich.SR1 which is the latest?
Comment From: StarReider
Thank you, it is good point. I've updated spring-cloud version of all my microservices to "Greenwich.SR1" but problem is still reproduces You can find chrome network logs in attachment Network Sniffer.zip
Comment From: ryanjbaxter
I dont understand why you think this is a problem with Zuul. Zuul is just proxing the 302 response back to your client. If your client then makes a GET as a result of that, that is out of the control of Zuul.
Comment From: spring-projects-issues
If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.
Comment From: StarReider
Because zuul makes redirection to particular service. And when zuul does it "Access-Control-Request-Method" is lost.
Comment From: gpugems
I am facing same issue when forwarding OPTIONS request. Zuul tranform that to a GET Request.
Comment From: spencergibb
This module has entered maintenance mode. This means that the Spring Cloud team will no longer be adding new features to the module. We will fix blocker bugs and security issues, and we will also consider and review small pull requests from the community.