Browse Source

feat: android服务新增IP黑名单校验

hidewnd 2 days ago
parent
commit
6319bfd2ee

+ 2 - 2
ReadMe.md

@@ -827,7 +827,7 @@ question-service.jar > quick.log 2>&1 &
 ```shell
 ```shell
 # 逆向思维王 relogic-service
 # 逆向思维王 relogic-service
 # 10.206.16.10
 # 10.206.16.10
-# 10.206.16.15
+# 10.206.16.11
 nohup java -jar -Xms1024m -Xmx1024m -XX:MaxMetaspaceSize=256M -XX:+UseCompressedOops \
 nohup java -jar -Xms1024m -Xmx1024m -XX:MaxMetaspaceSize=256M -XX:+UseCompressedOops \
 -XX:+UseG1GC -XX:ConcGCThreads=2 -XX:InitiatingHeapOccupancyPercent=35 \
 -XX:+UseG1GC -XX:ConcGCThreads=2 -XX:InitiatingHeapOccupancyPercent=35 \
 -XX:G1ReservePercent=10 -XX:MaxGCPauseMillis=300 \
 -XX:G1ReservePercent=10 -XX:MaxGCPauseMillis=300 \
@@ -986,7 +986,7 @@ lemonios-service.jar > journey.log 2>&1 &
 ```shell
 ```shell
 # 小树问答 treeios-service
 # 小树问答 treeios-service
 # 10.206.0.3
 # 10.206.0.3
-
+# 10.206.16.11
 nohup java -jar -Xms1024m -Xmx1024m -XX:MaxMetaspaceSize=256M -XX:+UseCompressedOops \
 nohup java -jar -Xms1024m -Xmx1024m -XX:MaxMetaspaceSize=256M -XX:+UseCompressedOops \
 -XX:+UseG1GC -XX:ConcGCThreads=2 -XX:InitiatingHeapOccupancyPercent=35 \
 -XX:+UseG1GC -XX:ConcGCThreads=2 -XX:InitiatingHeapOccupancyPercent=35 \
 -XX:G1ReservePercent=10 -XX:MaxGCPauseMillis=300 \
 -XX:G1ReservePercent=10 -XX:MaxGCPauseMillis=300 \

+ 85 - 0
yt-common/src/main/java/com/ytpm/util/ResourceUtils.java

@@ -0,0 +1,85 @@
+package com.ytpm.util;
+
+
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.core.io.Resource;
+import org.springframework.util.FileCopyUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * @author lih
+ * @date 2025-11-11 13:31
+ */
+public class ResourceUtils {
+
+
+    /**
+     * 优先读取JAR同级目录slogans.txt,不存在则读取resource目录
+     *
+     * @return 每行内容的List<String>(过滤空行)
+     * @throws IOException 外部和内部文件均读取失败时抛出
+     */
+    public static List<String> readFileToList(String fileName) throws IOException {
+        // 1. 构造JAR同级目录的外部文件路径
+        // user.dir = JAR所在的同级目录(执行java -jar命令的目录)
+        String externalFilePath = Paths.get(System.getProperty("user.dir"), fileName).toString();
+        File externalFile = new File(externalFilePath);
+
+        // 2. 优先读取外部文件(存在且可读)
+        if (externalFile.exists() && externalFile.canRead()) {
+            try {
+                return readFileToList(externalFile);
+            } catch (IOException e) {
+                // 外部文件存在但读取失败(如权限不足),降级读取内部文件
+            }
+        }
+
+        // 3. 读取内部resource目录文件(兜底方案)
+        Resource internalResource = new ClassPathResource(fileName);
+        if (!internalResource.exists()) {
+            throw new IOException("外部文件(" + externalFilePath + ")和内部resource文件均不存在");
+        }
+        return readFileToList(internalResource);
+    }
+
+    /**
+     * 读取本地File文件转为List<String>
+     */
+    private static List<String> readFileToList(File file) throws IOException {
+        try (InputStreamReader reader = new InputStreamReader(
+                Files.newInputStream(file.toPath()), StandardCharsets.UTF_8)) {
+            String content = FileCopyUtils.copyToString(reader);
+            return processContent(content);
+        }
+    }
+
+    /**
+     * 读取resource目录文件转为List<String>
+     */
+    private static List<String> readFileToList(Resource resource) throws IOException {
+        try (InputStreamReader reader = new InputStreamReader(
+                resource.getInputStream(), StandardCharsets.UTF_8)) {
+            String content = FileCopyUtils.copyToString(reader);
+            return processContent(content);
+        }
+    }
+
+    /**
+     * 处理文件内容:分割换行符、过滤空行、去除前后空白
+     */
+    private static List<String> processContent(String content) {
+        return Arrays.stream(content.split("\\r?\\n"))  // 兼容Windows/Linux换行符
+                .map(String::trim)                      // 去除每行前后空白(可选)
+                .filter(line -> !line.isEmpty())         // 过滤空行(可选)
+                .collect(Collectors.toList());
+    }
+}

+ 24 - 0
yt-question/yt-question-service/src/main/java/com/ytpm/question/config/interceptor/HttpInterceptor.java

@@ -1,5 +1,8 @@
 package com.ytpm.question.config.interceptor;
 package com.ytpm.question.config.interceptor;
 
 
+import cn.hutool.core.util.StrUtil;
+import com.ytpm.handle.CommonException;
+import com.ytpm.question.redis.RedisService;
 import lombok.NonNull;
 import lombok.NonNull;
 import lombok.extern.slf4j.Slf4j;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.beans.factory.annotation.Value;
@@ -7,20 +10,29 @@ import org.springframework.stereotype.Component;
 import org.springframework.web.servlet.HandlerInterceptor;
 import org.springframework.web.servlet.HandlerInterceptor;
 import org.springframework.web.servlet.ModelAndView;
 import org.springframework.web.servlet.ModelAndView;
 
 
+import javax.annotation.Resource;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpServletResponse;
+import java.util.Arrays;
 import java.util.Date;
 import java.util.Date;
 
 
 @Slf4j
 @Slf4j
 @Component
 @Component
 public class HttpInterceptor implements HandlerInterceptor {
 public class HttpInterceptor implements HandlerInterceptor {
 
 
+    @Resource
+    private RedisService redisService;
+
     @Value("${spring.application.name-zh:}")
     @Value("${spring.application.name-zh:}")
     private String applicationNameZh;
     private String applicationNameZh;
 
 
+    @Value("${spring.application.name:}")
+    private String applicationName;
+
     @Override
     @Override
     public boolean preHandle(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response,
     public boolean preHandle(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response,
                              @NonNull Object obj) {
                              @NonNull Object obj) {
+        checkBlackIp(request);
         // 获取客户端IP地址
         // 获取客户端IP地址
         String clientIp = getClientIp(request);
         String clientIp = getClientIp(request);
         String requestURI = request.getRequestURI();
         String requestURI = request.getRequestURI();
@@ -28,6 +40,18 @@ public class HttpInterceptor implements HandlerInterceptor {
         return true;
         return true;
     }
     }
 
 
+    /**
+     * 黑名单校验
+     */
+    private void checkBlackIp(HttpServletRequest request) {
+        String clientIp = getClientIp(request);
+        String serviceName = StrUtil.replace(applicationName, "-service", "");
+        String str = redisService.getStr(StrUtil.format("blackIps:{}", serviceName));
+        if (StrUtil.isNotEmpty(str) && Arrays.asList(str.split(",")).contains(clientIp)) {
+            throw new CommonException("not allow request");
+        }
+    }
+
     private String getClientIp(HttpServletRequest request) {
     private String getClientIp(HttpServletRequest request) {
         String xfHeader = request.getHeader("X-Forwarded-For");
         String xfHeader = request.getHeader("X-Forwarded-For");
         if (xfHeader == null) {
         if (xfHeader == null) {

+ 46 - 0
yt-question/yt-question-service/src/main/java/com/ytpm/question/config/redis/RedisCacheInitializer.java

@@ -0,0 +1,46 @@
+package com.ytpm.question.config.redis;
+
+
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
+import com.ytpm.question.redis.RedisService;
+import com.ytpm.util.ResourceUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.context.event.ApplicationReadyEvent;
+import org.springframework.context.event.EventListener;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * @author lih
+ * @date 2025-11-11 14:00
+ */
+@Slf4j
+@Component
+public class RedisCacheInitializer {
+    @Resource
+    private RedisService redisService;
+
+    @Value("${spring.application.name:}")
+    private String applicationName;
+
+    @Value("${spring.config.black-ip.file-name:blackIpList.txt}")
+    private String configFileName;
+
+    @EventListener(ApplicationReadyEvent.class)
+    public void initRedisCache() {
+        try {
+            List<String> strings = ResourceUtils.readFileToList(configFileName);
+            if (CollectionUtils.isNotEmpty(strings)) {
+                String serviceName = StrUtil.replace(applicationName, "-service", "");
+                String cacheKey = StrUtil.format("blackIps:{}", serviceName);
+                redisService.setStr(cacheKey, String.join(",", strings));
+                log.info("已加载IP黑名单:{}", String.join(",", strings));
+            }
+        } catch (IOException ignored) {}
+    }
+}