Spring Security for API Rate Limiting

Last Updated : 2 May, 2026

API rate limiting in Spring Security is used to control how many requests a client can make to an API within a specific time period. It helps protect applications from abuse, excessive traffic, and Denial of Service (DoS) attacks. By integrating rate limiting with Spring Security filters, requests can be monitored and restricted efficiently.

  • Controls the number of API requests per user/IP within a time window.
  • Helps prevent abuse, spam requests, and DoS attacks.
  • Can be implemented using in-memory counters, Redis, or libraries like Bucket4j.

Prerequisites:

  • Basic knowledge of the Java and Spring Framework.
  • Familiarity with concepts like filters and authentication.
  • Maven for building dependency management.
  • JDK and IntelliJ IDEA installed in your system

API Rate Limiting

API rate limiting helps manage traffic by setting the maximum number of requests a user can make to the API within a given time period. For instance, we might restrict users to 100 requests per minute to prevent overuse. When the user exceeds this limit, the server responds with a 429 Too Many Requests status.

Common Approaches to Rate Limiting:

  1. In-Memory Counters: Suitable for simple use cases in a single-instance application.
  2. Distributed Rate Limiting with Redis: Suitable for applications running across multiple instances.
  3. Libraries like Bucket4j: For more advanced rate-limiting scenarios.

Implementing API Rate Limiting with Spring Boot and Spring Security

This example demonstrates how to implement API rate limiting in a Spring Boot application using Spring Security. We'll use an in-memory rate-limiting mechanism in this example.

Step 1: Create a New Spring Boot Project

Create a new Spring Boot project using IntelliJ IDEA and select the following options:

  • Name: rate-limiting-demo
  • Language: Java
  • Type: Maven
  • Packaging: Jar

Click Next, then select the necessary dependencies.

Project Metadata

Step 2: Add the Dependencies

Add the following dependencies into the Spring Boot Project.

  • Spring Web
  • Spring Boot DevTools
  • Lombok
  • Spring Security
  • Spring Data Redis

Click on the Create button.

Add Dependencies

Project Structure

After the project creation done, set the file structure as shown in the below image:

Project Folder Structure

Step 3: Configure Application Properties

In the application.properties file, configure the application and Redis settings (if needed for distributed environments):

# Application settings
spring.application.name=api-rate-limiting
server.port=8080

# Redis configuration (if using Redis for distributed rate limiting)
spring.data.redis.host=localhost
spring.data.redis.port=6379

Step 4: Creating the Rate Limiting Filter

We will create a custom filter that tracks and limits requests based on the client's IP address.

RateLimitingFilter.java:

Java
package com.gfg.apiratelimiting;

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Component
public class RateLimitingFilter extends OncePerRequestFilter {

    // Class to store request count + timestamp
    private static class RequestInfo {
        int count;
        long timestamp;

        RequestInfo(int count, long timestamp) {
            this.count = count;
            this.timestamp = timestamp;
        }
    }

    // Store per-IP request data
    private final Map<String, RequestInfo> requestCounts = new ConcurrentHashMap<>();

    private static final int MAX_REQUESTS = 5;
    private static final long TIME_WINDOW = 60 * 1000; // 1 minute

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain)
            throws ServletException, IOException {

        String clientIp = request.getRemoteAddr();
        long currentTime = System.currentTimeMillis();

        requestCounts.putIfAbsent(clientIp, new RequestInfo(0, currentTime));
        RequestInfo info = requestCounts.get(clientIp);

        // Reset counter if time window passed
        if (currentTime - info.timestamp > TIME_WINDOW) {
            info.count = 0;
            info.timestamp = currentTime;
        }

        // Block if limit exceeded
        if (info.count >= MAX_REQUESTS) {
            response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());
            response.getWriter().write("Too many requests - please try again later.");
            return;
        }

        // Increment count
        info.count++;

        filterChain.doFilter(request, response);
    }
}
  • This class extends OncePerRequestFilter, meaning the filter is executed once per request.
  • The map requestCounts tracks the number of requests for each client (based on IP).
  • When the request count exceeds the defined MAX_REQUESTS_PER_MINUTE, it returns an HTTP 429 Too Many Requests response.
  • If the limit is not exceeded, the request proceeds as usual.

Step 5: Registering the Rate Limiting Filter in Security Configuration

To apply the rate-limiting filter to your API, we need to register it in the security configuration.

SecurityConfig.java:

Java
package com.gfg.apiratelimiting;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
public class SecurityConfig {

    // Define the security filter chain
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http, RateLimitingFilter rateLimitingFilter) throws Exception {
        // Disable CSRF for simplicity and allow all requests
        http.csrf().disable()
                .authorizeHttpRequests()
                .anyRequest().permitAll()
                .and()
                // Register the rate-limiting filter before the UsernamePasswordAuthenticationFilter
                .addFilterBefore(rateLimitingFilter, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.class);
        
        return http.build();
    }
}
  • The security configuration disables CSRF (Cross-Site Request Forgery) for simplicity in this example.
  • The custom rate-limiting filter (RateLimitingFilter) is registered before the UsernamePasswordAuthenticationFilter, meaning it will intercept every request before authentication.

Step 6: Create a Controller Class for Testing

We'll create a simple API endpoint to test the rate limiting.

ApiController.java:

Java
package com.gfg.apiratelimiting;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ApiController {

    // A simple GET endpoint for testing rate limiting
    @GetMapping("/api/test")
    public String testApi() {
        return "API is working!";
    }
}
  • This controller defines a simple GET endpoint (/api/test) that returns a success message.
  • We will use this endpoint to test the rate-limiting functionality.

pom.xml File

XML
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.3.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.gfg</groupId>
    <artifactId>api-rate-limiting</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>api-rate-limiting</name>
    <description>api-rate-limiting</description>
    <url/>
    <licenses>
        <license/>
    </licenses>
    <developers>
        <developer/>
    </developers>
    <scm>
        <connection/>
        <developerConnection/>
        <tag/>
        <url/>
    </scm>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

Step 7: Run the Application

Once the project is complete, run the Spring Boot application on port 8080 using the following command:

mvn spring-boot:run

Output:

Console Output

Step 8: Testing the Rate Limiting

We can use Postman to test the API rate limiting. Here is how the responses should behave:

First 5 requests: The API will respond with a 200 OK status and the message "API is working!".

GET http://localhost:8080/api/test

Postman showing the successful API requests until the rate limit can be reached.

Response:

Postman Response


6th request and beyond (within a minute): The API will respond with a 429 Too Many Requests status and the message "Too many requests - please try again later."

Response:

Postman Response

In this example project, we successfully implemented the simple API rate limiting solution in the Spring Boot using the Spring Security.

Comment

Explore