Skip to content

Commit 8edbcee

Browse files
authored
Merge pull request #535 from DSM-PICK/534-이메일-고의적-재발송-방어
534 이메일 고의적 재발송 방어
2 parents e5653c6 + 5158320 commit 8edbcee

File tree

5 files changed

+51
-1
lines changed

5 files changed

+51
-1
lines changed

src/main/kotlin/dsm/pick2024/domain/mail/service/SendMailService.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import dsm.pick2024.domain.mail.port.`in`.SendMailUseCase
44
import dsm.pick2024.domain.mail.presentation.dto.request.SendMailRequest
55
import dsm.pick2024.global.security.jwt.exception.InternalServerErrorException
66
import dsm.pick2024.infrastructure.mail.MailProperties
7+
import dsm.pick2024.infrastructure.util.redis.port.RateLimiterPort
78
import dsm.pick2024.infrastructure.util.redis.port.RedisUtilPort
89
import org.springframework.beans.factory.annotation.Value
910
import org.springframework.core.io.ClassPathResource
@@ -20,14 +21,15 @@ class SendMailService(
2021
private val mailSender: JavaMailSender,
2122
private val redisUtilPort: RedisUtilPort,
2223
private val mailProperties: MailProperties,
24+
private val rateLimiterPort: RateLimiterPort,
2325
@Value("\${server.url}")
2426
private val SERVER_URL: String
2527
) : SendMailUseCase {
2628

2729
private val CODE_LENGTH = 6
2830

2931
override fun execute(request: SendMailRequest) {
30-
println("SERVER_URL = $SERVER_URL")
32+
rateLimiterPort.isAllowed(request.mail)
3133

3234
val email = request.mail + mailProperties.dsmPostFix
3335
val authCode = generateAuthCode()

src/main/kotlin/dsm/pick2024/global/error/exception/ErrorCode.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ enum class ErrorCode(
4949

5050
NOT_EXTENSION_XSL(415, "XSL 확장자가 아닙니다"),
5151

52+
TOO_MANY_REQUEST(429, "너무 많은 요청입니다."),
53+
5254
// Internal Server Error
5355
INTERNAL_SERVER_ERROR(500, "내부 서버 오류"),
5456
FEIGN_SERVER_ERROR(500, "Feign 서버 오류"),
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package dsm.pick2024.infrastructure.util.redis
2+
3+
import dsm.pick2024.infrastructure.util.redis.exception.TooManyRequestException
4+
import dsm.pick2024.infrastructure.util.redis.port.RateLimiterPort
5+
import org.springframework.data.redis.core.StringRedisTemplate
6+
import org.springframework.stereotype.Component
7+
import java.time.Duration
8+
9+
@Component
10+
class RateLimiter(
11+
private val redisTemplate: StringRedisTemplate
12+
) : RateLimiterPort {
13+
private val RATE_LIMIT_PREFIX = "RATE_LIMIT"
14+
15+
private val REQUEST_LIMIT = 5L
16+
17+
private val TIME_WINDOW = Duration.ofMinutes(10)
18+
19+
override fun isAllowed(key: String) {
20+
val valueOperations = redisTemplate.opsForValue()
21+
val redisKey = "$RATE_LIMIT_PREFIX:$key"
22+
23+
val requestCount = valueOperations.increment(redisKey) ?: 0L
24+
25+
if (requestCount == 1L) {
26+
redisTemplate.expire(redisKey, TIME_WINDOW)
27+
}
28+
29+
if (requestCount > REQUEST_LIMIT) {
30+
throw TooManyRequestException
31+
}
32+
}
33+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package dsm.pick2024.infrastructure.util.redis.exception
2+
3+
import dsm.pick2024.global.error.exception.ErrorCode
4+
import dsm.pick2024.global.error.exception.PickException
5+
6+
object TooManyRequestException : PickException(
7+
ErrorCode.TOO_MANY_REQUEST
8+
)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package dsm.pick2024.infrastructure.util.redis.port
2+
3+
interface RateLimiterPort {
4+
fun isAllowed(key: String)
5+
}

0 commit comments

Comments
 (0)