spring-cloud-gateway icon indicating copy to clipboard operation
spring-cloud-gateway copied to clipboard

Support for routing cross-origin OPTIONS requests

Open thammerl opened this issue 6 years ago • 19 comments

It seems that it's currently not possible to route cross-origin OPTIONS requests with Spring Cloud Gateway to other backend services. This would be desirable so that the components serving the requests can decide on their own which CORS headers to return to their clients. As far as I know it's currently only possible to handle CORS within the gateway itself via a global configuration.

I've created a pull request in my forked repository that adds a failing test to the spring-cloud-gateway-sample application.

There's already been a discussion on the topic in #229 starting with this comment.

thammerl avatar Feb 11 '19 22:02 thammerl

Closing in favor of #840 that has a link to a PR

spencergibb avatar Feb 25 '19 15:02 spencergibb

I don't think that the PR for #840 accomplishes what this issue asks for. If I understand the PR correctly, it adds an option to apply the global configuration for requests that are not routed by the gateway. This feature request is about adding support for routing preflight requests to other backend services.

@tony-clarke-amdocs May I ask you to confirm this?

thammerl avatar Feb 25 '19 15:02 thammerl

Thanks for the clarification.

spencergibb avatar Feb 25 '19 16:02 spencergibb

PRs welcome

spencergibb avatar Feb 25 '19 16:02 spencergibb

+1

howardem avatar Mar 15 '19 19:03 howardem

+1

renecairo avatar Mar 15 '19 19:03 renecairo

Please stop commenting +1 and just add a thumbs up on the original comment

spencergibb avatar Mar 15 '19 19:03 spencergibb

Faced this issue when tried to pass preflight request through API gateway.

The core of issue is AbstractHandlerMapping from webflux. I just briefly observed webflux sources and still not so familiar with ideology but from the first look webflux processes OPTIONS (and looking deeper - preflights) in very specific way.

For me fix looks like the following:

public class GatewayCustomConfiguration extends GatewayAutoConfiguration {

    @Bean
    public RoutePredicateHandlerMapping routePredicateHandlerMapping(FilteringWebHandler webHandler,
                 RouteLocator routeLocator, GlobalCorsProperties globalCorsProperties, Environment environment) {

        return new RoutePredicateHandlerMapping(webHandler, routeLocator, globalCorsProperties, environment){

            public Mono<Object> getHandler(ServerWebExchange exchange) {
                Mono<Object> handler = getHandlerInternal(exchange).map((hndl) -> {return hndl;});
                return CorsUtils.isPreFlightRequest(exchange.getRequest()) ? handler : super.getHandler(exchange);
            }
        };
    }
}

and

spring.cloud.gateway.enabled = false

That works for me as hotfix but I don't like it because I cannot predict all drawbacks.

A-n-d-r-e-i-P avatar Apr 07 '19 20:04 A-n-d-r-e-i-P

PRs welcome

@spencergibb Do you have an idea what you expect to be the real functional solution? Is it something as simple as a configuration option that (e.g. spring.cloud.gateway.globalcors.route=true) that does effectively what @A-n-d-r-e-i-P suggested?

Or perhaps RoutePredicateHandlerMapping could always pass the request to the handler, then a filter could be provided that allows for more fine grain control on a route by route basis. That feels maybe like the best approach.

I'm trying to understand what the most ideal solution would be that plays well with AbstractHandlerMapping?

workmanw avatar Jul 03 '19 23:07 workmanw

Maybe the property name should be something like spring.cloud.gateway.globalcors.preflight.passthrough=true? (I've not looked into how to implement it yet)

tony-clarke-amdocs avatar Jul 03 '19 23:07 tony-clarke-amdocs

Have the same problem. Use SpringBoot webflux and SpringCloud Gateway ,can't forward OPTIONS requests with Spring Cloud Gateway to out backend service. So I overwite "org.springframework.web.cors.reactive.CorsUtils" class, just make isPreFlightRequest always return false , that solved my problem.

 public static boolean isPreFlightRequest(ServerHttpRequest request) {
        //HttpHeaders headers = request.getHeaders();
        //return (request.getMethod() == HttpMethod.OPTIONS
        //        && headers.containsKey(HttpHeaders.ORIGIN)
        //        && headers.containsKey(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD));
        return false;
    }

superbool avatar Sep 11 '20 08:09 superbool

@spencergibb, would it be appropriate to override org.springframework.web.cors.reactive.CorsUtils#isPreFlightReuqest as proposed by @superbool and make this configureable with spring.cloud.gateway.globalcors.preflight.passthrough=true? I could open a PR...

tillmannheigel avatar Jul 22 '21 13:07 tillmannheigel

Have the same problem. Use SpringBoot webflux and SpringCloud Gateway ,can't forward OPTIONS requests with Spring Cloud Gateway to out backend service. So I overwite "org.springframework.web.cors.reactive.CorsUtils" class, just make isPreFlightRequest always return false , that solved my problem.

 public static boolean isPreFlightRequest(ServerHttpRequest request) {
        //HttpHeaders headers = request.getHeaders();
        //return (request.getMethod() == HttpMethod.OPTIONS
        //        && headers.containsKey(HttpHeaders.ORIGIN)
        //        && headers.containsKey(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD));
        return false;
    }

You should consider the other public methods as well to not get a NoSuchMethod Exception occasionally:

public class CorsUtils {

    public static boolean isCorsRequest(ServerHttpRequest request) {
        return false;
    }

    public static boolean isPreFlightRequest(ServerHttpRequest request) {
        return false;
    }

    public static boolean isSameOrigin(ServerHttpRequest request) {
        return false;
    }
}

tillmannheigel avatar Jul 22 '21 15:07 tillmannheigel

@tillmannheigel I tried this code:

import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.cors.reactive.CorsUtils;

@Component
public class CustomPostGlobalFilter extends CorsUtils {

    public static boolean isCorsRequest(ServerHttpRequest request) {
        return false;
    }

    public static boolean isPreFlightRequest(ServerHttpRequest request) {
        return false;
    }

    public static boolean isSameOrigin(ServerHttpRequest request) {
        return false;
    }
}

But again OPTIONS request is forwarded. Can you advice what is the proper way to implement the quick fix, please?

rcbandit111 avatar Jan 09 '22 13:01 rcbandit111

@tillmannheigel I tried this code:

import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.cors.reactive.CorsUtils;

@Component
public class CustomPostGlobalFilter extends CorsUtils {

    public static boolean isCorsRequest(ServerHttpRequest request) {
        return false;
    }

    public static boolean isPreFlightRequest(ServerHttpRequest request) {
        return false;
    }

    public static boolean isSameOrigin(ServerHttpRequest request) {
        return false;
    }
}

But again OPTIONS request is forwarded. Can you advice what is the proper way to implement the quick fix, please?

Not extends CorsUtils ,but overwite CorsUtils abstract class.

superbool avatar Jan 11 '22 01:01 superbool

@superbool Can you show me code example, please?

rcbandit111 avatar Jan 11 '22 08:01 rcbandit111

@superbool Can you show me code example, please?

Copy org.springframework.web.cors.reactive.CorsUtils.java source code to your own project,and overwrite isPreFlightRequest method. It looks like this。 image

superbool avatar Jan 11 '22 15:01 superbool

Is there any Update on this?

@superbool Your suggested Solution works! Thank you. ❤️

I am searching for a way to tell Spring Reactive to handle Preflight Requests like the any other Request. I faced this Issue when using Spring Cloud Gateway and posting a Request to an API. The Preflight-Request is Rejected with 403. I found many Threads about similar Issues and many people suggest implementing a Cors Filter. But I do not think that the Gateway should worry about CORS. Therefore I would like to pass this Preflight request to the API.

valcorn-dev avatar Jan 24 '23 12:01 valcorn-dev

I've been troubled by this problem for a long time. If we haven't configured the gateway to perform certain tasks, the gateway should be completely transparent.

image

If there is no config, just return true. But this class org.springframework.web.cors.reactive.DefaultCorsProcessor is in spring-web, not the gateway.

libinglong avatar Jun 15 '24 16:06 libinglong

If there is no config, just return true. But this class org.springframework.web.cors.reactive.DefaultCorsProcessor is in spring-web, not the gateway.

Even if this class could return true (for example by not being hard instantiated with "new" but autowired instead so we could offer our own implementation. AbstractHandlerMapping.java does

 if (!this.corsProcessor.process(config, exchange) || CorsUtils.isPreFlightRequest(request)) {
         return NO_OP_HANDLER;
 }

so as you see returning true from the first statement would not solve it. PreFlight requests are doomed.

alianos- avatar Jan 09 '25 08:01 alianos-