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

CorsGatewayFilterApplicationListener loses CORS path priority due to unordered HashMap use

Open yavor300 opened this issue 8 months ago • 3 comments

Description

Hi team,

I am encountering an issue in spring-cloud-gateway related to how CORS configurations are handled during route refresh.

The GlobalCorsProperties class stores the CORS configurations using a LinkedHashMap to preserve insertion order:

@ConfigurationProperties("spring.cloud.gateway.globalcors")
public class GlobalCorsProperties {

    private final Map<String, CorsConfiguration> corsConfigurations = new LinkedHashMap<>();

    public Map<String, CorsConfiguration> getCorsConfigurations() {
        return corsConfigurations;
    }
}

However, in the CorsGatewayFilterApplicationListener implementation, this insertion order is lost due to the following line:

var corsConfigurations = new HashMap<>(globalCorsProperties.getCorsConfigurations());

This results in the CORS rules being reordered, which affects route specificity resolution. For example, in the following YAML configuration:

corsConfigurations:
  '[/api/v2/sensitive/**]':
    allowedOriginPatterns: "*"
    allowCredentials: true
    allowedMethods:
      - GET
      - POST
    allowedHeaders: '*'
    exposedHeaders:
      - X-Custom-Header

  '[/api/**]':
    allowedOriginPatterns:
      - "https://example-docs.domain.com"
      - "https://internal-tools.domain.net"
    allowCredentials: true
    allowedMethods:
      - GET
      - POST
    allowedHeaders: '*'

We observe that the [/api/**] rule is sometimes evaluated before the more specific [/api/v2/sensitive/**], resulting in CORS exceptions for requests that should match the specific rule.

Proposed Fix

To preserve the intended rule order and avoid these subtle and hard-to-debug issues, I propose changing:

var corsConfigurations = new HashMap<>(globalCorsProperties.getCorsConfigurations());

to:

var corsConfigurations = new LinkedHashMap<>(globalCorsProperties.getCorsConfigurations());

This change would ensure the ordering defined in configuration files is respected throughout the lifecycle of the application.

Contribution

If you agree with this change, I will send you a pull request with the update.

Let me know your thoughts.

Versions

The issue was observed using the following dependencies:

  • org.springframework.boot:spring-boot-gradle-plugin:3.4.5
  • org.springframework.boot:spring-boot-dependencies:3.4.5
  • org.springframework.cloud:spring-cloud-dependencies:2024.0.1
  • org.springframework.cloud.spring-cloud-gateway-server:4.2.1

yavor300 avatar May 22 '25 17:05 yavor300

@spencergibb @ryanjbaxter Can you share your feedback on this issue whenever possible?

yavor300 avatar Jun 05 '25 08:06 yavor300

PRs welcome, the fix sounds reasonable

spencergibb avatar Jun 05 '25 13:06 spencergibb

@spencergibb @ryanjbaxter A PR has been submitted that includes a test class verifying the insertion order of the merged cors configurations. When you have a chance, please review it and feel free to share any feedback

yavor300 avatar Jun 13 '25 13:06 yavor300