spring-boot 1.4.0-RELEASE
spring-cloud BRIXTON.SR4
I have a Spring Boot / Cloud Gateway Service which serves a simple REST API and a web app, and also acts as a Zuul Proxy.
If I invoke the REST API through the Zuul Proxy, I get duplicated headers. Browsers dont accept the response when multiple Access-Control-Allow-Origin Headers are present.
Access-Control-Allow-Credentials:true
Access-Control-Allow-Credentials:true
Access-Control-Allow-Origin:http://localhost:3000
Access-Control-Allow-Origin:http://localhost:3000
Cache-Control:no-cache, no-store, max-age=0, must-revalidate
Content-Type:application/json;charset=UTF-8
Date:Thu, 11 Aug 2016 14:23:09 GMT
Expires:0
Pragma:no-cache
Transfer-Encoding:chunked
Vary:Origin
Vary:Origin
X-Application-Context:shipping-service:dev:8080
X-Content-Type-Options:nosniff
X-Frame-Options:DENY
X-XSS-Protection:1; mode=block
Browser Error
XMLHttpRequest cannot load http://localhost:8080/serviceA/api/serviceA/1337?cacheBuster=1470925389167. The
Access-Control-Allow-Originheader contains multiple values 'http://localhost:3000, http://localhost:3000', but only one is allowed. Origin 'http://localhost:3000' is therefore not allowed access.
I did find some issues mentioning a similar problem, but since they all had Zuul running on a separate service the fixes dont seem to work for me.
What I have tried (without success) is:
@Configuration
@EnableZuulProxy
public class SimpleCORSFilter {
@Order
@Bean
public CorsFilter corsFilter() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
final CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("OPTIONS");
config.addAllowedMethod("HEAD");
config.addAllowedMethod("GET");
config.addAllowedMethod("PUT");
config.addAllowedMethod("POST");
config.addAllowedMethod("DELETE");
config.addAllowedMethod("PATCH");
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
}
Which does give me the above mentioned duplicate errors. If I remove it, I get no Access-Control-Allow-Origin header at all, which breaks the web app similarly.
Is this a bug or did I miss something?
It appears to be caused by a local forward which I had configured manually as url: http://localhost:${server.port}
zuul:
sensitiveHeaders: Cookie,Set-Cookie
routes:
shipping:
path: /shipping/**
url: http://localhost:${server.port}
The solution is to use url: forward:, the correct configuration looks like this:
zuul:
sensitiveHeaders: Cookie,Set-Cookie
routes:
shipping:
path: /shipping/**
url: forward:/
Im still not sure why the header duplication happened with the first approach, and why it even is possible - Shouldn't Headers be in a Map having unique keys?
the HTTP spec allows for multiple headers and query parameters with the same key. Allows for a list of sorts.
Closing this due to inactivity. Please re-open if there's more to discuss.
I'm facing the same issue when requesting a resource that is redirected with zuul.
e.g.
Request URL:http://localhost:8070/user # this is mapped to http://localhost:19999/uaa/user
Origin:http://localhost:4200
response:
Access-Control-Allow-Credentials:true
Access-Control-Allow-Credentials:true
Access-Control-Allow-Headers:x-requested-with
Access-Control-Allow-Methods:POST, OPTIONS, GET
Access-Control-Allow-Origin:http://localhost:4200
Access-Control-Allow-Origin:http://localhost:4200
and the request fails:
XMLHttpRequest cannot load http://localhost:8070/user. The 'Access-Control-Allow-Origin' header contains multiple values 'http://localhost:4200, http://localhost:4200', but only one is allowed. Origin 'http://localhost:4200' is therefore not allowed access.
I'm using the Camden.SR2 spring cloud release and my zuul configuration looks like this:
zuul:
routes:
resource:
path: /api/**
url: http://localhost:8081/api
user:
path: /user/**
url: http://localhost:19999/uaa/user
I haven't tried switching the zuul path to forward. I will investigate further. I just wanted to let you know about these preliminary findings.
@IsNull my understanding of forward is that you can't use when you have to act as a reverse proxy and redirect the request to an url.
I kind of make it work by ignoring those headers that were duplicated on the zuul side by adding this to the configuration:
zuul:
ignored-headers: Access-Control-Allow-Credentials, Access-Control-Allow-Origin
I have the same problem. Duplication Access-Control-Allow-Origin.
In my case I have a spring project where I have a CORS filter where I add :
Access-Control-Allow-Origin: *
````
Zuul also if I have already set my own header to the response add also his ``` Access-Control-Allow-Origin ``` with the origin host in it . This combination bring me to have two ``` Access-Control-Allow-Origin ``` things that the normal browsers don't like too much.
In Zuul there is also this test that seems to indicate that the origin is added in the Access-Control-Allow-Origin.
```java
@Test
public void optionsOnSelf() throws Exception {
this.routes.addRoute("/self/**", "http://localhost:" + this.port + "/app/local");
this.endpoint.reset();
ResponseEntity<String> result = new TestRestTemplate().exchange(RequestEntity
.options(new URI("http://localhost:" + this.port + "/app/self/1"))
.header("Origin", "http://localhost:9000").header("Access-Control-Request-Method", "GET").build(),
String.class);
assertEquals(HttpStatus.OK, result.getStatusCode());
assertEquals("http://localhost:9000", result.getHeaders().getFirst("Access-Control-Allow-Origin"));
}
The solution I think should be to not add the Access-Control-Allow-Origin header if already set or to set only the origin host and not added it to the Access-Control-Allow-Origin header`.
Thanks to your post I solved it with:
zuul:
ignored-headers: Access-Control-Allow-Credentials, Access-Control-Allow-Origin
@nucatus @eromano or @IsNull could any of you provide a sample project?
Thanks @nucatus this line solved my problem
zuul:
ignored-headers: Access-Control-Allow-Credentials, Access-Control-Allow-Origin
Thanks
Hi,
In my case I cannot ignore the headers since it only fails when I access the resource APIs. For authorization it works (like "oauth/token") but after that, when I call a resource server API, I get the duplicated headers error.
Is there any way to avoid the duplicated headers?
@CleverMee I had the same issue; not the most "elegant" solution, but it seems to be working for me.
Create a new post-filter which inspects to see if CORS headers are duplicated, and if so, remove them from Zuul request (e.g. default to what comes from downstream). This can probably be made wider to get rid of all duplicated headers, but this specifically worked for my use case.
...
public int filterOrder() {
return SEND_RESPONSE_FILTER_ORDER - 1;
}
public boolean shouldFilter() {
return RequestContext.getCurrentContext().getZuulResponseHeaders().stream()
.anyMatch(ssp -> ssp.first().toLowerCase().equalsIgnoreCase("access-control-allow-origin") &&
RequestContext.getCurrentContext().getOriginResponseHeaders().stream()
.anyMatch(ssp -> ssp.first().toLowerCase().equalsIgnoreCase("access-control-allow-origin");
}
public Object run() throws ZuulException {
RequestContext.getCurrentContext().getZuulResponseHeaders()
.removeIf(ssp -> ssp.first().toLowerCase().startsWith("access-control-allow")
return null;
}
I'm still refactoring this and I wouldn't use it as-is, but this is perhaps a workable solution
Most helpful comment
I kind of make it work by ignoring those headers that were duplicated on the zuul side by adding this to the configuration: