From ac959c50151c035ecf6f2c7e1b59b4eac1cc8d3a Mon Sep 17 00:00:00 2001 From: GYJ <1157756119@qq.com> Date: Sun, 23 Mar 2025 19:57:44 +0800 Subject: [PATCH] =?UTF-8?q?ip=20=E8=AF=B7=E6=B1=82=E6=AC=A1=E6=95=B0?= =?UTF-8?q?=E9=99=90=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sqx/common/aspect/IpAccessCounter.java | 83 +++++++++++++++++++ .../sqx/common/aspect/RateLimitAspect.java | 12 +++ 2 files changed, 95 insertions(+) create mode 100644 src/main/java/com/sqx/common/aspect/IpAccessCounter.java diff --git a/src/main/java/com/sqx/common/aspect/IpAccessCounter.java b/src/main/java/com/sqx/common/aspect/IpAccessCounter.java new file mode 100644 index 00000000..95ac1a36 --- /dev/null +++ b/src/main/java/com/sqx/common/aspect/IpAccessCounter.java @@ -0,0 +1,83 @@ +package com.sqx.common.aspect; + +import java.util.HashMap; +import java.util.Map; +import java.util.Timer; +import java.util.TimerTask; + +/** + * @author GYJoker + */ +public class IpAccessCounter { + // 记录 IP 及其访问次数 + private static final Map IP_ACCESS_COUNT = new HashMap<>(); + // 记录 IP 及其首次访问时间 + private static final Map IP_FIRST_ACCESS_TIME = new HashMap<>(); + // 黑名单,记录 IP 及其加入黑名单的时间 + private static final Map BLACKLIST = new HashMap<>(); + // 一分钟的毫秒数 + private static final long ONE_MINUTE = 60 * 1000; + // 十分钟的毫秒数 + private static final long TEN_MINUTES = 10 * ONE_MINUTE; + // 一分钟内允许的最大访问次数 + + static { + // 定时任务,每分钟清理一次访问计数 + Timer timer = new Timer(); + timer.scheduleAtFixedRate(new TimerTask() { + @Override + public void run() { + long currentTime = System.currentTimeMillis(); + // 清理超过一分钟的访问记录 + IP_FIRST_ACCESS_TIME.entrySet().removeIf(entry -> currentTime - entry.getValue() > ONE_MINUTE); + IP_ACCESS_COUNT.keySet().retainAll(IP_FIRST_ACCESS_TIME.keySet()); + // 清理超过十分钟的黑名单记录 + BLACKLIST.entrySet().removeIf(entry -> currentTime - entry.getValue() > TEN_MINUTES); + } + }, ONE_MINUTE, ONE_MINUTE); + } + + // 检查 IP 是否可以访问 + public static boolean canAccess(String ip, Integer maxAccessCount) { + if (isBlacklisted(ip)) { + return false; + } + long currentTime = System.currentTimeMillis(); + if (!IP_FIRST_ACCESS_TIME.containsKey(ip)) { + IP_FIRST_ACCESS_TIME.put(ip, currentTime); + IP_ACCESS_COUNT.put(ip, 1); + } else { + if (currentTime - IP_FIRST_ACCESS_TIME.get(ip) <= ONE_MINUTE) { + int count = IP_ACCESS_COUNT.get(ip); + if (count + 1 > maxAccessCount) { + addToBlacklist(ip); + return false; + } + IP_ACCESS_COUNT.put(ip, count + 1); + } else { + IP_FIRST_ACCESS_TIME.put(ip, currentTime); + IP_ACCESS_COUNT.put(ip, 1); + } + } + return true; + } + + // 检查 IP 是否在黑名单中 + private static boolean isBlacklisted(String ip) { + return BLACKLIST.containsKey(ip) && System.currentTimeMillis() - BLACKLIST.get(ip) <= TEN_MINUTES; + } + + // 将 IP 加入黑名单 + private static void addToBlacklist(String ip) { + BLACKLIST.put(ip, System.currentTimeMillis()); + IP_ACCESS_COUNT.remove(ip); + IP_FIRST_ACCESS_TIME.remove(ip); + } + + public static void main(String[] args) { + String testIp = "192.168.1.1"; + for (int i = 0; i < 150; i++) { + System.out.println("Attempt " + (i + 1) + ": " + (canAccess(testIp, 120) ? "Allowed" : "Blocked")); + } + } +} diff --git a/src/main/java/com/sqx/common/aspect/RateLimitAspect.java b/src/main/java/com/sqx/common/aspect/RateLimitAspect.java index 1acc287f..b643c7b7 100644 --- a/src/main/java/com/sqx/common/aspect/RateLimitAspect.java +++ b/src/main/java/com/sqx/common/aspect/RateLimitAspect.java @@ -6,6 +6,7 @@ import com.sqx.common.exception.CzgException; import com.sqx.common.exception.LimitException; import com.sqx.common.utils.IPUtils; import com.sqx.common.utils.Result; +import com.sqx.modules.common.service.CommonInfoService; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature; @@ -13,6 +14,7 @@ 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.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; @@ -28,6 +30,10 @@ import java.util.concurrent.ConcurrentHashMap; @Component @Slf4j public class RateLimitAspect { + + @Autowired + private CommonInfoService commonInfoService; + private ConcurrentHashMap RATE_LIMITER = new ConcurrentHashMap<>(); private RateLimiter rateLimiter; @@ -43,6 +49,12 @@ public class RateLimitAspect { // 获取请求的 IP 地址 String ip = IPUtils.getIpAddr(request); + Integer maxAccessCount = Integer.parseInt(commonInfoService.findOne(935).getValue()); + if (!IpAccessCounter.canAccess(ip, maxAccessCount)) { + log.info("一分钟内请求超过限制,IP: {}, 最大访问次数: {}", ip, maxAccessCount); + throw new LimitException("访问频率过高,请稍后再试"); + } + // 获取方法上的 @AccessLimit 注解 MethodSignature signature = (MethodSignature) point.getSignature(); Method method = signature.getMethod();