Skip to content

Commit

Permalink
feat: rate limiting 추가 QUZ-122
Browse files Browse the repository at this point in the history
  • Loading branch information
HMWG committed Dec 6, 2024
1 parent b42ae36 commit 722dd48
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 41 deletions.
2 changes: 1 addition & 1 deletion gateway-service/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ dependencies {
testImplementation("org.springframework.security:spring-security-test")

//redis
implementation("org.springframework.boot:spring-boot-starter-data-redis")
implementation("org.springframework.boot:spring-boot-starter-data-redis-reactive")
}

tasks.named<BootJar>("bootJar") {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.grepp.quizy.config

import com.grepp.quizy.jwt.JwtProvider
import com.grepp.quizy.user.api.global.util.CookieUtils
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.http.HttpHeaders
import org.springframework.http.server.reactive.ServerHttpRequest
import reactor.core.publisher.Mono

@Configuration
class RateLimiterConfig(
private val jwtProvider: JwtProvider,
) {

@Bean
fun userKeyResolver(): KeyResolver {
return KeyResolver { exchange ->
// JWT 토큰에서 사용자 ID를 추출하여 rate limit key로 사용
val token = extractToken(exchange.request)

if (token != null) {
try {
val userId = jwtProvider.getUserIdFromToken(token)
Mono.just(userId.value.toString())
} catch (e: Exception) {
// 토큰이 유효하지 않은 경우 IP 주소를 key로 사용
Mono.just(exchange.request.remoteAddress?.address?.hostAddress ?: "anonymous")
}

} else {
// 토큰이 없는 경우 IP 주소를 key로 사용
Mono.just(exchange.request.remoteAddress?.address?.hostAddress ?: "anonymous")
}
}
}

private fun extractToken(request: ServerHttpRequest): String? {
return if (request.headers.containsKey(HttpHeaders.AUTHORIZATION)) {
resolveToken(request)
} else {
CookieUtils.getCookieValue(request, "refreshToken") ?: ""
}
}

private fun resolveToken(request: ServerHttpRequest): String? {
val authHeader = request.headers[HttpHeaders.AUTHORIZATION]?.get(0) ?: ""
return if (authHeader.startsWith("Bearer ")) {
authHeader.substring(7)
} else {
null
}
}
}
110 changes: 70 additions & 40 deletions gateway-service/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,38 +57,53 @@ spring:
- id: game
uri: http://localhost:8081
predicates:
- Path=/api/game/**
- Path=/api/game/**, /ws/**
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20
redis-rate-limiter.requestedTokens: 1
key-resolver: "#{@userKeyResolver}"

- id: matching
uri: http://localhost:8082
predicates:
- Path=/api/matching/**
metadata:
response-timeout: 300000
connect-timeout: 300000
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20
redis-rate-limiter.requestedTokens: 1
key-resolver: "#{@userKeyResolver}"

- id: quiz
uri: http://localhost:8083
predicates:
- Path=/api/quiz/**
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20
redis-rate-limiter.requestedTokens: 1
key-resolver: "#{@userKeyResolver}"

- id: user
uri: http://localhost:8085
predicates:
- Path=/api/user/**
- id: oauth2
uri: http://localhost:8085
predicates:
- Path=/oauth2/**
- id: auth
uri: http://localhost:8085
predicates:
- Path=/api/auth/**
- id: login
uri: http://localhost:8085
predicates:
- Path=/login/**
- id: sockjs
uri: http://localhost:8081
predicates:
- Path=/ws/**
- Path=/api/user/**, /oauth2/**, /api/auth/**, /login/**
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20
redis-rate-limiter.requestedTokens: 1
key-resolver: "#{@userKeyResolver}"

data:
redis:
Expand Down Expand Up @@ -133,41 +148,56 @@ spring:
cloud:
gateway:
routes:
- id: quiz
uri: http://dev-quiz-service:8080
predicates:
- Path=/api/quiz/**
- id: game
uri: http://dev-game-service:8080
predicates:
- Path=/api/game/**
- id: user
uri: http://dev-user-service:8080
predicates:
- Path=/api/user/**
- Path=/api/game/**, /ws/**
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20
redis-rate-limiter.requestedTokens: 1
key-resolver: "#{@userKeyResolver}"

- id: matching
uri: http://dev-matching-service:8080
predicates:
- Path=/api/matching/**
metadata:
response-timeout: 300000
connect-timeout: 300000
- id: oauth2
uri: http://dev-user-service:8080
predicates:
- Path=/oauth2/**
- id: auth
uri: http://dev-user-service:8080
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20
redis-rate-limiter.requestedTokens: 1
key-resolver: "#{@userKeyResolver}"

- id: quiz
uri: http://dev-quiz-service:8080
predicates:
- Path=/api/auth/**
- id: login
- Path=/api/quiz/**
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20
redis-rate-limiter.requestedTokens: 1
key-resolver: "#{@userKeyResolver}"

- id: user
uri: http://dev-user-service:8080
predicates:
- Path=/login/**
- id: sockjs
uri: http://dev-game-service:8080
predicates:
- Path=/ws/**
- Path=/api/user/**, /oauth2/**, /api/auth/**, /login/**
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20
redis-rate-limiter.requestedTokens: 1
key-resolver: "#{@userKeyResolver}"

kubernetes:
discovery:
Expand Down

0 comments on commit 722dd48

Please sign in to comment.