diff --git a/continew-starter-cache/continew-starter-cache-redisson/src/main/java/top/continew/starter/cache/redisson/util/RedisUtils.java b/continew-starter-cache/continew-starter-cache-redisson/src/main/java/top/continew/starter/cache/redisson/util/RedisUtils.java index 73ebd973..a3079d9a 100644 --- a/continew-starter-cache/continew-starter-cache-redisson/src/main/java/top/continew/starter/cache/redisson/util/RedisUtils.java +++ b/continew-starter-cache/continew-starter-cache-redisson/src/main/java/top/continew/starter/cache/redisson/util/RedisUtils.java @@ -206,6 +206,25 @@ public class RedisUtils { return rateLimiter.tryAcquire(1); } + /** + * 限流 + * + * @param key 限流key + * @param rateType 限流类型 + * @param rate 速率 + * @param rateInterval 速率间隔 + * @return -1 表示失败 + */ + public static long rateLimiter(String key, RateType rateType, int rate, int rateInterval, RateIntervalUnit unit) { + RRateLimiter rateLimiter = CLIENT.getRateLimiter(key); + rateLimiter.trySetRate(rateType, rate, rateInterval, unit); + if (rateLimiter.tryAcquire()) { + return rateLimiter.availablePermits(); + } else { + return -1L; + } + } + /** * 格式化键,将各子键用 : 拼接起来 * diff --git a/continew-starter-dependencies/pom.xml b/continew-starter-dependencies/pom.xml index aff0925a..33586ddc 100644 --- a/continew-starter-dependencies/pom.xml +++ b/continew-starter-dependencies/pom.xml @@ -473,6 +473,13 @@ ${revision} + + + top.continew + continew-starter-security-limiter + ${revision} + + top.continew diff --git a/continew-starter-security/continew-starter-security-limiter/pom.xml b/continew-starter-security/continew-starter-security-limiter/pom.xml new file mode 100644 index 00000000..68c3c5ff --- /dev/null +++ b/continew-starter-security/continew-starter-security-limiter/pom.xml @@ -0,0 +1,42 @@ + + 4.0.0 + + top.continew + continew-starter-security + ${revision} + + + continew-starter-security-limiter + ContiNew Starter 安全模块 - 限流模块 + + + + + org.redisson + redisson-spring-boot-starter + + + + org.aspectj + aspectjrt + 1.9.4 + + + org.aspectj + aspectjweaver + 1.9.4 + + + + cn.hutool + hutool-all + + + + + top.continew + continew-starter-cache-redisson + + + diff --git a/continew-starter-security/continew-starter-security-limiter/src/main/java/top/continew/starter/security/limiter/annotation/RateLimiter.java b/continew-starter-security/continew-starter-security-limiter/src/main/java/top/continew/starter/security/limiter/annotation/RateLimiter.java new file mode 100644 index 00000000..bceed306 --- /dev/null +++ b/continew-starter-security/continew-starter-security-limiter/src/main/java/top/continew/starter/security/limiter/annotation/RateLimiter.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + *

+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.continew.starter.security.limiter.annotation; + +import org.redisson.api.RateIntervalUnit; +import top.continew.starter.security.limiter.enums.LimitType; + +import java.lang.annotation.*; + +/** + * 限流注解 + * @author KAI + * @since 2.2.0 + */ +@Documented +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface RateLimiter { + + /** + * LimitType 限流模式 + * DEFAULT 全局限流 + * IP IP限流 + * CLUSTER 实例限流 + */ + LimitType limitType() default LimitType.DEFAULT; + + /** + * 缓存实例名称 + */ + String name() default ""; + + /** + * 限流key 支持 Spring EL 表达式 + */ + String key() default ""; + + /** + * 单位时间产生的令牌数 + */ + int rate() default Integer.MAX_VALUE; + + /** + * 限流时间 + */ + int rateInterval() default 0; + + /** + * 时间单位,默认毫秒 + */ + RateIntervalUnit timeUnit() default RateIntervalUnit.MILLISECONDS; + + /** + * 拒绝请求时的提示信息 + */ + String message() default "您操作过于频繁,请稍后再试!"; +} \ No newline at end of file diff --git a/continew-starter-security/continew-starter-security-limiter/src/main/java/top/continew/starter/security/limiter/annotation/RateLimiters.java b/continew-starter-security/continew-starter-security-limiter/src/main/java/top/continew/starter/security/limiter/annotation/RateLimiters.java new file mode 100644 index 00000000..b669e9bf --- /dev/null +++ b/continew-starter-security/continew-starter-security-limiter/src/main/java/top/continew/starter/security/limiter/annotation/RateLimiters.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + *

+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.continew.starter.security.limiter.annotation; + +import java.lang.annotation.*; +/** + * 限流组 + * @author KAI + * @since 2.2.0 + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface RateLimiters { + /** + * 用于管理多个 RateLimiter + */ + RateLimiter[] value(); +} diff --git a/continew-starter-security/continew-starter-security-limiter/src/main/java/top/continew/starter/security/limiter/aop/RateLimiterAspect.java b/continew-starter-security/continew-starter-security-limiter/src/main/java/top/continew/starter/security/limiter/aop/RateLimiterAspect.java new file mode 100644 index 00000000..aa9b87fc --- /dev/null +++ b/continew-starter-security/continew-starter-security-limiter/src/main/java/top/continew/starter/security/limiter/aop/RateLimiterAspect.java @@ -0,0 +1,265 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + *

+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.continew.starter.security.limiter.aop; + +import cn.hutool.crypto.SecureUtil; +import cn.hutool.extra.servlet.JakartaServletUtil; +import cn.hutool.extra.spring.SpringUtil; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.MethodSignature; +import org.redisson.api.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.expression.BeanFactoryResolver; +import org.springframework.context.expression.MethodBasedEvaluationContext; +import org.springframework.core.DefaultParameterNameDiscoverer; +import org.springframework.core.ParameterNameDiscoverer; +import org.springframework.expression.Expression; +import org.springframework.expression.ExpressionParser; +import org.springframework.expression.ParserContext; +import org.springframework.expression.common.TemplateParserContext; +import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; +import top.continew.starter.security.limiter.annotation.RateLimiter; +import top.continew.starter.security.limiter.annotation.RateLimiters; +import top.continew.starter.security.limiter.autoconfigure.RateLimiterProperties; +import top.continew.starter.security.limiter.enums.LimitType; +import top.continew.starter.security.limiter.exception.RateLimiterException; +import top.continew.starter.web.util.ServletUtils; + +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 限流 AOP 拦截器 + * + * @author KAI + * @since 2.2.0 + */ +@Aspect +@Component +public class RateLimiterAspect { + + private static final Logger log = LoggerFactory.getLogger(RateLimiterAspect.class); + + private static final ConcurrentHashMap rateLimiterCache = new ConcurrentHashMap<>(); + + private static final RedissonClient CLIENT = SpringUtil.getBean(RedissonClient.class); + + private final ParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer(); + private final ExpressionParser parser = new SpelExpressionParser(); + private final ParserContext parserContext = new TemplateParserContext(); + + @Autowired + private RateLimiterProperties rateLimiterProperties; + + /** + * 单个限流注解切点 + */ + @Pointcut("@annotation(top.continew.starter.security.limiter.annotation.RateLimiter)") + public void rateLimiterSinglePointCut() { + } + + /** + * 多个限流注解切点 + */ + @Pointcut("@annotation(top.continew.starter.security.limiter.annotation.RateLimiters)") + public void rateLimiterBatchPointCut() { + } + + /** + * 环绕通知,处理单个限流注解 + * + * @param joinPoint 切点 + * @param rateLimiter 限流注解 + * @return 返回目标方法的执行结果 + * @throws Throwable 异常 + */ + @Around("@annotation(rateLimiter)") + public Object aroundSingle(ProceedingJoinPoint joinPoint, RateLimiter rateLimiter) throws Throwable { + // 未开启限流功能,直接执行目标方法 + if (!rateLimiterProperties.isEnabled()) { + return joinPoint.proceed(); + } + if (isRateLimited(joinPoint, rateLimiter)) { + throw new RateLimiterException(rateLimiter.message()); + } + return joinPoint.proceed(); + } + + /** + * 环绕通知,处理多个限流注解 + * + * @param joinPoint 切点 + * @param rateLimiters 多个限流注解 + * @return 返回目标方法的执行结果 + * @throws Throwable 异常 + */ + @Around("@annotation(rateLimiters)") + public Object aroundBatch(ProceedingJoinPoint joinPoint, RateLimiters rateLimiters) throws Throwable { + // 未开启限流功能,直接执行目标方法 + if (!rateLimiterProperties.isEnabled()) { + return joinPoint.proceed(); + } + for (RateLimiter rateLimiter : rateLimiters.value()) { + if (isRateLimited(joinPoint, rateLimiter)) { + throw new RateLimiterException(rateLimiter.message()); + } + } + return joinPoint.proceed(); + } + + /** + * 执行限流逻辑 + * + * @param joinPoint 切点 + * @param rateLimiter 限流注解 + * @throws Throwable 异常 + */ + private boolean isRateLimited(ProceedingJoinPoint joinPoint, RateLimiter rateLimiter) throws Throwable { + try { + // 生成限流 Key + String redisKey = generateRedisKey(rateLimiter, joinPoint); + String encipherKey = SecureUtil.md5(redisKey); + // 确定限流类型 + RateType rateType = rateLimiter.limitType() == LimitType.CLUSTER ? RateType.PER_CLIENT : RateType.OVERALL; + + // 获取redisson限流实例 + RRateLimiter rRateLimiter = getRateLimiter(encipherKey); + RateIntervalUnit rateIntervalUnit = rateLimiter.timeUnit(); + int rateInterval = rateLimiter.rateInterval(); + int rate = rateLimiter.rate(); + // 判断是否需要更新限流器 + if (shouldUpdateRateLimiter(rRateLimiter, rateType, rate, rateInterval, rateIntervalUnit)) { + // 更新限流器 + rRateLimiter.setRate(rateType, rate, rateInterval, rateIntervalUnit); + } + // 尝试获取令牌 + return !rRateLimiter.tryAcquire(); + } catch (RateLimiterException e) { + throw e; + } catch (Exception e) { + throw new RuntimeException("服务器限流异常,请稍候再试", e); + } + } + + /** + * 获取 Redisson RateLimiter 实例 + * + * @param key 限流器的 Key + * @return RateLimiter 实例 + */ + private RRateLimiter getRateLimiter(String key) { + RRateLimiter rRateLimiter = rateLimiterCache.get(key); + if (rRateLimiter == null) { + // 直接创建 RateLimiter 实例 + rRateLimiter = CLIENT.getRateLimiter(key); + rateLimiterCache.put(key, rRateLimiter); + } + return rRateLimiter; + } + + /** + * 判断是否需要更新限流器配置 + * + * @param rRateLimiter 现有的限流器 + * @param rateType 限流类型(OVERALL:全局限流;PER_CLIENT:单机限流) + * @param rate 速率(指定时间间隔产生的令牌数) + * @param rateInterval 速率间隔 + * @param rateIntervalUnit 时间单位 + * @return 是否需要更新配置 + */ + private boolean shouldUpdateRateLimiter(RRateLimiter rRateLimiter, + RateType rateType, + long rate, + long rateInterval, + RateIntervalUnit rateIntervalUnit) { + + RateLimiterConfig config = rRateLimiter.getConfig(); + return !Objects.equals(config.getRateType(), rateType) || !Objects.equals(config.getRate(), rate) || !Objects + .equals(config.getRateInterval(), rateIntervalUnit.toMillis(rateInterval)); + } + + /** + * 获取限流Key + * + * @param rateLimiter RateLimiter实例 + * @param point 切点 + * @return 限流Key + */ + private String generateRedisKey(RateLimiter rateLimiter, JoinPoint point) { + // 获取限流器配置的 key + String key = rateLimiter.key(); + // 如果 key 不为空,则解析表达式并获取最终的 key + key = Optional.ofNullable(key).map(k -> { + // 获取方法签名 + MethodSignature signature = (MethodSignature)point.getSignature(); + // 获取方法参数 + Object[] args = point.getArgs(); + // 创建表达式上下文 + MethodBasedEvaluationContext context = new MethodBasedEvaluationContext(null, signature + .getMethod(), args, discoverer); + // 设置 Bean 解析器 + context.setBeanResolver(new BeanFactoryResolver(SpringUtil.getBeanFactory())); + // 解析表达式 + Expression expression; + if (StringUtils.startsWithIgnoreCase(k, parserContext.getExpressionPrefix()) && StringUtils + .endsWithIgnoreCase(k, parserContext.getExpressionSuffix())) { + expression = parser.parseExpression(k, parserContext); + } else { + expression = parser.parseExpression(k); + } + // 获取表达式结果 + return expression.getValue(context, String.class); + }).orElse(key); + + // 拼接最终的 key + StringBuilder redisKey = new StringBuilder(rateLimiterProperties.getLimiterKey()).append(ServletUtils + .getRequest() + .getRequestURI()).append(":"); + //如果缓存name 不为空 则拼接上去 + String name = rateLimiter.name(); + if (StringUtils.hasText(name)) { + redisKey.append(name); + if (!name.endsWith(":")) { + redisKey.append(":"); + } + } + // 根据限流类型添加不同的信息 + switch (rateLimiter.limitType()) { + case IP: + // 获取请求 IP + redisKey.append(JakartaServletUtil.getClientIP(ServletUtils.getRequest())).append(":"); + break; + case CLUSTER: + // 获取客户端实例 ID + redisKey.append(CLIENT.getId()).append(":"); + break; + default: + break; + } + // 添加解析后的 key + return redisKey.append(key).toString(); + } +} diff --git a/continew-starter-security/continew-starter-security-limiter/src/main/java/top/continew/starter/security/limiter/autoconfigure/RateLimiterAutoConfiguration.java b/continew-starter-security/continew-starter-security-limiter/src/main/java/top/continew/starter/security/limiter/autoconfigure/RateLimiterAutoConfiguration.java new file mode 100644 index 00000000..9c5d1f75 --- /dev/null +++ b/continew-starter-security/continew-starter-security-limiter/src/main/java/top/continew/starter/security/limiter/autoconfigure/RateLimiterAutoConfiguration.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + *

+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.continew.starter.security.limiter.autoconfigure; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import top.continew.starter.security.limiter.aop.RateLimiterAspect; + +/** + * 限流配置注入器 + * @author KAI + * @since 2.2.0 + */ + +@AutoConfiguration +@EnableConfigurationProperties(RateLimiterProperties.class) +public class RateLimiterAutoConfiguration { + + private static final Logger log = LoggerFactory.getLogger(RateLimiterAutoConfiguration.class); + + @Bean + public RateLimiterAspect rateLimiterAspect() { + log.info("[ContiNew Starter] - Auto Configuration 'RateLimiterAspect' completed initialization."); + return new RateLimiterAspect(); + } +} diff --git a/continew-starter-security/continew-starter-security-limiter/src/main/java/top/continew/starter/security/limiter/autoconfigure/RateLimiterProperties.java b/continew-starter-security/continew-starter-security-limiter/src/main/java/top/continew/starter/security/limiter/autoconfigure/RateLimiterProperties.java new file mode 100644 index 00000000..7cdc059f --- /dev/null +++ b/continew-starter-security/continew-starter-security-limiter/src/main/java/top/continew/starter/security/limiter/autoconfigure/RateLimiterProperties.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + *

+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.continew.starter.security.limiter.autoconfigure; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.util.StringUtils; + +/** + * 限流器配置属性 + * @author KAI + * @since 2.2.0 + */ +@ConfigurationProperties(prefix = "continew-starter.security.limiter") +public class RateLimiterProperties { + private boolean enabled = false; + + private String limiterKey = "RateLimiter:"; + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public String getLimiterKey() { + return limiterKey; + } + + public void setLimiterKey(String limiterKey) { + //不为空且不以":"结尾,则添加":" + if (StringUtils.hasText(limiterKey)) { + if (!limiterKey.endsWith(":")) { + limiterKey = limiterKey + ":"; + } + } + this.limiterKey = limiterKey; + } + +} diff --git a/continew-starter-security/continew-starter-security-limiter/src/main/java/top/continew/starter/security/limiter/enums/LimitType.java b/continew-starter-security/continew-starter-security-limiter/src/main/java/top/continew/starter/security/limiter/enums/LimitType.java new file mode 100644 index 00000000..edfb65b6 --- /dev/null +++ b/continew-starter-security/continew-starter-security-limiter/src/main/java/top/continew/starter/security/limiter/enums/LimitType.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + *

+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.continew.starter.security.limiter.enums; + +/** + * 限流类型 + * @author KAI + * @since 2.2.0 + */ +public enum LimitType { + + /** + * 全局限流 + */ + DEFAULT, + /** + * 根据IP限流 + */ + IP, + /** + * 根据实例限流(支持集群多实例) + */ + CLUSTER +} diff --git a/continew-starter-security/continew-starter-security-limiter/src/main/java/top/continew/starter/security/limiter/exception/RateLimiterException.java b/continew-starter-security/continew-starter-security-limiter/src/main/java/top/continew/starter/security/limiter/exception/RateLimiterException.java new file mode 100644 index 00000000..20331b2a --- /dev/null +++ b/continew-starter-security/continew-starter-security-limiter/src/main/java/top/continew/starter/security/limiter/exception/RateLimiterException.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + *

+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.continew.starter.security.limiter.exception; + +import top.continew.starter.core.exception.BaseException; + +/** + * 限流异常 + * @author KAI + * @since 2.2.0 + */ +public class RateLimiterException extends BaseException { + public RateLimiterException(String message) { + super(message); + } +} diff --git a/continew-starter-security/continew-starter-security-limiter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/continew-starter-security/continew-starter-security-limiter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000..d365e008 --- /dev/null +++ b/continew-starter-security/continew-starter-security-limiter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +top.continew.starter.security.limiter.autoconfigure.RateLimiterAutoConfiguration \ No newline at end of file diff --git a/continew-starter-security/continew-starter-security-limiter/src/main/resources/spel-extension.json b/continew-starter-security/continew-starter-security-limiter/src/main/resources/spel-extension.json new file mode 100644 index 00000000..9088db73 --- /dev/null +++ b/continew-starter-security/continew-starter-security-limiter/src/main/resources/spel-extension.json @@ -0,0 +1,7 @@ +{ + "top.continew.starter.security.limiter.annotation.RateLimiter@key":{ + "method":{ + "parameters": true + } + } +} \ No newline at end of file diff --git a/continew-starter-security/pom.xml b/continew-starter-security/pom.xml index dd87ac2f..84df7437 100644 --- a/continew-starter-security/pom.xml +++ b/continew-starter-security/pom.xml @@ -17,6 +17,7 @@ continew-starter-security-password continew-starter-security-mask continew-starter-security-crypto + continew-starter-security-limiter @@ -25,5 +26,12 @@ top.continew continew-starter-core + + + + top.continew + continew-starter-web + + \ No newline at end of file diff --git a/continew-starter-web/src/main/java/top/continew/starter/web/autoconfigure/exception/GlobalExceptionHandler.java b/continew-starter-web/src/main/java/top/continew/starter/web/autoconfigure/exception/GlobalExceptionHandler.java index 792adf6a..7401da00 100644 --- a/continew-starter-web/src/main/java/top/continew/starter/web/autoconfigure/exception/GlobalExceptionHandler.java +++ b/continew-starter-web/src/main/java/top/continew/starter/web/autoconfigure/exception/GlobalExceptionHandler.java @@ -44,6 +44,8 @@ import top.continew.starter.web.autoconfigure.i18n.I18nProperties; import top.continew.starter.web.model.R; import top.continew.starter.web.util.MessageSourceUtils; +import java.awt.image.RasterFormatException; + /** * 全局异常处理器 * @@ -195,4 +197,5 @@ public class GlobalExceptionHandler { log.error("请求地址 [{}],发生未知异常。", request.getRequestURI(), e); return R.fail(e.getMessage()); } + } \ No newline at end of file