-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Api-v0.2.1
- Loading branch information
Showing
12 changed files
with
276 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
37 changes: 37 additions & 0 deletions
37
DuDoong-Api/src/main/java/band/gosrock/api/config/rateLimit/IPRateLimiter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package band.gosrock.api.config.rateLimit; | ||
|
||
|
||
import io.github.bucket4j.Bandwidth; | ||
import io.github.bucket4j.Bucket; | ||
import io.github.bucket4j.BucketConfiguration; | ||
import io.github.bucket4j.Refill; | ||
import io.github.bucket4j.distributed.proxy.ProxyManager; | ||
import java.time.Duration; | ||
import java.util.function.Supplier; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.stereotype.Component; | ||
|
||
@Component | ||
@RequiredArgsConstructor | ||
public class IPRateLimiter { | ||
// autowiring dependencies | ||
private final ProxyManager<String> buckets; | ||
|
||
@Value("${throttle.overdraft}") | ||
private long overdraft; | ||
|
||
@Value("${throttle.greedyRefill}") | ||
private long greedyRefill; | ||
|
||
public Bucket resolveBucket(String key) { | ||
Supplier<BucketConfiguration> configSupplier = getConfigSupplierForUser(); | ||
return buckets.builder().build(key, configSupplier); | ||
} | ||
|
||
private Supplier<BucketConfiguration> getConfigSupplierForUser() { | ||
Refill refill = Refill.greedy(greedyRefill, Duration.ofMinutes(1)); | ||
Bandwidth limit = Bandwidth.classic(overdraft, refill); | ||
return () -> (BucketConfiguration.builder().addLimit(limit).build()); | ||
} | ||
} |
73 changes: 73 additions & 0 deletions
73
DuDoong-Api/src/main/java/band/gosrock/api/config/rateLimit/ThrottlingInterceptor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
package band.gosrock.api.config.rateLimit; | ||
|
||
|
||
import band.gosrock.api.config.security.SecurityUtils; | ||
import band.gosrock.api.slack.sender.SlackThrottleErrorSender; | ||
import band.gosrock.common.dto.ErrorResponse; | ||
import band.gosrock.common.exception.GlobalErrorCode; | ||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import io.github.bucket4j.Bucket; | ||
import java.io.IOException; | ||
import javax.servlet.http.HttpServletRequest; | ||
import javax.servlet.http.HttpServletResponse; | ||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.http.MediaType; | ||
import org.springframework.stereotype.Component; | ||
import org.springframework.web.servlet.HandlerInterceptor; | ||
import org.springframework.web.util.ContentCachingRequestWrapper; | ||
|
||
@Component | ||
@RequiredArgsConstructor | ||
@Slf4j | ||
public class ThrottlingInterceptor implements HandlerInterceptor { | ||
|
||
private final UserRateLimiter userRateLimiter; | ||
private final IPRateLimiter ipRateLimiter; | ||
private final ObjectMapper objectMapper; | ||
|
||
private final SlackThrottleErrorSender slackThrottleErrorSender; | ||
|
||
@Override | ||
public boolean preHandle( | ||
HttpServletRequest request, HttpServletResponse response, Object handler) | ||
throws IOException { | ||
Long userId = SecurityUtils.getCurrentUserId(); | ||
Bucket bucket; | ||
if (userId == 0L) { | ||
// 익명 유저 ip 기반처리 | ||
String remoteAddr = request.getRemoteAddr(); | ||
bucket = ipRateLimiter.resolveBucket(remoteAddr); | ||
} else { | ||
// 비 익명 유저 유저 아이디 기반 처리 | ||
bucket = userRateLimiter.resolveBucket(userId.toString()); | ||
} | ||
|
||
long availableTokens = bucket.getAvailableTokens(); | ||
log.info(userId + " : " + availableTokens); | ||
|
||
if (bucket.tryConsume(1)) { | ||
return true; | ||
} | ||
|
||
// 슬랙 알림 메시지 발송. | ||
// limit is exceeded | ||
ContentCachingRequestWrapper cachingRequest = (ContentCachingRequestWrapper) request; | ||
slackThrottleErrorSender.execute(cachingRequest, userId); | ||
responseTooManyRequestError(request, response); | ||
|
||
return false; | ||
} | ||
|
||
private void responseTooManyRequestError( | ||
HttpServletRequest request, HttpServletResponse response) throws IOException { | ||
ErrorResponse errorResponse = | ||
new ErrorResponse( | ||
GlobalErrorCode.TOO_MANY_REQUEST.getErrorReason(), | ||
request.getRequestURL().toString()); | ||
response.setCharacterEncoding("UTF-8"); | ||
response.setContentType(MediaType.APPLICATION_JSON_VALUE); | ||
response.setStatus(errorResponse.getStatus()); | ||
response.getWriter().write(objectMapper.writeValueAsString(errorResponse)); | ||
} | ||
} |
18 changes: 18 additions & 0 deletions
18
DuDoong-Api/src/main/java/band/gosrock/api/config/rateLimit/ThrottlingWebConfigure.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package band.gosrock.api.config.rateLimit; | ||
|
||
|
||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.stereotype.Component; | ||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry; | ||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; | ||
|
||
@RequiredArgsConstructor | ||
@Component | ||
public class ThrottlingWebConfigure implements WebMvcConfigurer { | ||
private final ThrottlingInterceptor throttlingInterceptor; | ||
|
||
@Override | ||
public void addInterceptors(InterceptorRegistry registry) { | ||
registry.addInterceptor(throttlingInterceptor); | ||
} | ||
} |
37 changes: 37 additions & 0 deletions
37
DuDoong-Api/src/main/java/band/gosrock/api/config/rateLimit/UserRateLimiter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package band.gosrock.api.config.rateLimit; | ||
|
||
|
||
import io.github.bucket4j.Bandwidth; | ||
import io.github.bucket4j.Bucket; | ||
import io.github.bucket4j.BucketConfiguration; | ||
import io.github.bucket4j.Refill; | ||
import io.github.bucket4j.distributed.proxy.ProxyManager; | ||
import java.time.Duration; | ||
import java.util.function.Supplier; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.stereotype.Component; | ||
|
||
@Component | ||
@RequiredArgsConstructor | ||
public class UserRateLimiter { | ||
// autowiring dependencies | ||
private final ProxyManager<String> buckets; | ||
|
||
@Value("${throttle.overdraft}") | ||
private long overdraft; | ||
|
||
@Value("${throttle.greedyRefill}") | ||
private long greedyRefill; | ||
|
||
public Bucket resolveBucket(String key) { | ||
Supplier<BucketConfiguration> configSupplier = getConfigSupplierForUser(); | ||
return buckets.builder().build(key, configSupplier); | ||
} | ||
|
||
private Supplier<BucketConfiguration> getConfigSupplierForUser() { | ||
Refill refill = Refill.greedy(greedyRefill, Duration.ofMinutes(1)); | ||
Bandwidth limit = Bandwidth.classic(overdraft, refill); | ||
return () -> (BucketConfiguration.builder().addLimit(limit).build()); | ||
} | ||
} |
66 changes: 66 additions & 0 deletions
66
DuDoong-Api/src/main/java/band/gosrock/api/slack/sender/SlackThrottleErrorSender.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
package band.gosrock.api.slack.sender; | ||
|
||
import static com.slack.api.model.block.Blocks.divider; | ||
import static com.slack.api.model.block.Blocks.section; | ||
import static com.slack.api.model.block.composition.BlockCompositions.plainText; | ||
|
||
import band.gosrock.infrastructure.config.slack.SlackErrorNotificationProvider; | ||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import com.slack.api.model.block.Blocks; | ||
import com.slack.api.model.block.LayoutBlock; | ||
import com.slack.api.model.block.composition.MarkdownTextObject; | ||
import com.slack.api.model.block.composition.TextObject; | ||
import java.io.IOException; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.stereotype.Component; | ||
import org.springframework.web.util.ContentCachingRequestWrapper; | ||
|
||
@Component | ||
@RequiredArgsConstructor | ||
@Slf4j | ||
public class SlackThrottleErrorSender { | ||
private final ObjectMapper objectMapper; | ||
|
||
private final SlackErrorNotificationProvider slackProvider; | ||
|
||
public void execute(ContentCachingRequestWrapper cachingRequest, Long userId) | ||
throws IOException { | ||
final String url = cachingRequest.getRequestURL().toString(); | ||
final String method = cachingRequest.getMethod(); | ||
final String body = | ||
objectMapper.readTree(cachingRequest.getContentAsByteArray()).toString(); | ||
final String errorUserIP = cachingRequest.getRemoteAddr(); | ||
|
||
List<LayoutBlock> layoutBlocks = new ArrayList<>(); | ||
layoutBlocks.add( | ||
Blocks.header( | ||
headerBlockBuilder -> | ||
headerBlockBuilder.text(plainText("Rate Limit Error")))); | ||
layoutBlocks.add(divider()); | ||
|
||
MarkdownTextObject errorUserIdMarkdown = | ||
MarkdownTextObject.builder().text("* User Id :*\n" + userId).build(); | ||
MarkdownTextObject errorUserIpMarkdown = | ||
MarkdownTextObject.builder().text("* User IP :*\n" + errorUserIP).build(); | ||
layoutBlocks.add( | ||
section( | ||
section -> | ||
section.fields(List.of(errorUserIdMarkdown, errorUserIpMarkdown)))); | ||
|
||
MarkdownTextObject methodMarkdown = | ||
MarkdownTextObject.builder() | ||
.text("* Request Addr :*\n" + method + " : " + url) | ||
.build(); | ||
MarkdownTextObject bodyMarkdown = | ||
MarkdownTextObject.builder().text("* Request Body :*\n" + body).build(); | ||
List<TextObject> fields = List.of(methodMarkdown, bodyMarkdown); | ||
layoutBlocks.add(section(section -> section.fields(fields))); | ||
|
||
layoutBlocks.add(divider()); | ||
|
||
slackProvider.sendNotification(layoutBlocks); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
10 changes: 10 additions & 0 deletions
10
DuDoong-Common/src/main/java/band/gosrock/common/exception/TooManyRequestException.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package band.gosrock.common.exception; | ||
|
||
public class TooManyRequestException extends DuDoongCodeException { | ||
|
||
public static final DuDoongCodeException EXCEPTION = new TooManyRequestException(); | ||
|
||
private TooManyRequestException() { | ||
super(GlobalErrorCode.TOO_MANY_REQUEST); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters