mirror of
https://github.com/continew-org/continew-starter.git
synced 2025-09-09 04:59:21 +08:00
feat(security/limiter): 新增限流器
This commit is contained in:
@@ -206,6 +206,25 @@ public class RedisUtils {
|
|||||||
return rateLimiter.tryAcquire(1);
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 格式化键,将各子键用 : 拼接起来
|
* 格式化键,将各子键用 : 拼接起来
|
||||||
*
|
*
|
||||||
|
@@ -473,6 +473,13 @@
|
|||||||
<version>${revision}</version>
|
<version>${revision}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 安全模块 - 限流 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>top.continew</groupId>
|
||||||
|
<artifactId>continew-starter-security-limiter</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- API 文档模块 -->
|
<!-- API 文档模块 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>top.continew</groupId>
|
<groupId>top.continew</groupId>
|
||||||
|
@@ -0,0 +1,42 @@
|
|||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>top.continew</groupId>
|
||||||
|
<artifactId>continew-starter-security</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>continew-starter-security-limiter</artifactId>
|
||||||
|
<description>ContiNew Starter 安全模块 - 限流模块</description>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<!-- Redisson(不仅仅是一个 Redis Java 客户端,Redisson 充分的利用了 Redis 键值数据库提供的一系列优势,为使用者提供了一系列具有分布式特性的常用工具:分布式锁、限流器等) -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.redisson</groupId>
|
||||||
|
<artifactId>redisson-spring-boot-starter</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<!--aop-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.aspectj</groupId>
|
||||||
|
<artifactId>aspectjrt</artifactId>
|
||||||
|
<version>1.9.4</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.aspectj</groupId>
|
||||||
|
<artifactId>aspectjweaver</artifactId>
|
||||||
|
<version>1.9.4</version>
|
||||||
|
</dependency>
|
||||||
|
<!--hutool-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.hutool</groupId>
|
||||||
|
<artifactId>hutool-all</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!--Redisson缓存 模块-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>top.continew</groupId>
|
||||||
|
<artifactId>continew-starter-cache-redisson</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
@@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* 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
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* 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 "您操作过于频繁,请稍后再试!";
|
||||||
|
}
|
@@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* 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
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* 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();
|
||||||
|
}
|
@@ -0,0 +1,265 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* 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
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* 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<String, RRateLimiter> 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();
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* 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
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* 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();
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* 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
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* 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
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* 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
|
||||||
|
}
|
@@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* 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
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1 @@
|
|||||||
|
top.continew.starter.security.limiter.autoconfigure.RateLimiterAutoConfiguration
|
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"top.continew.starter.security.limiter.annotation.RateLimiter@key":{
|
||||||
|
"method":{
|
||||||
|
"parameters": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -17,6 +17,7 @@
|
|||||||
<module>continew-starter-security-password</module>
|
<module>continew-starter-security-password</module>
|
||||||
<module>continew-starter-security-mask</module>
|
<module>continew-starter-security-mask</module>
|
||||||
<module>continew-starter-security-crypto</module>
|
<module>continew-starter-security-crypto</module>
|
||||||
|
<module>continew-starter-security-limiter</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
@@ -25,5 +26,12 @@
|
|||||||
<groupId>top.continew</groupId>
|
<groupId>top.continew</groupId>
|
||||||
<artifactId>continew-starter-core</artifactId>
|
<artifactId>continew-starter-core</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Web 模块 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>top.continew</groupId>
|
||||||
|
<artifactId>continew-starter-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
@@ -44,6 +44,8 @@ import top.continew.starter.web.autoconfigure.i18n.I18nProperties;
|
|||||||
import top.continew.starter.web.model.R;
|
import top.continew.starter.web.model.R;
|
||||||
import top.continew.starter.web.util.MessageSourceUtils;
|
import top.continew.starter.web.util.MessageSourceUtils;
|
||||||
|
|
||||||
|
import java.awt.image.RasterFormatException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 全局异常处理器
|
* 全局异常处理器
|
||||||
*
|
*
|
||||||
@@ -195,4 +197,5 @@ public class GlobalExceptionHandler {
|
|||||||
log.error("请求地址 [{}],发生未知异常。", request.getRequestURI(), e);
|
log.error("请求地址 [{}],发生未知异常。", request.getRequestURI(), e);
|
||||||
return R.fail(e.getMessage());
|
return R.fail(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
Reference in New Issue
Block a user