Skip to content

CorsGatewayFilterApplicationListener loses CORS path priority due to unordered HashMap use #3805

@yavor300

Description

@yavor300

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    Status

    Done

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions