'Spring Cloud Gateway with Eureka discovery enable CORS policies

I've been searching on how to enable CORS when using Spring Cloud Gateway in addition with Eureka discovery.

My application is up and running and everything works fine. As I work with Eureka discovery I enabled automatic route discovery for Spring Cloud gateway with the following property in application.properties so I don't need to specify the (custom) routing myself.

spring.cloud.gateway.discovery.locator.enabled = true

I've searched to web to no avail on how to enable CORS, as it is very poorly documented at the moment. The only thing I could find is the following documentation: https://cloud.spring.io/spring-cloud-gateway/single/spring-cloud-gateway.html#_header_route_predicate_factory where it is mentiond in section 5.14 that some default security headers are applied and how to change them.

It says header X-Permitted-Cross-Domain-Policies is default set to none which means CORS is disabled.

The documentation tells me I can change this header by setting property in my application.properties file.

spring.cloud.gateway.filter.secure-headers.permitted-cross-domain-policies = ?

But I have no idea to what value I need to set this property and it isn't documented anywhere. I have tried setting it to 'any', 'all' and '*' but with no luck.

Can someone help or does anybody know the answer to this? Or if i misinterpreted something please let me know.



Solution 1:[1]

No,you can't set additional response headers when DiscoveryClient Route Definition Locator is enabled by spring.cloud.gateway.discovery.locator.enabled = true.

You should define your own route and add AddResponseHeader to filters in application.properties or application.properties,Here is an example for your reference.

spring:
  cloud:
      routes:
      - id: foo_service
        uri: lb://foo_service
        predicates:
        - Path=/foo_service/**
        filters:
        - AddResponseHeader=Access-Control-Allow-Origin, *
        - RewritePath=/foo_service/(?<segment>.*), /$\{segment}

Solution 2:[2]

OK, its a bit late but i have and answer. Just put CORS as global Filter:

spring.cloud.gateway.globalcors.corsConfigurations.[/**].allowedOriginPatterns=*
spring.cloud.gateway.globalcors.corsConfigurations.[/**].allowCredentials=true
spring.cloud.gateway.globalcors.corsConfigurations.[/**].allowedMethods=*
spring.cloud.gateway.globalcors.corsConfigurations.[/**].allowedHeaders=*

This is for application.properties but its the same way for yml.

Going a little deeper on CORS with eureka auto-discovery enable, it will give u a 404 without cors headers if your service is offline. It took me a while but i got to this code:

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.cors.reactive.CorsUtils;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;

import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

@Component
public class CustomWebFilter implements WebFilter {

    private final static List<String> gateway404Headers = List.of("transfer-encoding", "Content-Type", "Content-Length");

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        exchange.getResponse().beforeCommit(() -> {
            if(isBlockedByGateway(exchange.getResponse())) {
                addCors(exchange);
            }
            return Mono.empty();
        });
        return chain.filter(exchange);
    }

    private boolean isBlockedByGateway(ServerHttpResponse response) {
        return response.getStatusCode()== HttpStatus.NOT_FOUND && response.getHeaders().size()==3
                && checkHeaders(response.getHeaders());
    }

    private boolean checkHeaders(HttpHeaders headers) {
        AtomicBoolean allHeadersValid = new AtomicBoolean(true);
        headers.forEach((header, headerUtils) -> {
            allHeadersValid.set(allHeadersValid.get() && gateway404Headers.contains(header));
        });
        return allHeadersValid.get();
    }

    public void addCors(ServerWebExchange exchange) {
        if (CorsUtils.isCorsRequest(exchange.getRequest())) {
            HttpHeaders headers = exchange.getResponse().getHeaders();
            List<String> headerslist = exchange.getRequest().getHeaders().get("Origin");
            if(headerslist != null && headerslist.size() == 1) {
                headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, headerslist.get(0));
                headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
            }
        }
    }
}

I dont think its a good solution though, at least not for my purpose, but migth be useful for someone.

Solution 3:[3]

Please refer below link, if your format in yml is wrong then correct it as per example shared. My problem got fixed by this change.

https://github.com/spring-cloud/spring-cloud-gateway/issues/1922

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 chao_chang
Solution 2 Vitor Sulzbach
Solution 3 user18856742