Open In App

Rate Limiting With Client IP in Spring Cloud Gateway

Rate limiting is a controlling strategy for the request rate of a network, server, or other resource. Its purpose is to prevent excessive or abusive resource use while ensuring it is available to all users. There exist multiple approaches for executing rate limitations. A popular method is setting over most requests a user or client may make in a certain amount of time, such as a minute or an hour. The user's subsequent requests can be rejected or delayed until the rate limit is reset if they are over this restriction.

Example of Rate Limiting With Client IP in Spring Cloud Gateway:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Optional;

@Configuration
public class RateLimitingConfig {

    @Autowired
    private CustomKeyResolver customKeyResolver;

    @Bean
    public CustomKeyResolver customKeyResolver() {
        return new CustomKeyResolver();
    }

    @Bean
    public KeyResolver ipKeyResolver() {
        return exchange -> {
            // Extracting the client IP address from request
            String ip = Optional.ofNullable(exchange.getRequest().getRemoteAddress())
                                .map(address -> address.getAddress().getHostAddress())
                                .orElse("unknown");
            // Use custom key resolver to resolve IP-based keys
            return Mono.just(ip);
        };
    }
}


Implementation of Rate Limiting With Client IP in Spring Cloud Gateway

Below are the implementation steps of Rate Limiting With Client IP in Spring Cloud Gateway.

Step 1: Configure a Route

Given that distributed systems are involved, it may be necessary for us to monitor all incoming requests coming from all of our application's instances.

For this reason, storing the contents of the bucket in a distributed cache system is practical. In this instance, we simulated a real-world application by preconfiguring a Redis instance we'll set up a route with a rate limiter next. We will listen for requests at the /example endpoint and send them to http://custom-service:

@Bean
public RouteLocator customRoutes(RouteLocatorBuilder locatorBuilder) {
    return locatorBuilder.routes()
        .route("custom_route", predicate -> predicate
            .path("/custom-path") // Update the path according to requirements
            .filters(filter -> filter.requestRateLimiter(rateLimiter -> rateLimiter.setRateLimiter(customRateLimiter()))) // Customize the rate limiter as needed
            .uri("http://custom-service")) // Update the target URI to the appropriate endpoint
        .build();
}


Step 2: The IP address of the client in KeyResolver

Since there is not yet a default implementation for this interface, we must create one while keeping in mind that we need the IP address of the client:

@Component
public class CustomAddressResolver implements KeyResolver {

    // Resolve method to determine the client address for rate limiting
    @Override
    public Mono<String> resolve(ServerWebExchange exchange) {
        // Retrieve the remote address from the exchange's request
        return Optional.ofNullable(exchange.getRequest().getRemoteAddress())
            // Map the remote address to the host address
            .map(address -> address.getAddress().getHostAddress())
            // Wrap the host address into a Mono (asynchronous value) if present
            .map(Mono::just)
            // If the remote address is not available, return an empty Mono
            .orElseGet(Mono::empty);
    }
}


Step 3: Identify the Origin of IP Address

Determining the geographic location or network provider linked to an IP address is usually necessary to find its origin.

import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.cloud.gateway.support.ipresolver.XForwardedRemoteAddressResolver;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

// Indicates this bean as the primary one if multiple implementations are available
@Primary 
// Marks this class as a Spring component for component scanning and auto-configuration
@Component 
public class CustomProxyAddressResolver implements KeyResolver {

    // Resolve method to determine the client address for rate limiting
    @Override
    public Mono<String> resolve(ServerWebExchange exchange) {
        // Create an XForwardedRemoteAddressResolver with max trusted index
        XForwardedRemoteAddressResolver resolver = XForwardedRemoteAddressResolver.maxTrustedIndex(1);
        
        // Resolve the client's address from the exchange using the XForwardedRemoteAddressResolver
        InetSocketAddress inetSocketAddress = resolver.resolve(exchange);
        
        // Extract and return the host address from the resolved InetSocketAddress
        return Mono.just(inetSocketAddress.getAddress().getHostAddress());
    }
}


Article Tags :