From ecc617c709de00ac41ca97d735c6fc9172b7e3e5 Mon Sep 17 00:00:00 2001 From: John Niang Date: Mon, 26 Jun 2023 16:12:54 +0800 Subject: [PATCH] Remap RequestNotPermittedException with RateLimitExceededException (#4119) --- .../exception/RateLimitExceededException.java | 15 +++++++++++++++ .../login/UsernamePasswordAuthenticator.java | 16 +++++++++------- .../theme/endpoint/CommentFinderEndpoint.java | 5 ++++- .../resources/config/i18n/messages.properties | 4 ++-- .../resources/config/i18n/messages_zh.properties | 4 ++-- 5 files changed, 32 insertions(+), 12 deletions(-) create mode 100644 application/src/main/java/run/halo/app/infra/exception/RateLimitExceededException.java diff --git a/application/src/main/java/run/halo/app/infra/exception/RateLimitExceededException.java b/application/src/main/java/run/halo/app/infra/exception/RateLimitExceededException.java new file mode 100644 index 000000000..4ac4eb6e3 --- /dev/null +++ b/application/src/main/java/run/halo/app/infra/exception/RateLimitExceededException.java @@ -0,0 +1,15 @@ +package run.halo.app.infra.exception; + +import java.net.URI; +import org.springframework.http.HttpStatus; +import org.springframework.lang.Nullable; +import org.springframework.web.server.ResponseStatusException; + +public class RateLimitExceededException extends ResponseStatusException { + + public RateLimitExceededException(@Nullable Throwable cause) { + super(HttpStatus.TOO_MANY_REQUESTS, "You have exceeded your quota", cause); + setType(URI.create(Exceptions.REQUEST_NOT_PERMITTED_TYPE)); + } + +} diff --git a/application/src/main/java/run/halo/app/security/authentication/login/UsernamePasswordAuthenticator.java b/application/src/main/java/run/halo/app/security/authentication/login/UsernamePasswordAuthenticator.java index 991c907e4..fda714ffb 100644 --- a/application/src/main/java/run/halo/app/security/authentication/login/UsernamePasswordAuthenticator.java +++ b/application/src/main/java/run/halo/app/security/authentication/login/UsernamePasswordAuthenticator.java @@ -1,6 +1,5 @@ package run.halo.app.security.authentication.login; -import static org.springframework.http.HttpStatus.TOO_MANY_REQUESTS; import static org.springframework.http.HttpStatus.UNAUTHORIZED; import static org.springframework.http.MediaType.APPLICATION_JSON; import static run.halo.app.infra.exception.Exceptions.createErrorResponse; @@ -39,6 +38,7 @@ import org.springframework.web.reactive.function.server.ServerResponse; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.WebFilterChain; import reactor.core.publisher.Mono; +import run.halo.app.infra.exception.RateLimitExceededException; import run.halo.app.infra.utils.IpAddressUtils; import run.halo.app.security.AdditionalWebFilter; @@ -136,9 +136,9 @@ public class UsernamePasswordAuthenticator implements AdditionalWebFilter { return RateLimiterOperator.of(rateLimiter); } - private Mono handleRequestNotPermitted(RequestNotPermitted e, + private Mono handleRateLimitExceededException(RateLimitExceededException e, ServerWebExchange exchange) { - var errorResponse = createErrorResponse(e, TOO_MANY_REQUESTS, exchange, messageSource); + var errorResponse = createErrorResponse(e, null, exchange, messageSource); return writeErrorResponse(errorResponse, exchange); } @@ -168,8 +168,9 @@ public class UsernamePasswordAuthenticator implements AdditionalWebFilter { WebFilterExchange webFilterExchange) { return super.onAuthenticationSuccess(authentication, webFilterExchange) .transformDeferred(createIPBasedRateLimiter(webFilterExchange.getExchange())) - .onErrorResume(RequestNotPermitted.class, - e -> handleRequestNotPermitted(e, webFilterExchange.getExchange())); + .onErrorMap(RequestNotPermitted.class, RateLimitExceededException::new) + .onErrorResume(RateLimitExceededException.class, + e -> handleRateLimitExceededException(e, webFilterExchange.getExchange())); } } @@ -230,8 +231,9 @@ public class UsernamePasswordAuthenticator implements AdditionalWebFilter { ) .flatMap(matchResult -> handleAuthenticationException(exception, exchange)) .transformDeferred(createIPBasedRateLimiter(exchange)) - .onErrorResume(RequestNotPermitted.class, - e -> handleRequestNotPermitted(e, exchange)); + .onErrorMap(RequestNotPermitted.class, RateLimitExceededException::new) + .onErrorResume(RateLimitExceededException.class, + e -> handleRateLimitExceededException(e, exchange)); } } diff --git a/application/src/main/java/run/halo/app/theme/endpoint/CommentFinderEndpoint.java b/application/src/main/java/run/halo/app/theme/endpoint/CommentFinderEndpoint.java index c4c63a223..e23003545 100644 --- a/application/src/main/java/run/halo/app/theme/endpoint/CommentFinderEndpoint.java +++ b/application/src/main/java/run/halo/app/theme/endpoint/CommentFinderEndpoint.java @@ -11,6 +11,7 @@ import static org.springdoc.core.fn.builders.requestbody.Builder.requestBodyBuil import com.fasterxml.jackson.annotation.JsonIgnore; import io.github.resilience4j.ratelimiter.RateLimiterRegistry; +import io.github.resilience4j.ratelimiter.RequestNotPermitted; import io.github.resilience4j.reactor.ratelimiter.operator.RateLimiterOperator; import io.swagger.v3.oas.annotations.enums.ParameterIn; import io.swagger.v3.oas.annotations.media.ArraySchema; @@ -50,6 +51,7 @@ import run.halo.app.extension.router.IListRequest; import run.halo.app.extension.router.QueryParamBuildUtil; import run.halo.app.infra.SystemConfigurableEnvironmentFetcher; import run.halo.app.infra.exception.AccessDeniedException; +import run.halo.app.infra.exception.RateLimitExceededException; import run.halo.app.infra.utils.HaloUtils; import run.halo.app.infra.utils.IpAddressUtils; import run.halo.app.theme.finders.CommentFinder; @@ -157,8 +159,9 @@ public class CommentFinderEndpoint implements CustomEndpoint { comment.getSpec().setUserAgent(HaloUtils.userAgentFrom(request)); return commentService.create(comment); }) + .flatMap(comment -> ServerResponse.ok().bodyValue(comment)) .transformDeferred(createIpBasedRateLimiter(request)) - .flatMap(comment -> ServerResponse.ok().bodyValue(comment)); + .onErrorMap(RequestNotPermitted.class, RateLimitExceededException::new); } private RateLimiterOperator createIpBasedRateLimiter(ServerRequest request) { diff --git a/application/src/main/resources/config/i18n/messages.properties b/application/src/main/resources/config/i18n/messages.properties index b3aa93299..48fde7ffc 100644 --- a/application/src/main/resources/config/i18n/messages.properties +++ b/application/src/main/resources/config/i18n/messages.properties @@ -18,7 +18,7 @@ problemDetail.title.run.halo.app.infra.exception.ThemeUpgradeException=Theme Upg problemDetail.title.run.halo.app.infra.exception.PluginInstallationException=Plugin Install Error problemDetail.title.run.halo.app.infra.exception.PluginAlreadyExistsException=Plugin Already Exists Error problemDetail.title.run.halo.app.infra.exception.DuplicateNameException=Duplicate Name Error -problemDetail.title.io.github.resilience4j.ratelimiter.RequestNotPermitted=Request Not Permitted +problemDetail.title.run.halo.app.infra.exception.RateLimitExceededException=Request Not Permitted problemDetail.title.internalServerError=Internal Server Error # Detail definitions @@ -36,7 +36,7 @@ problemDetail.run.halo.app.extension.exception.SchemaViolationException={1} of s problemDetail.run.halo.app.infra.exception.AttachmentAlreadyExistsException=File {0} already exists, please rename it and try again. problemDetail.run.halo.app.infra.exception.DuplicateNameException=Duplicate name detected, please rename it and retry. problemDetail.run.halo.app.infra.exception.PluginAlreadyExistsException=Plugin {0} already exists. -problemDetail.io.github.resilience4j.ratelimiter.RequestNotPermitted=API rate limit exceeded, please try again later. +problemDetail.run.halo.app.infra.exception.RateLimitExceededException=API rate limit exceeded, please try again later. problemDetail.user.signUpFailed.disallowed=System does not allow new users to register. problemDetail.user.duplicateName=The username {0} already exists, please rename it and retry. diff --git a/application/src/main/resources/config/i18n/messages_zh.properties b/application/src/main/resources/config/i18n/messages_zh.properties index f5bc3a175..cc52d53f6 100644 --- a/application/src/main/resources/config/i18n/messages_zh.properties +++ b/application/src/main/resources/config/i18n/messages_zh.properties @@ -6,14 +6,14 @@ problemDetail.title.run.halo.app.infra.exception.AttachmentAlreadyExistsExceptio problemDetail.title.run.halo.app.infra.exception.DuplicateNameException=名称重复 problemDetail.title.run.halo.app.infra.exception.PluginAlreadyExistsException=插件已存在 problemDetail.title.run.halo.app.infra.exception.ThemeInstallationException=主题安装失败 -problemDetail.title.io.github.resilience4j.ratelimiter.RequestNotPermitted=请求限制 +problemDetail.title.run.halo.app.infra.exception.RateLimitExceededException=请求限制 problemDetail.title.internalServerError=服务器内部错误 problemDetail.org.springframework.security.authentication.BadCredentialsException=用户名或密码错误。 problemDetail.run.halo.app.infra.exception.AttachmentAlreadyExistsException=文件 {0} 已存在,建议更名后重试。 problemDetail.run.halo.app.infra.exception.DuplicateNameException=检测到有重复的名称,请重命名后重试。 problemDetail.run.halo.app.infra.exception.PluginAlreadyExistsException=插件 {0} 已经存。 -problemDetail.io.github.resilience4j.ratelimiter.RequestNotPermitted=请求过于频繁,请稍候再试。 +problemDetail.run.halo.app.infra.exception.RateLimitExceededException=请求过于频繁,请稍候再试。 problemDetail.user.signUpFailed.disallowed=系统不允许注册新用户。 problemDetail.user.duplicateName=用户名 {0} 已存在,请更换用户名后重试。