Browse Source

refactor(sign): 优化签名拦截过滤器URL匹配规则

- 将请求体保留过滤器仅应用于配置的签名拦截URL列表
- 避免之前统一拦截所有POST请求带来的空请求体问题
- 删除SQL注入拦截器及相关代码,简化拦截逻辑
- 优化SqlInjectionUtil中SQL注入关键词过滤规则,移除JNDI相关风险字符串检测
SheepHy 2 days ago
parent
commit
72e4540a12

+ 1 - 5
national-motion-base-core/src/main/java/org/jeecg/common/util/SqlInjectionUtil.java

@@ -26,7 +26,7 @@ public class SqlInjectionUtil {
 	/**
 	 * 默认—sql注入关键词
 	 */
-	private final static String XSS_STR = "and |exec |peformance_schema|information_schema|extractvalue|updatexml|geohash|gtid_subset|gtid_subtract|insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |;|or |+|--|union |jndi:|ldap:|rmi:";
+	private final static String XSS_STR = "and |exec |peformance_schema|information_schema|extractvalue|updatexml|geohash|gtid_subset|gtid_subtract|insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |;|or |+|--";
 	/**
 	 * online报表专用—sql注入关键词
 	 */
@@ -62,10 +62,6 @@ public class SqlInjectionUtil {
 			"show\\s+databases",
 			"sleep\\(\\d*\\)",
 			"sleep\\(.*\\)",
-			"union\\s+select",
-			"\\$\\{jndi:",
-			"jndi:ldap",
-			"jndi:rmi",
 	};
 	/**
 	 * sql注释的正则

+ 0 - 56
national-motion-base-core/src/main/java/org/jeecg/config/firewall/interceptor/SqlInjectionConfiguration.java

@@ -1,56 +0,0 @@
-package org.jeecg.config.firewall.interceptor;
-
-import org.springframework.context.annotation.Configuration;
-import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
-import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
-
-/**
- * SQL注入拦截器配置
- * 
- * @author system
- * @date 2025-12-05
- */
-@Configuration
-public class SqlInjectionConfiguration implements WebMvcConfigurer {
-
-    public SqlInjectionInterceptor sqlInjectionInterceptor() {
-        return new SqlInjectionInterceptor();
-    }
-
-    @Override
-    public void addInterceptors(InterceptorRegistry registry) {
-        registry.addInterceptor(sqlInjectionInterceptor())
-                .addPathPatterns("/**")  // 拦截所有请求
-                .excludePathPatterns(
-                        // 排除静态资源
-                        "/**/*.js",
-                        "/**/*.css",
-                        "/**/*.html",
-                        "/**/*.jpg",
-                        "/**/*.png",
-                        "/**/*.gif",
-                        "/**/*.ico",
-                        "/**/*.svg",
-                        "/**/*.pdf",
-                        "/**/*.ttf",
-                        "/**/*.woff",
-                        "/**/*.woff2",
-                        "/**/*.glb",
-                        "/**/*.wasm",
-                        // 排除swagger文档
-                        "/doc.html",
-                        "/swagger-ui.html",
-                        "/swagger**/**",
-                        "/webjars/**",
-                        "/v3/**",
-                        // 排除druid监控
-                        "/druid/**",
-                        // 排除错误页面
-                        "/error",
-                        // 排除WebSocket
-                        "/websocket/**",
-                        "/newsWebsocket/**",
-                        "/vxeSocket/**"
-                );
-    }
-}

+ 0 - 226
national-motion-base-core/src/main/java/org/jeecg/config/firewall/interceptor/SqlInjectionInterceptor.java

@@ -1,226 +0,0 @@
-package org.jeecg.config.firewall.interceptor;
-
-import com.alibaba.fastjson.JSON;
-import com.alibaba.fastjson.JSONObject;
-import lombok.extern.slf4j.Slf4j;
-import org.jeecg.common.api.vo.Result;
-import org.jeecg.common.exception.JeecgSqlInjectionException;
-import org.jeecg.common.util.SqlInjectionUtil;
-import org.jeecg.config.sign.util.BodyReaderHttpServletRequestWrapper;
-import org.springframework.web.servlet.HandlerInterceptor;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.Enumeration;
-import java.util.Map;
-
-/**
- * SQL注入全局拦截器
- * 拦截所有请求参数,防止SQL注入攻击
- * 
- * @author system
- * @date 2025-12-05
- */
-@Slf4j
-public class SqlInjectionInterceptor implements HandlerInterceptor {
-
-    /**
-     * 在请求处理之前进行调用
-     */
-    @Override
-    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
-        try {
-            String requestURI = request.getRequestURI();
-            log.debug("SQL注入拦截器检查请求: {}", requestURI);
-            
-            // 检查URL参数
-            checkUrlParameters(request);
-            
-            // 检查请求头参数(排除常见的安全请求头)
-            checkHeaders(request);
-            
-            // 检查POST请求的JSON Body
-            checkRequestBody(request);
-            
-            return true;
-        } catch (JeecgSqlInjectionException e) {
-            log.error("检测到SQL注入攻击! URI: {}, 错误信息: {}", request.getRequestURI(), e.getMessage());
-            handleSqlInjectionException(response, e.getMessage());
-            return false;
-        } catch (Exception e) {
-            log.error("SQL注入拦截器异常: ", e);
-            return true; // 其他异常不阻止请求继续
-        }
-    }
-
-    /**
-     * 检查URL参数
-     */
-    private void checkUrlParameters(HttpServletRequest request) {
-        Enumeration<String> parameterNames = request.getParameterNames();
-        while (parameterNames.hasMoreElements()) {
-            String paramName = parameterNames.nextElement();
-            String[] paramValues = request.getParameterValues(paramName);
-            
-            if (paramValues != null) {
-                for (String paramValue : paramValues) {
-                    if (paramValue != null && !paramValue.trim().isEmpty()) {
-                        // 进行SQL注入检测
-                        SqlInjectionUtil.filterContent(paramValue, null);
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * 检查请求头(仅检查自定义请求头,排除标准HTTP请求头)
-     */
-    private void checkHeaders(HttpServletRequest request) {
-        Enumeration<String> headerNames = request.getHeaderNames();
-        while (headerNames.hasMoreElements()) {
-            String headerName = headerNames.nextElement();
-            
-            // 排除标准HTTP请求头和常见的安全请求头
-            if (isStandardHeader(headerName)) {
-                continue;
-            }
-            
-            String headerValue = request.getHeader(headerName);
-            if (headerValue != null && !headerValue.trim().isEmpty()) {
-                // 进行SQL注入检测
-                SqlInjectionUtil.filterContent(headerValue, null);
-            }
-        }
-    }
-
-    /**
-     * 检查POST请求的JSON Body
-     */
-    private void checkRequestBody(HttpServletRequest request) {
-        // 只检查POST请求
-        if (!"POST".equalsIgnoreCase(request.getMethod())) {
-            return;
-        }
-        
-        // 如果是BodyReaderHttpServletRequestWrapper,说明已经被包装过,可以读取body
-        if (request instanceof BodyReaderHttpServletRequestWrapper) {
-            BodyReaderHttpServletRequestWrapper wrapper = (BodyReaderHttpServletRequestWrapper) request;
-            String bodyString = wrapper.getBodyString(request);
-            
-            log.info("[SQL注入检查] 开始检查POST请求Body, URI: {}, Body: {}", request.getRequestURI(), bodyString);
-            
-            if (bodyString != null && !bodyString.trim().isEmpty()) {
-                try {
-                    // 尝试解析为JSON
-                    JSONObject jsonObject = JSON.parseObject(bodyString);
-                    if (jsonObject != null) {
-                        // 递归检查JSON中的所有字符串值
-                        checkJsonValues(jsonObject);
-                    }
-                } catch (Exception e) {
-                    // 如果不是JSON格式,直接检查整个body字符串
-                    log.debug("请求body不是JSON格式,直接检查内容");
-                    SqlInjectionUtil.filterContent(bodyString, null);
-                }
-            }
-        } else {
-            log.warn("[SQL注入检查] POST请求未被BodyReaderHttpServletRequestWrapper包装! URI: {}, Request类型: {}", 
-                    request.getRequestURI(), request.getClass().getName());
-        }
-    }
-
-    /**
-     * 递归检查JSON中的所有字符串值
-     */
-    private void checkJsonValues(Object obj) {
-        if (obj == null) {
-            return;
-        }
-        
-        if (obj instanceof String) {
-            String value = (String) obj;
-            if (!value.trim().isEmpty()) {
-                log.debug("[SQL注入检查] 检查字符串值: {}", value);
-                SqlInjectionUtil.filterContent(value, null);
-            }
-        } else if (obj instanceof JSONObject) {
-            JSONObject jsonObject = (JSONObject) obj;
-            for (Map.Entry<String, Object> entry : jsonObject.entrySet()) {
-                log.debug("[SQL注入检查] 检查JSON字段: {} = {}", entry.getKey(), entry.getValue());
-                checkJsonValues(entry.getValue());
-            }
-        } else if (obj instanceof Map) {
-            Map<?, ?> map = (Map<?, ?>) obj;
-            for (Object value : map.values()) {
-                checkJsonValues(value);
-            }
-        } else if (obj instanceof Iterable) {
-            for (Object item : (Iterable<?>) obj) {
-                checkJsonValues(item);
-            }
-        }
-    }
-
-    /**
-     * 判断是否为标准HTTP请求头
-     */
-    private boolean isStandardHeader(String headerName) {
-        if (headerName == null) {
-            return true;
-        }
-        
-        String lowerHeaderName = headerName.toLowerCase();
-        
-        // 标准HTTP请求头
-        return lowerHeaderName.equals("accept") 
-            || lowerHeaderName.equals("accept-encoding") 
-            || lowerHeaderName.equals("accept-language")
-            || lowerHeaderName.equals("authorization")
-            || lowerHeaderName.equals("cache-control")
-            || lowerHeaderName.equals("connection")
-            || lowerHeaderName.equals("content-type")
-            || lowerHeaderName.equals("content-length")
-            || lowerHeaderName.equals("cookie")
-            || lowerHeaderName.equals("host")
-            || lowerHeaderName.equals("origin")
-            || lowerHeaderName.equals("referer")
-            || lowerHeaderName.equals("user-agent")
-            || lowerHeaderName.equals("x-requested-with")
-            || lowerHeaderName.equals("x-forwarded-for")
-            || lowerHeaderName.equals("x-forwarded-proto")
-            || lowerHeaderName.equals("x-real-ip")
-            // Shiro和JWT相关
-            || lowerHeaderName.equals("x-access-token")
-            || lowerHeaderName.equals("x-sign")
-            || lowerHeaderName.equals("x-timestamp")
-            // CORS相关
-            || lowerHeaderName.startsWith("access-control-")
-            || lowerHeaderName.startsWith("sec-");
-    }
-
-    /**
-     * 处理SQL注入异常,返回错误信息
-     */
-    private void handleSqlInjectionException(HttpServletResponse response, String message) {
-        response.setCharacterEncoding("UTF-8");
-        response.setContentType("application/json; charset=utf-8");
-        response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
-        
-        PrintWriter writer = null;
-        try {
-            Result<Object> result = Result.error(400, "请求参数存在安全风险,禁止访问!");
-            writer = response.getWriter();
-            writer.write(JSON.toJSONString(result));
-            writer.flush();
-        } catch (IOException e) {
-            log.error("输出SQL注入错误信息失败", e);
-        } finally {
-            if (writer != null) {
-                writer.close();
-            }
-        }
-    }
-}

+ 12 - 4
national-motion-base-core/src/main/java/org/jeecg/config/sign/interceptor/SignAuthConfiguration.java

@@ -52,10 +52,18 @@ public class SignAuthConfiguration implements WebMvcConfigurer {
         FilterRegistrationBean registration = new FilterRegistrationBean();
         registration.setFilter(requestBodyReserveFilter());
         registration.setName("requestBodyReserveFilter");
-        // 修改为拦截所有POST请求,使SQL注入拦截器能读取body
-        registration.addUrlPatterns("/*");
-        // 设置最高优先级,确保在所有Filter(包括Shiro)之前执行
-        registration.setOrder(Integer.MIN_VALUE);
+        //------------------------------------------------------------
+        //查询需要进行签名拦截的接口 signUrls
+        String signUrls = jeecgBaseConfig.getSignUrls();
+        String[] signUrlsArray = null;
+        if (StringUtils.isNotBlank(signUrls)) {
+            signUrlsArray = signUrls.split(",");
+        } else {
+            signUrlsArray = PathMatcherUtil.SIGN_URL_LIST;
+        }
+        //------------------------------------------------------------
+        // 建议此处只添加post请求地址而不是所有的都需要走过滤器
+        registration.addUrlPatterns(signUrlsArray);
         return registration;
     }
     //update-end-author:taoyan date:20220427 for: issues/I53J5E post请求X_SIGN签名拦截校验后报错, request body 为空