Skip to content

Commit 07cc96c

Browse files
Add Redis distributed rate limiting (prod only).
Signed-off-by: vimal-tech-dev <be08it929@gmail.com>
1 parent d290779 commit 07cc96c

4 files changed

Lines changed: 107 additions & 10 deletions

File tree

pom.xml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,22 @@
9191
<groupId>org.flywaydb</groupId>
9292
<artifactId>flyway-database-postgresql</artifactId>
9393
</dependency>
94+
<dependency>
95+
<groupId>com.bucket4j</groupId>
96+
<artifactId>bucket4j-core</artifactId>
97+
<version>8.6.0</version>
98+
</dependency>
99+
100+
<dependency>
101+
<groupId>com.bucket4j</groupId>
102+
<artifactId>bucket4j-redis</artifactId>
103+
<version>8.6.0</version>
104+
</dependency>
105+
106+
<dependency>
107+
<groupId>org.springframework.boot</groupId>
108+
<artifactId>spring-boot-starter-data-redis</artifactId>
109+
</dependency>
94110
</dependencies>
95111

96112
<build>
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package com.vimaltech.contactapi.security;
2+
3+
import io.github.bucket4j.Bandwidth;
4+
import io.github.bucket4j.Bucket;
5+
import io.github.bucket4j.BucketConfiguration;
6+
import io.github.bucket4j.distributed.proxy.ProxyManager;
7+
import io.github.bucket4j.redis.lettuce.cas.LettuceBasedProxyManager;
8+
9+
import io.lettuce.core.RedisClient;
10+
import io.lettuce.core.RedisURI;
11+
12+
import jakarta.servlet.FilterChain;
13+
import jakarta.servlet.ServletException;
14+
import jakarta.servlet.http.HttpServletRequest;
15+
import jakarta.servlet.http.HttpServletResponse;
16+
17+
import org.springframework.beans.factory.annotation.Value;
18+
import org.springframework.context.annotation.Profile;
19+
import org.springframework.stereotype.Component;
20+
import org.springframework.web.filter.OncePerRequestFilter;
21+
22+
import java.io.IOException;
23+
import java.time.Duration;
24+
import java.util.Optional;
25+
26+
@Component
27+
@Profile("prod")
28+
public class RedisRateLimitFilter extends OncePerRequestFilter {
29+
30+
private final ProxyManager<byte[]> proxyManager;
31+
32+
public RedisRateLimitFilter(
33+
@Value("${spring.data.redis.host}") String host,
34+
@Value("${spring.data.redis.port}") int port
35+
) {
36+
37+
RedisURI redisURI = RedisURI.Builder.redis(host)
38+
.withPort(port)
39+
.build();
40+
41+
RedisClient redisClient = RedisClient.create(redisURI);
42+
43+
this.proxyManager = LettuceBasedProxyManager
44+
.builderFor(redisClient)
45+
.build();
46+
}
47+
48+
private BucketConfiguration configuration() {
49+
return BucketConfiguration.builder()
50+
.addLimit(Bandwidth.simple(10, Duration.ofMinutes(1)))
51+
.build();
52+
}
53+
54+
@Override
55+
protected void doFilterInternal(HttpServletRequest request,
56+
HttpServletResponse response,
57+
FilterChain filterChain)
58+
throws ServletException, IOException {
59+
60+
if (!request.getRequestURI().equals("/api/v1/contact")) {
61+
filterChain.doFilter(request, response);
62+
return;
63+
}
64+
65+
String ip = Optional.ofNullable(request.getHeader("X-Forwarded-For"))
66+
.map(s -> s.split(",")[0].trim())
67+
.orElse(request.getRemoteAddr());
68+
69+
Bucket bucket = proxyManager.builder()
70+
.build(("ip:" + ip).getBytes(), this::configuration);
71+
72+
if (bucket.tryConsume(1)) {
73+
filterChain.doFilter(request, response);
74+
} else {
75+
response.setStatus(429);
76+
response.setContentType("application/json");
77+
response.getWriter().write("""
78+
{
79+
"success": false,
80+
"message": "Too many requests. Please try again later."
81+
}
82+
""");
83+
}
84+
}
85+
}

src/main/resources/ApiResponse.java

Lines changed: 0 additions & 10 deletions
This file was deleted.

src/main/resources/application-prod.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ spring:
55
password: ${DB_PASSWORD:?DB_PASSWORD is required}
66
driver-class-name: org.postgresql.Driver
77

8+
data:
9+
redis:
10+
host: redis
11+
port: 6379
12+
timeout: 2000
13+
814
jpa:
915
hibernate:
1016
ddl-auto: validate

0 commit comments

Comments
 (0)