diff --git a/bonus-admin/src/main/resources/application.yml b/bonus-admin/src/main/resources/application.yml index 32c619c..7e3accc 100644 --- a/bonus-admin/src/main/resources/application.yml +++ b/bonus-admin/src/main/resources/application.yml @@ -19,7 +19,7 @@ server: port: 8080 servlet: # 应用的访问路径 - context-path: / + context-path: /smartArchives tomcat: # tomcat的URI编码 uri-encoding: UTF-8 @@ -34,7 +34,7 @@ server: # 日志配置 logging: level: - com.bonus: debug + com.bonus: info org.springframework: warn # 用户配置 diff --git a/bonus-common/src/main/java/com/bonus/common/filter/RequestCoverFilter.java b/bonus-common/src/main/java/com/bonus/common/filter/RequestCoverFilter.java index 60fd354..ef51fb1 100644 --- a/bonus-common/src/main/java/com/bonus/common/filter/RequestCoverFilter.java +++ b/bonus-common/src/main/java/com/bonus/common/filter/RequestCoverFilter.java @@ -62,7 +62,7 @@ public class RequestCoverFilter implements Filter { // 处理不同类型的请求 if (contentType == null) { - log.info("请求头中无Content-Type信息,处理URL参数。"); +// log.info("请求头中无Content-Type信息,处理URL参数。"); handleUrlParams(httpRequest, response, chain, integrality, encrypt); } else if (contentType.contains(MediaType.APPLICATION_JSON_VALUE)) { handleBodyRequest(httpRequest, response, chain, integrality, encrypt); diff --git a/bonus-common/src/main/java/com/bonus/common/utils/SafeUtil.java b/bonus-common/src/main/java/com/bonus/common/utils/SafeUtil.java new file mode 100644 index 0000000..a4abf6f --- /dev/null +++ b/bonus-common/src/main/java/com/bonus/common/utils/SafeUtil.java @@ -0,0 +1,175 @@ +package com.bonus.common.utils; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 安全验证的工具类 + * 提供用于检测SQL注入、特殊字符、脚本注入等的工具方法 + * 作者: GeYazhong + * 日期: 2021/11/23 10:54 + */ +public class SafeUtil { + + /** + * 安全SQL模式,用于检测SQL注入的正则表达式 + * 包含常见的SQL注入关键字和注释符号 + */ + public final static String SAFE_SQL_PATTERN = "(?:')|(?:--)|(/\\*(?:.|[\\n\\r])*?\\*/)|" + + "(\\b(select|update|delete|insert|trancate|char|into|substr|ascii|declare|exec|count|master|into|drop|execute)\\b)"; + + /** + * 安全脚本模式,用于检测脚本注入的正则表达式 + * 由于平台中setfilter中使用多个参数时用到&符号,因此未包含&符号 + */ + public final static String SAFE_SCRIPT_PATTERN = + "(\\||;|\\$|'|\\'|0x0d|0x0a|\\%27|\\%3B" + + "|<>|\\[\\]|\\(\\)|/|\"" + + "|script|alert|svg|confirm|prompt|onload" + + "|%3c|%3e|%2b|@|!|img|src" + + "|%|_)"; + + /** + * 检查特殊字符的正则表达式 + * 用于判断字符串是否为数字、字母、下划线或汉字 + */ + public static final String CHECK_SPECIAL = "^[\u4E00-\u9FA5A-Za-z_][\u4E00-\u9FA5A-Za-z0-9_]{0,}$"; + + /** + * 验证字符串是否包含SQL注入关键字 + * + * @param mark 需要检查的字符串 + * @return 如果包含SQL注入关键字返回true,否则返回false + */ + public static boolean checkSafeSql(String mark) { + if (mark != null && !"".equals(mark)) { + return match(SAFE_SQL_PATTERN, mark.toLowerCase().trim()); + } + return false; + } + + /** + * 验证字符串是否包含特殊字符 + * + * @param mark 需要检查的字符串 + * @return 如果包含特殊字符返回true,否则返回false + */ + public static boolean checkSpecial(String mark) { + if (mark != null && !"".equals(mark)) { + return match(SAFE_SQL_PATTERN, mark.toLowerCase().trim()); + } + return false; + } + + /** + * 验证字符串是否包含特殊脚本字符 + * + * @param mark 需要检查的字符串 + * @return 如果包含特殊脚本字符返回true,否则返回false + */ + public static boolean checkScript(String mark) { + if (mark != null && !"".equals(mark)) { + return match(SAFE_SCRIPT_PATTERN, mark.toLowerCase().trim()); + } + return false; + } + + /** + * 执行正则表达式匹配 + * + * @param pattern 正则表达式模式 + * @param str 需要验证的字符串 + * @return 如果匹配返回true,否则返回false + */ + private static boolean match(String pattern, String str) { + Pattern p = Pattern.compile(pattern); + Matcher m = p.matcher(str); + return m.find(); + } + + /** + * 检查字符串是否符合正则表达式 + * + * @param regex 正则表达式 + * @param orginal 需要检查的字符串 + * @return 如果匹配返回true,否则返回false + */ + private static boolean isMatch(String regex, String orginal) { + if (StringUtils.isEmpty(orginal)) { + return false; + } + Pattern pattern = Pattern.compile(regex); + Matcher isNum = pattern.matcher(orginal); + return isNum.matches(); + } + + /** + * 检查字符串是否为正整数 + * + * @param orginal 需要检查的字符串 + * @return 如果是正整数返回true,否则返回false + */ + public static boolean isPositiveInteger(String orginal) { + return isMatch("^\\+{0,1}[1-9]\\d*", orginal); + } + + /** + * 检查字符串是否为负整数 + * + * @param orginal 需要检查的字符串 + * @return 如果是负整数返回true,否则返回false + */ + public static boolean isNegativeInteger(String orginal) { + return isMatch("^-[1-9]\\d*", orginal); + } + + /** + * 检查字符串是否为整数 + * + * @param orginal 需要检查的字符串 + * @return 如果是整数返回true,否则返回false + */ + public static boolean isWholeNumber(String orginal) { + return isMatch("[+-]{0,1}0", orginal) || isPositiveInteger(orginal) || isNegativeInteger(orginal); + } + + /** + * 检查字符串是否为正小数 + * + * @param orginal 需要检查的字符串 + * @return 如果是正小数返回true,否则返回false + */ + public static boolean isPositiveDecimal(String orginal) { + return isMatch("\\+{0,1}[0]\\.[1-9]*|\\+{0,1}[1-9]\\d*\\.\\d*", orginal); + } + + /** + * 检查字符串是否为负小数 + * + * @param orginal 需要检查的字符串 + * @return 如果是负小数返回true,否则返回false + */ + public static boolean isNegativeDecimal(String orginal) { + return isMatch("^-[0]\\.[1-9]*|^-[1-9]\\d*\\.\\d*", orginal); + } + + /** + * 检查字符串是否为小数 + * + * @param orginal 需要检查的字符串 + * @return 如果是小数返回true,否则返回false + */ + public static boolean isDecimal(String orginal) { + return isMatch("[-+]{0,1}\\d+\\.\\d*|[-+]{0,1}\\d*\\.\\d+", orginal); + } + + /** + * 检查字符串是否为实数(整数或小数) + * + * @param orginal 需要检查的字符串 + * @return 如果是实数返回true,否则返回false + */ + public static boolean isRealNumber(String orginal) { + return isWholeNumber(orginal) || isDecimal(orginal); + } +} diff --git a/bonus-framework/src/main/java/com/bonus/framework/config/ResourcesConfig.java b/bonus-framework/src/main/java/com/bonus/framework/config/ResourcesConfig.java index 72be28d..2836984 100644 --- a/bonus-framework/src/main/java/com/bonus/framework/config/ResourcesConfig.java +++ b/bonus-framework/src/main/java/com/bonus/framework/config/ResourcesConfig.java @@ -1,6 +1,8 @@ package com.bonus.framework.config; import java.util.concurrent.TimeUnit; + +import com.bonus.framework.interceptor.ParamSecureHandler; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -26,6 +28,12 @@ public class ResourcesConfig implements WebMvcConfigurer @Autowired private RepeatSubmitInterceptor repeatSubmitInterceptor; + @Autowired + private ParamSecureHandler paramSecureHandler; + + /** 不需要拦截地址 "/logout",*/ + public static final String[] EXCLUDEURLS = { "/login", "/refresh" }; + @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { @@ -46,6 +54,11 @@ public class ResourcesConfig implements WebMvcConfigurer public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(repeatSubmitInterceptor).addPathPatterns("/**"); + //自定义拦截器 + registry.addInterceptor(paramSecureHandler) + .addPathPatterns("/**") + .excludePathPatterns(EXCLUDEURLS) + .order(-10); } /** diff --git a/bonus-framework/src/main/java/com/bonus/framework/filter/ReplayAttackFilter.java b/bonus-framework/src/main/java/com/bonus/framework/filter/ReplayAttackFilter.java index a6c165c..9d46a66 100644 --- a/bonus-framework/src/main/java/com/bonus/framework/filter/ReplayAttackFilter.java +++ b/bonus-framework/src/main/java/com/bonus/framework/filter/ReplayAttackFilter.java @@ -33,11 +33,11 @@ import org.springframework.stereotype.Component; public class ReplayAttackFilter implements Filter { static List ignoreUrlPatterns = new ArrayList<>(); static { - ignoreUrlPatterns.add("/captchaImage"); - ignoreUrlPatterns.add("/login"); - ignoreUrlPatterns.add("/loginOut"); - ignoreUrlPatterns.add("/getInfo"); - ignoreUrlPatterns.add("/getRouters"); + ignoreUrlPatterns.add("/smartArchives/captchaImage"); + ignoreUrlPatterns.add("/smartArchives/login"); + ignoreUrlPatterns.add("/smartArchives/logout"); + ignoreUrlPatterns.add("/smartArchives/getInfo"); + ignoreUrlPatterns.add("/smartArchives/getRouters"); } @Autowired @@ -69,7 +69,6 @@ public class ReplayAttackFilter implements Filter { boolean shouldIgnore = ignoreUrlPatterns.contains(requestURI); if (shouldIgnore) { // 忽略该请求的处理 - System.out.println("忽略请求: " + requestURI); chain.doFilter(request, response); return; } @@ -172,7 +171,6 @@ public class ReplayAttackFilter implements Filter { // 构建待签名字符串 String signString = buildSignString(encryptUserId, timestamp, requestUrl, request.getMethod()); - System.err.println(signString); // 使用HMAC-SHA256计算签名 String calculatedSignature = calculateHMAC(signString, encryptSecret); diff --git a/bonus-framework/src/main/java/com/bonus/framework/interceptor/ParamSecureHandler.java b/bonus-framework/src/main/java/com/bonus/framework/interceptor/ParamSecureHandler.java new file mode 100644 index 0000000..592c384 --- /dev/null +++ b/bonus-framework/src/main/java/com/bonus/framework/interceptor/ParamSecureHandler.java @@ -0,0 +1,241 @@ +package com.bonus.framework.interceptor; + +import com.alibaba.fastjson2.JSON; +import com.bonus.common.core.domain.AjaxResult; +import com.bonus.common.utils.SafeUtil; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.AsyncHandlerInterceptor; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.*; + +import static org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE; + +/** + * @author bonus + * @data 2023/2/6 17:22 + * @description 安全参数验证 + */ +@Slf4j +@Component +public class ParamSecureHandler implements AsyncHandlerInterceptor { + + static List ignoreUrlPatterns = new ArrayList<>(); + static { + ignoreUrlPatterns.add("/smartArchives/captchaImage"); + ignoreUrlPatterns.add("/smartArchives/login"); + ignoreUrlPatterns.add("/smartArchives/logout"); + ignoreUrlPatterns.add("/smartArchives/getInfo"); + ignoreUrlPatterns.add("/smartArchives/getRouters"); + } + private String rnd = null; + + public static String ur = "/"; + + public static int max_length = 4; + + private static Map> requestLogMap = null; + + public boolean isFileUpload(HttpServletRequest request) { + String contentType = request.getContentType(); + if (StringUtils.isEmpty(contentType)) { + return false; + } + if (contentType.contains(MULTIPART_FORM_DATA_VALUE)) { + return true; + } + return false; + } + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + // 过滤部分请求 + String requestUrl = request.getRequestURI(); + boolean shouldIgnore = ignoreUrlPatterns.contains(requestUrl); + if (shouldIgnore) { + return true; + } + // 过滤文件上传功能 + if (isFileUpload(request)) { + return true; + } + XssRequestWrapper requestWrapper = new XssRequestWrapper(request); + + /** + * 校验参数是否合法 + */ + if (!requestWrapper.isChecked()) { + log.error("输入值非法: queryString={}, body={}", + StringUtils.defaultString(requestWrapper.getQueryString(), "null"), + StringUtils.defaultString(requestWrapper.getReaderParam(), "null")); + returnJson(response, "输入值非法", 500); + return false; + } +// System.err.println(JSON.toJSONString(request.getParameterMap())); + + /** + * 获取所有跳转路径参数,保留传入下个界面 + */ + Map map = requestWrapper.getParameterMap(); + boolean checkParameterMap = checkParameterMap(map, requestUrl); + if (!checkParameterMap) { + returnJson(response, "输入值非法", 500); + return false; + } + + /** + * 检查数据流参数 + */ + String readerParam = requestWrapper.getReaderParam(); + // 判断是否是文件上传,是不对流参数进行验证 + String uplFile = "/upload", upImage = "/uploadFiles",path="/"; + if (!requestUrl.contains(uplFile) && !requestUrl.contains(upImage) && !requestUrl.contains(path)) { + boolean checkReader = checkReader(readerParam, requestUrl); + if (!checkReader) { + returnJson(response, "不安全参数", 500); + return false; + } + } + return true; + } + + + private void returnJson(HttpServletResponse response, String msg, int code) { + response.reset(); + PrintWriter writer = null; + response.setCharacterEncoding("UTF-8"); + response.setContentType("applicatiopn/json;charset=utf-8"); + AjaxResult a = AjaxResult.error(code, msg); + String res = JSON.toJSONString(a); + try { + writer = response.getWriter(); + writer.println(res); + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * 检查所有页面输入的参数是否安全 request.getParameterMap() + * + * @param map + * @param requestUrl + */ + private boolean checkParameterMap(Map map, String requestUrl) { + rnd = null; + if (map != null && map.size() > 0) { + Iterator> iterator = map.entrySet().iterator(); + String value = ""; + while (iterator.hasNext()) { + value = ""; + Map.Entry entry = iterator.next(); + String pname = (String) entry.getKey(); + if (Objects.equals("token", pname)) { + continue; + } + Object objValue = entry.getValue(); + if (null == objValue) { + value = ""; + } else { + String[] values = (String[]) objValue; + for (String s : values) { + value = s + ","; + } + if (value.length() > 0) { + value = value.substring(0, value.length() - 1); + } + } + + if ("rnd".equals(pname)) { + rnd = value; + } + if (SafeUtil.checkSafeSql(value)) { + log.info("请求失败,当前请求参数不安全!请求地址:\n" + requestUrl + "\n不安全参数:" + pname + ":" + value); + return false; + } + if (SafeUtil.checkSpecial(value) || SafeUtil.checkScript(value)) { + log.info("请求失败,当前请求参数包含特殊字符!请求地址:\n" + requestUrl + "\n特殊字符参数:" + pname + ":" + value); + return false; + } + } + } + return true; + } + + /** + * 通过rnd随机参数判断相同url是否多次请求访问 + * + * @param currentRequest + * @param requestWrapper + * @return + */ + private boolean checkSameUrl(String currentRequest, XssRequestWrapper requestWrapper) { + if (rnd != null && rnd.length() > 0) { + try { + double newRnd = Double.parseDouble(rnd); + /** + * 当为空时,新增请求时间记录,第一次请求,放行,允许查询 + */ + HttpSession session = requestWrapper.getSession(); + Object obj = session.getAttribute("requestLogMap"); + if (obj == null) { + requestLogMap = new HashMap>(100); + } else { + requestLogMap = (Map>) obj; + } + + List list = requestLogMap.get(currentRequest); + if (list == null) { + list = new ArrayList(); + } else { + for (Double oldRnd : list) { + if (oldRnd == newRnd) { + log.info("请求失败,当前请求已过期!请重新登录!您的请求地址:\n" + currentRequest); + return false; + } + } + } + list.add(newRnd); + requestLogMap.put(currentRequest, list); + String brute = "requestLogMap"; + if (session.getAttribute(brute) != null) { + session.removeAttribute(brute); + } + session.setAttribute(brute, requestLogMap); + } catch (NumberFormatException e) { + log.info("rnd参数格式化错误 请检查"); + return false; + } + } else { + log.info("请求失败,rnd参数不存在!请重新登录!您的请求地址:\n" + currentRequest); + return true; + } + return true; + } + + /** + * 检查所有页面输入的参数是否安全 request.getReader() + * + * @param readerParam + * @param requestUrl + */ + private boolean checkReader(String readerParam, String requestUrl) { + if (SafeUtil.checkScript(readerParam) || SafeUtil.checkSafeSql(readerParam)) { + log.info("请求失败,当前请求参数不安全!请求地址:\n" + requestUrl + "\n不安全参数:数据流:" + readerParam); + return false; + } + return true; + } + + @Override + public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) + throws Exception { +// SecurityContextHolder.remove(); + } +} diff --git a/bonus-framework/src/main/java/com/bonus/framework/interceptor/XssRequestWrapper.java b/bonus-framework/src/main/java/com/bonus/framework/interceptor/XssRequestWrapper.java index 4cc2e87..747f52a 100644 --- a/bonus-framework/src/main/java/com/bonus/framework/interceptor/XssRequestWrapper.java +++ b/bonus-framework/src/main/java/com/bonus/framework/interceptor/XssRequestWrapper.java @@ -25,35 +25,42 @@ import java.util.regex.Pattern; public class XssRequestWrapper extends HttpServletRequestWrapper { private byte[] body; - private String queryString; - private String streamParam; - private boolean checked; public XssRequestWrapper(HttpServletRequest request) { super(request); - getParameterMap(); - BufferedReader reader; + + // 先读取请求体数据 try { - reader = request.getReader(); - StringBuilder sb = new StringBuilder(); - char[] buf = new char[1024]; - int rd; - while ((rd = reader.read(buf)) != -1) { - sb.append(buf, 0, rd); + // 读取请求体内容 + StringBuilder stringBuilder = new StringBuilder(); + BufferedReader reader = request.getReader(); + char[] charBuffer = new char[1024]; + int bytesRead; + while ((bytesRead = reader.read(charBuffer)) > 0) { + stringBuilder.append(charBuffer, 0, bytesRead); } - reader.close(); - streamParam = xssClean(sb.toString()); - setChecked(xssCleanNew(sb.toString()) && xssCleanNew(request.getQueryString())); - body = streamParam.getBytes(); + String requestBody = stringBuilder.toString(); + + // 进行XSS清理 + streamParam = xssClean(requestBody); + body = streamParam.getBytes(request.getCharacterEncoding() != null ? + request.getCharacterEncoding() : "UTF-8"); + + // 检查安全性 + String queryStr = request.getQueryString(); + setChecked(xssCleanNew(requestBody) && (queryStr == null || xssCleanNew(queryStr))); + } catch (IOException e) { - log.error(e.getLocalizedMessage(),e); + log.error("读取请求体数据失败: {}", e.getLocalizedMessage(), e); + body = new byte[0]; + streamParam = ""; + setChecked(false); } queryString = xssClean(request.getQueryString()); - } @Override @@ -63,135 +70,132 @@ public class XssRequestWrapper extends HttpServletRequestWrapper { @Override public BufferedReader getReader() throws IOException { - return new BufferedReader(new InputStreamReader(getInputStream())); + return new BufferedReader(new InputStreamReader(getInputStream(), getCharacterEncoding())); } @Override public ServletInputStream getInputStream() throws IOException { - final ByteArrayInputStream bais = new ByteArrayInputStream(body); return new ServletInputStream() { @Override public boolean isFinished() { - return false; + return bais.available() == 0; } @Override public boolean isReady() { - return false; + return true; } @Override public void setReadListener(ReadListener listener) { - + // 不需要实现 } @Override public int read() throws IOException { - int read = bais.read(); - bais.close(); - return read; + return bais.read(); } }; } + @Override public Map getParameterMap() { Map requestMap = super.getParameterMap(); - if(requestMap!=null){ - Iterator iterator = requestMap.entrySet().iterator(); + if (requestMap != null) { + Iterator> iterator = requestMap.entrySet().iterator(); while (iterator.hasNext()) { - Map.Entry me = (Map.Entry) iterator.next(); - String[] values = (String[]) me.getValue(); + Map.Entry entry = iterator.next(); + String[] values = entry.getValue(); for (int i = 0; i < values.length; i++) { if (values[i] != null) { values[i] = xssClean(values[i]); } } - }} + } + } return requestMap; } @Override public String[] getParameterValues(String paramString) { - String[] arrayOfString1 = super.getParameterValues(paramString); - if (arrayOfString1 == null){ + String[] values = super.getParameterValues(paramString); + if (values == null) { return null; } - int i = arrayOfString1.length; - String[] arrayOfString2 = new String[i]; - for (int j = 0; j < i; j++) { - if (arrayOfString1[j] != null) { - arrayOfString2[j] = xssClean(arrayOfString1[j]); + String[] cleanedValues = new String[values.length]; + for (int i = 0; i < values.length; i++) { + if (values[i] != null) { + cleanedValues[i] = xssClean(values[i]); } } - return arrayOfString2; + return cleanedValues; } @Override public String getParameter(String paramString) { - String str = super.getParameter(paramString); - if (str == null){ + String value = super.getParameter(paramString); + if (value == null) { return null; } - return xssClean(str); + return xssClean(value); } @Override public String getHeader(String paramString) { - String str = super.getHeader(paramString); - if (str == null){ + String value = super.getHeader(paramString); + if (value == null) { return null; } - return xssClean(str); + return xssClean(value); } - String regex1=""; - String regex2="src[\r\n]*=[\r\n]*'(.*?)'"; - String regex3="src[\r\n]*=[\r\n]*\"(.*?)\""; - String regex4=""; - String regex5=""; - String regex6="eval\\((.*?)\\)"; - String regex7="e-xpression\\((.*?)\\)"; - String regex8="javascript:"; - String regex9="vbscript:"; - String regex10="onload(.*?)="; + + // 正则表达式模式 + private static final String regex1 = ""; + private static final String regex2 = "src[\r\n]*=[\r\n]*'(.*?)'"; + private static final String regex3 = "src[\r\n]*=[\r\n]*\"(.*?)\""; + private static final String regex4 = ""; + private static final String regex5 = ""; + private static final String regex6 = "eval\\((.*?)\\)"; + private static final String regex7 = "e-xpression\\((.*?)\\)"; + private static final String regex8 = "javascript:"; + private static final String regex9 = "vbscript:"; + private static final String regex10 = "onload(.*?)="; + + // 添加安全脚本模式 + public static final String SAFE_SCRIPT_PATTERN = "(\\||;|\\$|'|\\'|0x0d|0x0a|\\%27|\\%3B|<>|\\[\\]|\\(\\)|/|\"|script|alert|svg|confirm|prompt|onload|%3c|%3e|%2b|@|!|img|src)"; + private String xssClean(String value) { if (value == null) { - return value; + return null; } - value = value.replaceAll("", ""); - Pattern scriptPattern = Pattern.compile(regex1, Pattern.CASE_INSENSITIVE); - value = scriptPattern.matcher(value).replaceAll(""); - scriptPattern = Pattern.compile(regex2, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL); - value = scriptPattern.matcher(value).replaceAll(""); - scriptPattern = Pattern.compile(regex3, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL); - value = scriptPattern.matcher(value).replaceAll(""); - scriptPattern = Pattern.compile(regex4, Pattern.CASE_INSENSITIVE); - value = scriptPattern.matcher(value).replaceAll(""); - scriptPattern = Pattern.compile(regex5, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL); - value = scriptPattern.matcher(value).replaceAll(""); - scriptPattern = Pattern.compile(regex6, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL); - value = scriptPattern.matcher(value).replaceAll(""); - scriptPattern = Pattern.compile(regex7, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL); - value = scriptPattern.matcher(value).replaceAll(""); - scriptPattern = Pattern.compile(regex8, Pattern.CASE_INSENSITIVE); - value = scriptPattern.matcher(value).replaceAll(""); - scriptPattern = Pattern.compile(regex9, Pattern.CASE_INSENSITIVE); - value = scriptPattern.matcher(value).replaceAll(""); - scriptPattern = Pattern.compile(regex10, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL); - value = scriptPattern.matcher(value).replaceAll(""); + // 移除空字符串替换(这行代码没有实际效果) + // value = value.replaceAll("", ""); + // 使用预编译的模式提高性能 + value = Pattern.compile(regex1, Pattern.CASE_INSENSITIVE).matcher(value).replaceAll(""); + value = Pattern.compile(regex2, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL).matcher(value).replaceAll(""); + value = Pattern.compile(regex3, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL).matcher(value).replaceAll(""); + value = Pattern.compile(regex4, Pattern.CASE_INSENSITIVE).matcher(value).replaceAll(""); + value = Pattern.compile(regex5, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL).matcher(value).replaceAll(""); + value = Pattern.compile(regex6, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL).matcher(value).replaceAll(""); + value = Pattern.compile(regex7, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL).matcher(value).replaceAll(""); + value = Pattern.compile(regex8, Pattern.CASE_INSENSITIVE).matcher(value).replaceAll(""); + value = Pattern.compile(regex9, Pattern.CASE_INSENSITIVE).matcher(value).replaceAll(""); + value = Pattern.compile(regex10, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL).matcher(value).replaceAll(""); + // 添加额外的安全过滤 + value = Pattern.compile(SAFE_SCRIPT_PATTERN, Pattern.CASE_INSENSITIVE).matcher(value).replaceAll(""); return value; } - private boolean xssCleanNew(String value) { - boolean find = false; if (value == null) { return true; } - List patterns = new ArrayList(); + + List patterns = new ArrayList<>(); patterns.add(Pattern.compile(regex1, Pattern.CASE_INSENSITIVE)); patterns.add(Pattern.compile(regex2, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL)); patterns.add(Pattern.compile(regex3, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL)); @@ -202,29 +206,21 @@ public class XssRequestWrapper extends HttpServletRequestWrapper { patterns.add(Pattern.compile(regex8, Pattern.CASE_INSENSITIVE)); patterns.add(Pattern.compile(regex9, Pattern.CASE_INSENSITIVE)); patterns.add(Pattern.compile(regex10, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL)); + patterns.add(Pattern.compile(SAFE_SCRIPT_PATTERN, Pattern.CASE_INSENSITIVE)); for (Pattern pattern : patterns) { - find = match(pattern, value) ; - if (find) { - break; + if (match(pattern, value)) { + return false; // 发现XSS攻击 } - } - return !find; + return true; // 安全 } /** - * 执行正则表达式 - * - * @param pattern - * 表达式 - * @param str - * 待验证字符串 - * @return 返回 true ,否则为 false + * 执行正则表达式匹配 */ private static boolean match(Pattern pattern, String str) { - Matcher m = pattern.matcher(str); - return m.find(); + return pattern.matcher(str).find(); } public String getReaderParam() { @@ -238,4 +234,11 @@ public class XssRequestWrapper extends HttpServletRequestWrapper { public void setChecked(boolean checked) { this.checked = checked; } -} + + + // 添加字符编码支持 + public String getCharacterEncoding() { + String encoding = super.getCharacterEncoding(); + return encoding != null ? encoding : "UTF-8"; + } +} \ No newline at end of file