Skip to content

Commit 722dd48

Browse files
committed
feat: rate limiting 추가 QUZ-122
1 parent b42ae36 commit 722dd48

File tree

3 files changed

+126
-41
lines changed

3 files changed

+126
-41
lines changed

gateway-service/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ dependencies {
2020
testImplementation("org.springframework.security:spring-security-test")
2121

2222
//redis
23-
implementation("org.springframework.boot:spring-boot-starter-data-redis")
23+
implementation("org.springframework.boot:spring-boot-starter-data-redis-reactive")
2424
}
2525

2626
tasks.named<BootJar>("bootJar") {
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package com.grepp.quizy.config
2+
3+
import com.grepp.quizy.jwt.JwtProvider
4+
import com.grepp.quizy.user.api.global.util.CookieUtils
5+
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver
6+
import org.springframework.context.annotation.Bean
7+
import org.springframework.context.annotation.Configuration
8+
import org.springframework.http.HttpHeaders
9+
import org.springframework.http.server.reactive.ServerHttpRequest
10+
import reactor.core.publisher.Mono
11+
12+
@Configuration
13+
class RateLimiterConfig(
14+
private val jwtProvider: JwtProvider,
15+
) {
16+
17+
@Bean
18+
fun userKeyResolver(): KeyResolver {
19+
return KeyResolver { exchange ->
20+
// JWT 토큰에서 사용자 ID를 추출하여 rate limit key로 사용
21+
val token = extractToken(exchange.request)
22+
23+
if (token != null) {
24+
try {
25+
val userId = jwtProvider.getUserIdFromToken(token)
26+
Mono.just(userId.value.toString())
27+
} catch (e: Exception) {
28+
// 토큰이 유효하지 않은 경우 IP 주소를 key로 사용
29+
Mono.just(exchange.request.remoteAddress?.address?.hostAddress ?: "anonymous")
30+
}
31+
32+
} else {
33+
// 토큰이 없는 경우 IP 주소를 key로 사용
34+
Mono.just(exchange.request.remoteAddress?.address?.hostAddress ?: "anonymous")
35+
}
36+
}
37+
}
38+
39+
private fun extractToken(request: ServerHttpRequest): String? {
40+
return if (request.headers.containsKey(HttpHeaders.AUTHORIZATION)) {
41+
resolveToken(request)
42+
} else {
43+
CookieUtils.getCookieValue(request, "refreshToken") ?: ""
44+
}
45+
}
46+
47+
private fun resolveToken(request: ServerHttpRequest): String? {
48+
val authHeader = request.headers[HttpHeaders.AUTHORIZATION]?.get(0) ?: ""
49+
return if (authHeader.startsWith("Bearer ")) {
50+
authHeader.substring(7)
51+
} else {
52+
null
53+
}
54+
}
55+
}

gateway-service/src/main/resources/application.yml

Lines changed: 70 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -57,38 +57,53 @@ spring:
5757
- id: game
5858
uri: http://localhost:8081
5959
predicates:
60-
- Path=/api/game/**
60+
- Path=/api/game/**, /ws/**
61+
filters:
62+
- name: RequestRateLimiter
63+
args:
64+
redis-rate-limiter.replenishRate: 10
65+
redis-rate-limiter.burstCapacity: 20
66+
redis-rate-limiter.requestedTokens: 1
67+
key-resolver: "#{@userKeyResolver}"
68+
6169
- id: matching
6270
uri: http://localhost:8082
6371
predicates:
6472
- Path=/api/matching/**
6573
metadata:
6674
response-timeout: 300000
6775
connect-timeout: 300000
76+
filters:
77+
- name: RequestRateLimiter
78+
args:
79+
redis-rate-limiter.replenishRate: 10
80+
redis-rate-limiter.burstCapacity: 20
81+
redis-rate-limiter.requestedTokens: 1
82+
key-resolver: "#{@userKeyResolver}"
83+
6884
- id: quiz
6985
uri: http://localhost:8083
7086
predicates:
7187
- Path=/api/quiz/**
88+
filters:
89+
- name: RequestRateLimiter
90+
args:
91+
redis-rate-limiter.replenishRate: 10
92+
redis-rate-limiter.burstCapacity: 20
93+
redis-rate-limiter.requestedTokens: 1
94+
key-resolver: "#{@userKeyResolver}"
95+
7296
- id: user
7397
uri: http://localhost:8085
7498
predicates:
75-
- Path=/api/user/**
76-
- id: oauth2
77-
uri: http://localhost:8085
78-
predicates:
79-
- Path=/oauth2/**
80-
- id: auth
81-
uri: http://localhost:8085
82-
predicates:
83-
- Path=/api/auth/**
84-
- id: login
85-
uri: http://localhost:8085
86-
predicates:
87-
- Path=/login/**
88-
- id: sockjs
89-
uri: http://localhost:8081
90-
predicates:
91-
- Path=/ws/**
99+
- Path=/api/user/**, /oauth2/**, /api/auth/**, /login/**
100+
filters:
101+
- name: RequestRateLimiter
102+
args:
103+
redis-rate-limiter.replenishRate: 10
104+
redis-rate-limiter.burstCapacity: 20
105+
redis-rate-limiter.requestedTokens: 1
106+
key-resolver: "#{@userKeyResolver}"
92107

93108
data:
94109
redis:
@@ -133,41 +148,56 @@ spring:
133148
cloud:
134149
gateway:
135150
routes:
136-
- id: quiz
137-
uri: http://dev-quiz-service:8080
138-
predicates:
139-
- Path=/api/quiz/**
140151
- id: game
141152
uri: http://dev-game-service:8080
142153
predicates:
143-
- Path=/api/game/**
144-
- id: user
145-
uri: http://dev-user-service:8080
146-
predicates:
147-
- Path=/api/user/**
154+
- Path=/api/game/**, /ws/**
155+
filters:
156+
- name: RequestRateLimiter
157+
args:
158+
redis-rate-limiter.replenishRate: 10
159+
redis-rate-limiter.burstCapacity: 20
160+
redis-rate-limiter.requestedTokens: 1
161+
key-resolver: "#{@userKeyResolver}"
162+
148163
- id: matching
149164
uri: http://dev-matching-service:8080
150165
predicates:
151166
- Path=/api/matching/**
152167
metadata:
153168
response-timeout: 300000
154169
connect-timeout: 300000
155-
- id: oauth2
156-
uri: http://dev-user-service:8080
157-
predicates:
158-
- Path=/oauth2/**
159-
- id: auth
160-
uri: http://dev-user-service:8080
170+
filters:
171+
- name: RequestRateLimiter
172+
args:
173+
redis-rate-limiter.replenishRate: 10
174+
redis-rate-limiter.burstCapacity: 20
175+
redis-rate-limiter.requestedTokens: 1
176+
key-resolver: "#{@userKeyResolver}"
177+
178+
- id: quiz
179+
uri: http://dev-quiz-service:8080
161180
predicates:
162-
- Path=/api/auth/**
163-
- id: login
181+
- Path=/api/quiz/**
182+
filters:
183+
- name: RequestRateLimiter
184+
args:
185+
redis-rate-limiter.replenishRate: 10
186+
redis-rate-limiter.burstCapacity: 20
187+
redis-rate-limiter.requestedTokens: 1
188+
key-resolver: "#{@userKeyResolver}"
189+
190+
- id: user
164191
uri: http://dev-user-service:8080
165192
predicates:
166-
- Path=/login/**
167-
- id: sockjs
168-
uri: http://dev-game-service:8080
169-
predicates:
170-
- Path=/ws/**
193+
- Path=/api/user/**, /oauth2/**, /api/auth/**, /login/**
194+
filters:
195+
- name: RequestRateLimiter
196+
args:
197+
redis-rate-limiter.replenishRate: 10
198+
redis-rate-limiter.burstCapacity: 20
199+
redis-rate-limiter.requestedTokens: 1
200+
key-resolver: "#{@userKeyResolver}"
171201

172202
kubernetes:
173203
discovery:

0 commit comments

Comments
 (0)