Gateway Service Gateway

📢 This article was translated by gemini-2.5-flash

Gateway acts as the unified entry point for all microservices. It handles the following:

  • Authentication and Authorization: As the microservice entry, the gateway verifies if a user is authorized to make a request. If not, it blocks the request.
  • Request Routing, Load Balancing: All requests must pass through the gateway first. The gateway doesn’t handle business logic directly. Instead, it forwards requests to a specific microservice based on defined rules – that’s routing. If multiple target services are available for a route, load balancing is also applied.
  • Request Rate Limiting: When traffic is too high, the gateway throttles requests to match the downstream microservice’s capacity, preventing services from getting overloaded.

Spring Cloud offers two gateway implementations:

  • Zuul: Built on Servlet, uses blocking I/O.
  • SpringCloudGateway: Based on Spring5’s WebFlux, it’s a reactive programming implementation with better performance.

Getting Started

Create a project and add the dependencies:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<!-- Gateway -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- Nacos service discovery dependency -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

Write the basic configuration and routing rules:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
server:
  port: 10010 # Gateway port
spring:
  application:
    name: gateway # Service name
  cloud:
    nacos:
      server-addr: localhost:8848 # Nacos address
    gateway:
      routes: # Gateway routing configuration
        - id: user-service # Route ID, custom, must be unique
          # uri: http://127.0.0.1:8081 # Target address for the route. 'http' means a fixed address.
          uri: lb://userService # Target address for the route. 'lb' means load balancing, followed by the service name.
          predicates: # Route predicates, conditions to determine if a request matches the route.
            - Path=/user/** # This matches by path. Any path starting with /user/ will match.

Then, visit localhost:10010/user/1 to test it.

Routing configuration includes:

  1. Route ID: The unique identifier for the route.
  2. Route Target (URI): The target address for the route. ‘http’ means a fixed address, ’lb’ means load balancing based on service name.
  3. Route Predicates: Rules that determine if a request matches the route. If it matches, the request is forwarded to the route’s destination.
  4. Route Filters: Process requests or responses.

Predicate Factories

The predicate rules defined in the configuration are just strings. These strings are read and processed by Predicate Factories, turning them into conditions for route matching.

For example, the Path=/user/** rule from above matches by path and is handled by the org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory class. Similar predicate factories include:

NameDescriptionExample
AfterRequests after a certain time- After=2037-01-20T17:42:47.789-07:00[America/Denver]
BeforeRequests before a certain time- Before=2031-04-13T15:14:47.433+08:00[Asia/Shanghai]
BetweenRequests between two specific times- Between=2037-01-20T17:42:47.789-07:00[America/Denver], 2037-01-21T17:42:47.789-07:00[America/Denver]
CookieRequest must include certain cookies- Cookie=chocolate, ch.p
HeaderRequest must include certain headers- Header=X-Request-Id, \d+
HostRequest must be for a specific host (domain)- Host=.somehost.org,.anotherhost.org
MethodRequest method must be specified- Method=GET,POST
PathRequest path must match specified rules- Path=/red/{segment},/blue/**
QueryRequest parameters must include specified parameters- Query=name, Jack or - Query=name
RemoteAddrRequester’s IP must be within a specified range- RemoteAddr=192.168.1.1/24
WeightWeight handling

Filter Factories

A GatewayFilter is a type of filter provided in the gateway that can process requests entering the gateway and responses returned by microservices.

Spring provides 31 different route filter factories, for example:

NameDescription
AddRequestHeaderAdds a request header to the current request
RemoveRequestHeaderRemoves a request header from the request
AddResponseHeaderAdds a response header to the response
RemoveResponseHeaderRemoves a response header from the response
RequestRateLimiterLimits request traffic

For more details, see: https://docs.spring.io/spring-cloud-gateway/reference/spring-cloud-gateway/gatewayfilter-factories.html

Request Header Filter

Let’s take the request header filter as an example: add a request header ‘Hello=World’ to all requests going to ‘userService’.

Modify application.yml in your gateway project to add a route filter:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
spring:
  cloud:
    gateway:
      routes:
      - id: user-service 
        uri: lb://userservice 
        predicates: 
        - Path=/user/** 
        filters: # Filters
        - AddRequestHeader=Hello, World # Add request header

Then, you can modify the Controller to test it:

1
2
3
4
5
@GetMapping("/{id}")
public User queryById(@PathVariable("id") Long id, @RequestHeader("Hello") String hello) {
    System.out.println(hello);
    return userService.queryById(id);
}

Default Filters

If you want a filter factory to apply to all routes, you can put it under ‘default-filters’.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
spring:
  cloud:
    gateway:
      routes:
      - id: user-service 
        uri: lb://userservice 
        predicates: 
        - Path=/user/**
      default-filters: # Default filter items
      - AddRequestHeader=Hello, World

Global Filters

The gateway provides 31 types of filters, but each has a fixed purpose. If we want to intercept requests and implement our own custom business logic, these fixed filters won’t cut it.

Global Filters, on the other hand, also process all requests entering the gateway and responses from microservices, just like GatewayFilters. The difference is that GatewayFilters are configuration-driven with fixed logic, while GlobalFilter logic requires custom code by implementing the GlobalFilter interface.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public interface GlobalFilter {
    /**
     *  Processes the current request. If necessary, it passes the request to the next filter via {@link GatewayFilterChain}.
     *
     * @param exchange The request context, where you can get Request, Response, and other info.
     * @param chain Used to delegate the request to the next filter. 
     * @return {@code Mono<Void>} Returns to indicate the current filter's business logic is complete.
     */
    Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);
}

By writing custom logic in the filter, you can achieve the following functionalities:

  • Login status check
  • Permission validation
  • Request rate limiting, etc.

Basic Usage

Goal: Define a global filter to intercept requests and check if the request parameters meet the following conditions:

  • Does the parameter contain ‘authorization’?
  • Is the ‘authorization’ parameter value ‘admin’?

If both conditions are met, proceed; otherwise, block the request.

First, define a filter in the gateway:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
@Order(-1) // Process first
@Component
public class AuthorizeFilter implements GlobalFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // Get request parameters
        MultiValueMap<String, String> params = exchange.getRequest().getQueryParams();
        // Get the 'AuthorizeFilter' parameter. Here, we assume the first one is the authentication parameter.
        String authorize = params.getFirst("AuthorizeFilter");
        // Check
        if("admin".equals(authorize)){
            // Proceed
            return chain.filter(exchange);
        }
        // Intercept, forbid access
        exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
        // Finish processing
        return exchange.getResponse().setComplete();
    }
}

Filter Execution Order

When a request enters the gateway, it encounters three types of filters: current route filters, Default Filters, and Global Filters.

After routing, the current route filters, Default Filters, and Global Filters are merged into a single filter chain (collection). They are then sorted and executed sequentially.

Each filter has an integer ‘order’ value. A smaller ‘order’ value means higher priority and earlier execution.

The ‘order’ values for route filters and default filters are assigned by Spring, defaulting to an incrementing sequence starting from 1.

GlobalFilters can specify their ‘order’ value by implementing the ‘Ordered’ interface or by adding the ‘@Order’ annotation. We define these values.

If filters have the same ‘order’ value, they are executed in the sequence: DefaultFilter > Route Filter > GlobalFilter.

Here’s how to specify the order value by implementing an interface:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//@Order(-1) // Process first
@Component
public class AuthorizeFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // Get request parameters
        MultiValueMap<String, String> params = exchange.getRequest().getQueryParams();
        // Get the 'AuthorizeFilter' parameter. Here, we assume the first one is the authentication parameter.
        String authorize = params.getFirst("AuthorizeFilter");
        // Check
        if("admin".equals(authorize)){
            // Proceed
            return chain.filter(exchange);
        }
        // Intercept, forbid access
        exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
        // Finish processing
        return exchange.getResponse().setComplete();
    }

    @Override
    public int getOrder() {
        return -1;
    }
}

Cross-Origin (CORS) Issues

Same origin means same protocol, same IP or domain, and same port.

A cross-origin issue occurs when the browser blocks an AJAX request from the requester to a server if they are from different origins. The solution is CORS.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
spring:
  cloud:
    gateway:
      globalcors: # Global CORS handling
        add-to-simple-url-handler-mapping: true # Fixes issues with OPTIONS requests being blocked
        corsConfigurations:
          '[/**]':
            allowedOrigins: # Which origins are allowed to make cross-origin requests
              - "http://localhost:8090"
            allowedMethods: # Allowed cross-origin AJAX request methods
              - "GET"
              - "POST"
              - "DELETE"
              - "PUT"
              - "OPTIONS"
            allowedHeaders: "*" # Allowed headers to be carried in the request
            allowCredentials: true # Whether to allow carrying cookies
            maxAge: 360000 # Validity period for this CORS preflight check