CorsGatewayFilterApplicationListener loses CORS path priority due to unordered HashMap use
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
@spencergibb @ryanjbaxter Can you share your feedback on this issue whenever possible?
PRs welcome, the fix sounds reasonable
@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