Configuring CORS in Spring Cloud Gateway
Spring Cloud Gateway, built on the Spring Framework, serves as an API gateway for microservices architectures. It provides a mechanism to handle Cross-Origin Resource Sharing (CORS), a common requirement when web applications served from one origin need to make requests to APIs hosted on a different origin. The browser's same-origin policy blocks such requests by default. Gateway can be configured to add the necessary HTTP response headers, allowing controlled cross-origin communication.
Understanding CORS
A cross-origin request occurs when the protocol, domain, or port of a requesting webpage differs from the API it's calling. For instance:
- Different domains:
app.example.comcallingapi.service.net. - Same domain, different ports:
localhost:3000callinglocalhost:8080.
CORS is a W3C standard that uses HTTP headers to inform a browser which origins are permitted to access resources from a server. The server responds with headers like Access-Control-Allow-Origin to grant permission. Spring Cloud Gateway can be configured to apply these headers globally.
Implementation Methods
1. Using Configuration Properties
The primary and recommended method is to define CORS rules in the application.yml or application.properties file. This approach is declarative and leverages Gateway's built-in CORS support.
spring:
cloud:
gateway:
globalcors:
add-to-simple-url-handler-mapping: true # Handles preflight OPTIONS requests
cors-configurations:
'[/**]': # Apply to all routes
allowed-origins:
- "https://frontend-app.com"
- "http://localhost:3000"
allowed-methods:
- "GET"
- "POST"
- "PUT"
- "DELETE"
- "OPTIONS"
allowed-headers: "*"
allow-credentials: true
max-age: 7200 # Cache duration for preflight response in seconds
Key configuration properties:
allowed-origins: Specifies the permitted origin URLs. Use"*"to allow any origin (not recommended with credentials).allowed-methods: Defines the HTTP verbs allowed for cross-origin requests.allowed-headers: Lists request headers the client can use."*"permits all.allow-credentials: Whentrue, allows cookies or authorization headers.max-age: How long (seconds) the browser caches the preflight response.
2. Creating a Global Filter (Programmatic Approach)
For more complex logic, you can implement a custom global filter. This provides full control over the CORS headers.
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.cors.reactive.CorsUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Component
public class CustomCorsFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
HttpHeaders headers = response.getHeaders();
// Handle preflight request
if (CorsUtils.isCorsRequest(request)) {
headers.add("Access-Control-Allow-Origin", "https://trusted-client.org");
headers.add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
headers.add("Access-Control-Allow-Headers", "Authorization, Content-Type");
headers.add("Access-Control-Allow-Credentials", "true");
headers.add("Access-Control-Max-Age", "7200");
if (request.getMethod() == HttpMethod.OPTIONS) {
response.setStatusCode(HttpStatus.OK);
return Mono.empty();
}
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
}
This filter checks for CORS requests, sets the appropriate headers, and handles OPTIONS preflight requests by returning an immediate successful response.
Key Considerations
- Security: Avoid using
"*"forallowed-originsin production, especially whenallow-credentialsistrue. Explicitly list trusted origins. - Preflight Requests: The
add-to-simple-url-handler-mapping: trueproperty or the custom filter's OPTIONS handling is crucial. Browsers send an OPTIONS request before the actual request to check permissions. - Order of Precedence: Programmatic filters run before route predicates. Ensure CORS headers are set early in the filter chain by implementing the
Orderedinterface. - Testing: Verify CORS configuration using browser developer tools or tools like
curlto inspect response headers.