|
|
@@ -0,0 +1,153 @@
|
|
|
+package org.jeecg.config.firewall.interceptor;
|
|
|
+
|
|
|
+import com.alibaba.fastjson.JSON;
|
|
|
+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.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);
|
|
|
+
|
|
|
+ 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);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 判断是否为标准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();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|