Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Global CORS configuration with GlobalFilter #1690

Closed
kadimulam opened this issue Apr 26, 2020 · 19 comments
Closed

Global CORS configuration with GlobalFilter #1690

kadimulam opened this issue Apr 26, 2020 · 19 comments

Comments

@kadimulam
Copy link

We are using Global CORS configurations with GlobalFilter as shown below

spring:
  cloud:
    gateway:
      default-filters:
          - DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin, RETAIN_UNIQUE
      globalcors:
       add-to-simple-url-handler-mapping: true
        corsConfigurations:
          '[/**]':
            allowedOrigins: "*.ourdomain.com"
            allowCredentials: true
            allowedMethods:
            - GET
            - POST
            - DELETE
            - PUT
            - OPTIONS
     routes:
       - id: my-service
        ......
        .....

MyGlobalFilter.java

@Component
public class MyGlobalFilter implements GlobalFilter {

    @Override
    public Mono < Void > filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        if (exchange.getMethod().equals(HTTPMethod.OPTIONS)) {
            exchange.getResponse().setStatusCode(HttpStatus.OK);
            return exchange.getResponse().setComplete();
        }

        //Our some logic
        return chain.filter(exchange);
    }

    @override
    public int getOrder() {
        return -1;
    }
}

With the above configuration, the request comes into GlobalFilter and satisfies the above OPTIONS if conditions for POST calls and returns 200 but we do not see the CORS headers set on the browser. Our actual POST call does not happen and the request does not proceed. Is this the correct approach for handling CORS with GlobalFilter?

For OPTIONS call is it possible to send 200 OK directly from bootstrap.yaml config itself instead of entering into GlobalFilter and then handling OPTIONS? Please advise.

@spencergibb
Copy link
Member

I'm afraid I don't understand this question. Can you be more specific?

For OPTIONS call is it possible to send 200 OK directly from bootstrap.yaml config itself instead of entering into GlobalFilter and then handling OPTIONS?

@tony-clarke-amdocs
Copy link
Contributor

@kadimulam What are you trying to achieve with the global filter?

@tony-clarke-amdocs
Copy link
Contributor

@kadimulam Can you provide a GIT repo that we can clone with a unit test that illustrates the failure?
It will just remove any ambiguity if we can see a full repo that we can clone.

@kadimulam
Copy link
Author

I'm afraid I don't understand this question. Can you be more specific?

For OPTIONS call is it possible to send 200 OK directly from bootstrap.yaml config itself instead of entering into GlobalFilter and then handling OPTIONS?

@spencergibb , sorry if i did not explain properly.
Lets assume there is a POST call from UI that will result in CORS issue. My question is can we handle the OPTIONS request with the configuration (bootstrap.yaml)? That is whenever an OPTIONS request comes, is there a way Spring Cloud Gateway can send 200 OK and populate all appropriate response headers; that way the subsequent POST request does not fail with CORS issue?

@kadimulam
Copy link
Author

@kadimulam Can you provide a GIT repo that we can clone with a unit test that illustrates the failure?
It will just remove any ambiguity if we can see a full repo that we can clone.

@tony-clarke-amdocs , unfortunately i cannot share the repo. In GlobalFilter i have some business logic that gets executed for all the requests. Within GlobalFilter i added the above IF condition for OPTIONS call as the request is first received by GlobalFilter.

@kadimulam
Copy link
Author

@spencergibb , i can do some research and fix my issue if you could provide some documentation which shows how CORS should be configured when using GlobalFilter.

@tony-clarke-amdocs
Copy link
Contributor

I am not following:

  • Why are you referencing bootstrap.yaml. Configuration should be in application.yaml

  • Yes Spring Cloud Gateway can populate the CORS headers in the response. You can find the unit tests in the repo for it.

  • Why are you implementing some Default filter? That is not necessary to get CORS working.

@spencergibb
Copy link
Member

Also, we don't want your private repo, just a minimal one that reproduces the problem.

@kadimulam
Copy link
Author

I am not following:

  • Why are you referencing bootstrap.yaml. Configuration should be in application.yaml
  • Yes Spring Cloud Gateway can populate the CORS headers in the response. You can find the unit tests in the repo for it.
  • Why are you implementing some Default filter? That is not necessary to get CORS working.

@tony-clarke-amdocs

  1. We configured our routes in bootstrap.yaml instead of application.yaml; will that be an issue?
  2. What is the minimal configuration needed for cors configuration?
  3. GlobalFilter has some business logic that needs to be executed for every request based on the Cookie. This filter is not for CORS. I can remove the OPTIONS IF condition that i added. The only reason I added the IF condition because Spring Cloud Gateway is sending the OPTIONS request to GlobalFilter. I was under the impression that the globalcors configuration that i put in bootstrap.yaml will handle it and OPTIONS request will not be intercepted by GlobalFilter

@tony-clarke-amdocs
Copy link
Contributor

  1. It should be fine to use bootstrap.yaml - it's just a little odd in this use case
  2. Your configuration looks fine.
  3. I think you are correct, when preflight requests are handled by spring, it would not be handled any spring cloud gateway filters.
    It would really help if you can provide a standalone Git repo that minimally demonstrates the issue.

@kadimulam
Copy link
Author

@tony-clarke-amdocs , @spencergibb when we disabled MyGlobalFilter then OPTIONS calls are working as expected and POST calls are successful.

It's only when MyGlobalFilter is configured, OPTIONS calls are hitting MyGlobalFilter. As of now the Order is HIGHEST_PRECEDENCE, how do I reduce the precedence that way CORS filter executes first and then MyGlobalFilter. I tried to set MyGlobalFilter to '0', but it didn't work (I mean OPTIONs calls are hitting MyGlobalFilter).

@tony-clarke-amdocs
Copy link
Contributor

If the CorsPreFlight processor runs then the remaining handler worker is shortcut and just the CORS headers are returned. You can see this here. But you are saying the opposite. If you can provide a Git repo to look at I can look into it more.

@kadimulam
Copy link
Author

@spencergibb , @tony-clarke-amdocs , please find a dummy implementation which has the issue i am facing.

1) After running the Spring Cloud Gateway project and the Controller, run OPTIONS call for below endpoint. You will see MyGlobalFilter gets triggered. Is this the expected behavior? If this gets triggered there is no point in configuring the global cors. I believe developer should not code for OPTIONS inside MyGlobalFilter. Do you agree?

curl OPTIONS 'http://localhost:8080/api/v1/hello-world/hello'

2) Remove MyGlobalFilter, deploy Spring Cloud Gateway and rerun above OPTIONS call. It will send 200OK. This is expected and I would expect the same behavior even when i have MyGlobalFilter.

Based on my analysis, I believe somewhere we are overriding the precedence of global cors in #1 scenario above. I have provided all the information including sample project for troubleshooting the issue. Please advise.

This is dummy Spring Cloud Gateway project which runs on 8080
my-cloud-gateway.zip

Place this Controller in any of the SpringBoot project and run locally on 8081
HelloWorldController.zip

@tony-clarke-amdocs
Copy link
Contributor

A number of follow up points:

  1. Your curl command is incorrect. I assume you mean curl -X OPTIONS...?
  2. But even the above corrected curl command is insufficient since you are not sending the ORIGIN header. So let's update it to something like: curl -i -X OPTIONS -H "Origin: foo.com" 'http://localhost:8080/api/v1/hello-world/hello'
  3. With the above we see the correct CORS headers being returned. However it is not a CORS preflight request and hence the rest of the chain runs.
  4. If we send a preflight request: curl -i -X OPTIONS -H "Origin: foo.com" -H "Access-Control-Request-Method: GET" 'http://localhost:8080/api/v1/hello-world/hello' Then we get all the CORS headers in the response and your global filter is not executed.

Everything looks to be behaving correctly in the APIGW.

@kadimulam
Copy link
Author

kadimulam commented Apr 28, 2020

@tony-clarke-amdocs , when i added Access-Control-Request-Method it worked. Thank you!

And one more issue with my configuration was there was a typo in field 'corsConfigurations'. On my actual repo I specified as 'corsConfiguration' and that is the reason it did not work from application UI. I will correct it and verify from UI.

One more thing, how do i pass allowedOrigins to allow range of subdomains?
For example with the below configuration, when i pass --header 'Origin: myapp.ourdomain.com' it is literally looking for *.ourdomain.com and giving 403; I want to allow all the sub domains like myapp.ourdomain.com, myservice.ourdomain.com etc. How do i pass allowedOrigins to match above criteria?

spring:
  cloud:
    gateway:
      default-filters:
          - DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin, RETAIN_UNIQUE
      globalcors:
       add-to-simple-url-handler-mapping: true
        corsConfigurations:
          '[/**]':
            allowedOrigins: 
             - "*.ourdomain.com"
             - "*.ourotherdomain.com"
            allowCredentials: true
            allowedMethods:
            - GET
            - POST
            - DELETE
            - PUT
            - OPTIONS
     routes:
       - id: my-service
        ......
        .....

@tony-clarke-amdocs
Copy link
Contributor

tony-clarke-amdocs commented Apr 28, 2020

As far as I can tell Spring supports "*" to permit all origins...but I don't think you can specify a wild card to represent a subdomain. @rstoyanchev might be able to confirm/deny.

@spencergibb
Copy link
Member

I'm going to go ahead and close this as working. Feel free to continue the conversation though. Thanks @tony-clarke-amdocs for the help.

@rstoyanchev
Copy link

@sumitbadwal
Copy link

I've faced similar problems like @kadimulam
I'm getting following error if I add Bean of CorsWebFilter
"Access to XMLHttpRequest at 'http://myserver.com/api/' from origin 'http://localhost:3000' has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header contains multiple values '*, *', but only one is allowed."

If I follow @kadimulam approach of configuring CORS in application.yml file then I get
"Access to XMLHttpRequest at 'http://myserver.com/api/' from origin 'http://localhost:3000' 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."

I'm using APIGW 2.2.6.RELEASE

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants