Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Configuring CORS in Spring Cloud Gateway

Tech May 11 3

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.com calling api.service.net.
  • Same domain, different ports: localhost:3000 calling localhost: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: When true, 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 "*" for allowed-origins in production, especially when allow-credentials is true. Explicitly list trusted origins.
  • Preflight Requests: The add-to-simple-url-handler-mapping: true property 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 Ordered interface.
  • Testing: Verify CORS configuration using browser developer tools or tools like curl to inspect response headers.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.