From 216d49a0af3e97aba0062f493fc5bcdffe9ce92d Mon Sep 17 00:00:00 2001 From: gaoxq <376340421@qq.com> Date: Thu, 4 Dec 2025 21:24:42 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/jeesite/common/codec/EncodeUtils.java | 517 +++++++++--------- .../jeesite/modules/app/utils/MysqlUtils.java | 2 +- 2 files changed, 249 insertions(+), 270 deletions(-) diff --git a/common/src/main/java/com/jeesite/common/codec/EncodeUtils.java b/common/src/main/java/com/jeesite/common/codec/EncodeUtils.java index 1c1efe62..17a2626e 100644 --- a/common/src/main/java/com/jeesite/common/codec/EncodeUtils.java +++ b/common/src/main/java/com/jeesite/common/codec/EncodeUtils.java @@ -16,6 +16,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import jakarta.servlet.http.HttpServletRequest; + import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.net.URLEncoder; @@ -31,305 +32,283 @@ import java.util.regex.Pattern; * 3. Commons-Lang 的 xml/html escape * 4. JDK 提供的 URLEncoder * 5. XSS、SQL、orderBy 过滤器 + * * @author calvin、ThinkGem * @version 2025-7-9 */ public class EncodeUtils { - public static final String UTF_8 = "UTF-8"; + public static final String UTF_8 = "UTF-8"; - private static final Logger logger = LoggerFactory.getLogger(EncodeUtils.class); - private static final char[] BASE62 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".toCharArray(); + private static final Logger logger = LoggerFactory.getLogger(EncodeUtils.class); + private static final char[] BASE62 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".toCharArray(); - /** - * Hex编码. - */ - public static String encodeHex(byte[] input) { - return new String(Hex.encodeHex(input)); - } + /** + * Hex编码. + */ + public static String encodeHex(byte[] input) { + return new String(Hex.encodeHex(input)); + } - /** - * Hex解码. - */ - public static byte[] decodeHex(String input) { - try { - return Hex.decodeHex(input.toCharArray()); - } catch (DecoderException e) { - throw ExceptionUtils.unchecked(e); - } - } + /** + * Hex解码. + */ + public static byte[] decodeHex(String input) { + try { + return Hex.decodeHex(input.toCharArray()); + } catch (DecoderException e) { + throw ExceptionUtils.unchecked(e); + } + } - /** - * Base64编码. - */ - public static String encodeBase64(byte[] input) { - return new String(Base64.encodeBase64(input)); - } + /** + * Base64编码. + */ + public static String encodeBase64(byte[] input) { + return new String(Base64.encodeBase64(input)); + } - /** - * Base64编码. - */ - public static String encodeBase64(String input) { - if (StringUtils.isBlank(input)){ - return StringUtils.EMPTY; - } - return new String(Base64.encodeBase64(input.getBytes(StandardCharsets.UTF_8))); - } + /** + * Base64编码. + */ + public static String encodeBase64(String input) { + if (StringUtils.isBlank(input)) { + return StringUtils.EMPTY; + } + return new String(Base64.encodeBase64(input.getBytes(StandardCharsets.UTF_8))); + } -// /** -// * Base64编码, URL安全(将Base64中的URL非法字符'+'和'/'转为'-'和'_', 见RFC3548). -// */ -// public static String encodeUrlSafeBase64(byte[] input) { -// return Base64.encodeBase64URLSafe(input); -// } - /** - * Base64解码. - */ - public static byte[] decodeBase64(String input) { - return Base64.decodeBase64(input.getBytes(StandardCharsets.UTF_8)); - } + /** + * Base64解码. + */ + public static byte[] decodeBase64(String input) { + return Base64.decodeBase64(input.getBytes(StandardCharsets.UTF_8)); + } - /** - * Base64解码. - */ - public static String decodeBase64String(String input) { - if (StringUtils.isBlank(input)){ - return StringUtils.EMPTY; - } - return new String(Base64.decodeBase64(input.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8); - } + /** + * Base64解码. + */ + public static String decodeBase64String(String input) { + if (StringUtils.isBlank(input)) { + return StringUtils.EMPTY; + } + return new String(Base64.decodeBase64(input.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8); + } - /** - * Base62编码。 - */ - public static String encodeBase62(byte[] input) { - char[] chars = new char[input.length]; - for (int i = 0; i < input.length; i++) { - chars[i] = BASE62[((input[i] & 0xFF) % BASE62.length)]; - } - return new String(chars); - } + /** + * Base62编码。 + */ + public static String encodeBase62(byte[] input) { + char[] chars = new char[input.length]; + for (int i = 0; i < input.length; i++) { + chars[i] = BASE62[((input[i] & 0xFF) % BASE62.length)]; + } + return new String(chars); + } - /** - * Html 转码. - */ - public static String encodeHtml(String html) { - return StringEscapeUtils.escapeHtml4(html); - } + /** + * Html 转码. + */ + public static String encodeHtml(String html) { + return StringEscapeUtils.escapeHtml4(html); + } - /** - * Html 解码. - */ - public static String decodeHtml(String htmlEscaped) { - return StringEscapeUtils.unescapeHtml4(htmlEscaped); - } + /** + * Html 解码. + */ + public static String decodeHtml(String htmlEscaped) { + return StringEscapeUtils.unescapeHtml4(htmlEscaped); + } - /** - * Xml 转码. - */ - public static String encodeXml(String xml) { - return StringEscapeUtils.escapeXml10(xml); - } + /** + * Xml 转码. + */ + public static String encodeXml(String xml) { + return StringEscapeUtils.escapeXml10(xml); + } - /** - * Xml 解码. - */ - public static String decodeXml(String xmlEscaped) { - return StringEscapeUtils.unescapeXml(xmlEscaped); - } + /** + * Xml 解码. + */ + public static String decodeXml(String xmlEscaped) { + return StringEscapeUtils.unescapeXml(xmlEscaped); + } - /** - * URL 编码, Encode默认为UTF-8. - */ - public static String encodeUrl(String part) { - return encodeUrl(part, EncodeUtils.UTF_8); - } + /** + * URL 编码, Encode默认为UTF-8. + */ + public static String encodeUrl(String part) { + return encodeUrl(part, EncodeUtils.UTF_8); + } - /** - * URL 编码, Encode默认为UTF-8. - */ - public static String encodeUrl(String part, String encoding) { - if (part == null){ - return null; - } - try { - return URLEncoder.encode(part, StringUtils.isNotBlank(encoding) ? encoding : EncodeUtils.UTF_8); - } catch (UnsupportedEncodingException e) { - throw ExceptionUtils.unchecked(e); - } - } + /** + * URL 编码, Encode默认为UTF-8. + */ + public static String encodeUrl(String part, String encoding) { + if (part == null) { + return null; + } + try { + return URLEncoder.encode(part, StringUtils.isNotBlank(encoding) ? encoding : EncodeUtils.UTF_8); + } catch (UnsupportedEncodingException e) { + throw ExceptionUtils.unchecked(e); + } + } - /** - * URL 解码, Encode默认为UTF-8. - */ - public static String decodeUrl(String part) { - return decodeUrl(part, EncodeUtils.UTF_8); - } + /** + * URL 解码, Encode默认为UTF-8. + */ + public static String decodeUrl(String part) { + return decodeUrl(part, EncodeUtils.UTF_8); + } - /** - * URL 解码, Encode默认为UTF-8. - */ - public static String decodeUrl(String part, String encoding) { - if (part == null){ - return null; - } - try { - return URLDecoder.decode(part, StringUtils.isNotBlank(encoding) ? encoding : EncodeUtils.UTF_8); - } catch (UnsupportedEncodingException e) { - throw ExceptionUtils.unchecked(e); - } - } + /** + * URL 解码, Encode默认为UTF-8. + */ + public static String decodeUrl(String part, String encoding) { + if (part == null) { + return null; + } + try { + return URLDecoder.decode(part, StringUtils.isNotBlank(encoding) ? encoding : EncodeUtils.UTF_8); + } catch (UnsupportedEncodingException e) { + throw ExceptionUtils.unchecked(e); + } + } - /** - * URL 解码(两次), Encode默认为UTF-8. - */ - public static String decodeUrl2(String part) { - return decodeUrl(decodeUrl(part)); - } + /** + * URL 解码(两次), Encode默认为UTF-8. + */ + public static String decodeUrl2(String part) { + return decodeUrl(decodeUrl(part)); + } - // 预编译XSS过滤正则表达式 - private static final List xssPatterns = ListUtils.newArrayList( - Pattern.compile("(<\\s*(script|link|style|iframe)([\\s\\S]*?)(>|<\\/\\s*\\1\\s*>))|()", Pattern.CASE_INSENSITIVE), - Pattern.compile("\\s*(href|src)\\s*=\\s*(\"\\s*(javascript|vbscript|data):[^\"]+\"|'\\s*(javascript|vbscript|data):[^']+'|(javascript|vbscript|data):[^\\s]+)\\s*(?=>)", Pattern.CASE_INSENSITIVE), - Pattern.compile("\\s*/?\\s*on[a-zA-Z]+\\s*=\\s*(['\"]?)(.*?)\\1(?=\\s|>|/>)", Pattern.CASE_INSENSITIVE), - Pattern.compile("(eval\\((.*?)\\)|expression\\((.*?)\\))", Pattern.CASE_INSENSITIVE), - Pattern.compile("^(javascript:|vbscript:)", Pattern.CASE_INSENSITIVE) - ); + // 预编译XSS过滤正则表达式 + private static final List xssPatterns = ListUtils.newArrayList( + Pattern.compile("(<\\s*(script|link|style|iframe)([\\s\\S]*?)(>|<\\/\\s*\\1\\s*>))|()", Pattern.CASE_INSENSITIVE), + Pattern.compile("\\s*(href|src)\\s*=\\s*(\"\\s*(javascript|vbscript|data):[^\"]+\"|'\\s*(javascript|vbscript|data):[^']+'|(javascript|vbscript|data):[^\\s]+)\\s*(?=>)", Pattern.CASE_INSENSITIVE), + Pattern.compile("\\s*/?\\s*on[a-zA-Z]+\\s*=\\s*(['\"]?)(.*?)\\1(?=\\s|>|/>)", Pattern.CASE_INSENSITIVE), + Pattern.compile("(eval\\((.*?)\\)|expression\\((.*?)\\))", Pattern.CASE_INSENSITIVE), + Pattern.compile("^(javascript:|vbscript:)", Pattern.CASE_INSENSITIVE) + ); - /** - * XSS 非法字符过滤,内容以开头的用以下规则(保留标签) - * @author ThinkGem - */ - public static String xssFilter(String text) { - return xssFilter(text, null); - } + /** + * XSS 非法字符过滤,内容以开头的用以下规则(保留标签) + * + * @author ThinkGem + */ + public static String xssFilter(String text) { + return xssFilter(text, null); + } - /** - * XSS 非法字符过滤,内容以开头的用以下规则(保留标签) - * @author ThinkGem - */ - public static String xssFilter(String text, HttpServletRequest request) { - request = (request != null ? request : ServletUtils.getRequest()); - if (request != null && StringUtils.containsAny(request.getRequestURI(), ServletUtils.XSS_FILE_EXCLUDE_URI)) { - return text; - } - String oriValue = StringUtils.trim(text); - if (text != null){ - String value = oriValue; - for (Pattern pattern : xssPatterns) { - Matcher matcher = pattern.matcher(value); - if (matcher.find()) { - value = matcher.replaceAll(StringUtils.EMPTY); - } - } - // 如果开始不是HTML,XML,JOSN格式,则再进行HTML的 "、<、> 转码。 - if (!StringUtils.startsWithIgnoreCase(value, "") // HTML - && !StringUtils.startsWithIgnoreCase(value, "': - sb.append(">"); - break; - case '<': - sb.append("<"); - break; - case '\'': - sb.append("'"); - break; - case '\"': - sb.append("""); - break; - default: - sb.append(c); - break; - } - } - value = sb.toString(); - } - if (logger.isInfoEnabled() && !value.equals(oriValue)){ - logger.info("xssFilter: {} <=<=<= {} source: {}", value, text, - request != null ? request.getRequestURL() : "common"); - } - return value; - } - return null; - } + /** + * XSS 非法字符过滤,内容以开头的用以下规则(保留标签) + * + * @author ThinkGem + */ + public static String xssFilter(String text, HttpServletRequest request) { + // 获取当前请求对象(优先级:入参 > 上下文获取) + HttpServletRequest currentRequest = (request != null) ? request : ServletUtils.getRequest(); + // 排除指定URI的过滤 + if (currentRequest != null && StringUtils.containsAny(currentRequest.getRequestURI(), ServletUtils.XSS_FILE_EXCLUDE_URI)) { + return text; + } + // 空值处理 + if (text == null) { + return null; + } + // 去除首尾空格,保留原始值用于日志对比 + String originalValue = StringUtils.trim(text); + String filteredValue = originalValue; + // 执行XSS正则过滤 + if (!filteredValue.isEmpty()) { + for (Pattern pattern : xssPatterns) { + Matcher matcher = pattern.matcher(filteredValue); + if (matcher.find()) { + filteredValue = matcher.replaceAll(StringUtils.EMPTY); + } + } + } + // 记录过滤日志(仅当内容有变化时) + if (logger.isInfoEnabled() && !filteredValue.equals(originalValue)) { + String requestUrl = currentRequest != null ? currentRequest.getRequestURL().toString() : "common"; + logger.info("xssFilter: {} <=<=<= {} source: {}", filteredValue, text, requestUrl); + } + return filteredValue; + } - // 预编译SQL过滤正则表达式 - private static final Pattern sqlPattern = Pattern.compile( - "(?:')|(?:--)|(/\\*(?:.|[\\n\\r])*?\\*/)|((extractvalue|updatexml|if|mid|database|rand|user)([\\s]*?)\\()" - + "|(\\b(select|update|and|or|delete|insert|trancate|substr|ascii|declare|exec|count|master|into" - + "|drop|execute|case when|sleep|union|load_file)\\b)", Pattern.CASE_INSENSITIVE); - private static final Pattern simplePattern = Pattern.compile("[a-z0-9_\\.\\, ]*", Pattern.CASE_INSENSITIVE); - private static final Pattern columnNamePattern = Pattern.compile("[a-z0-9_\\.`\"\\[\\]]*", Pattern.CASE_INSENSITIVE); + // 预编译SQL过滤正则表达式 + private static final Pattern sqlPattern = Pattern.compile( + "(?:')|(?:--)|(/\\*(?:.|[\\n\\r])*?\\*/)|((extractvalue|updatexml|if|mid|database|rand|user)([\\s]*?)\\()" + + "|(\\b(select|update|and|or|delete|insert|trancate|substr|ascii|declare|exec|count|master|into" + + "|drop|execute|case when|sleep|union|load_file)\\b)", Pattern.CASE_INSENSITIVE); + private static final Pattern simplePattern = Pattern.compile("[a-z0-9_\\.\\, ]*", Pattern.CASE_INSENSITIVE); + private static final Pattern columnNamePattern = Pattern.compile("[a-z0-9_\\.`\"\\[\\]]*", Pattern.CASE_INSENSITIVE); - /** - * SQL过滤,防止注入,传入参数输入有select相关代码,替换空。 - * @author ThinkGem - */ - public static String sqlFilter(String text){ - return sqlFilter(text, "common"); - } + /** + * SQL过滤,防止注入,传入参数输入有select相关代码,替换空。 + * + * @author ThinkGem + */ + public static String sqlFilter(String text) { + return sqlFilter(text, "common"); + } - /** - * SQL过滤,防止注入,传入参数输入有select相关代码,替换空。 - * @author ThinkGem - */ - public static String sqlFilter(String text, String source){ - if (text != null){ - String value = text; - if (StringUtils.inString(source, "simple", "orderBy")) { - Matcher matcher = simplePattern.matcher(value); - if (!matcher.matches()) { - value = StringUtils.EMPTY; - } - } else if (StringUtils.inString(source, "columnName")) { - Matcher matcher = columnNamePattern.matcher(value); - if (!matcher.matches()) { - value = StringUtils.EMPTY; - } - } else { - Matcher matcher = sqlPattern.matcher(value); - if (matcher.find()) { - value = matcher.replaceAll(StringUtils.EMPTY); - } - } - if (logger.isWarnEnabled() && !value.equals(text)){ - logger.info("sqlFilter: {} <=<=<= {} source: {}", value, text, source); - return StringUtils.EMPTY; - } - return value; - } - return null; - } + /** + * SQL过滤,防止注入,传入参数输入有select相关代码,替换空。 + * + * @author ThinkGem + */ + public static String sqlFilter(String text, String source) { + if (text != null) { + String value = text; + if (StringUtils.inString(source, "simple", "orderBy")) { + Matcher matcher = simplePattern.matcher(value); + if (!matcher.matches()) { + value = StringUtils.EMPTY; + } + } else if (StringUtils.inString(source, "columnName")) { + Matcher matcher = columnNamePattern.matcher(value); + if (!matcher.matches()) { + value = StringUtils.EMPTY; + } + } else { + Matcher matcher = sqlPattern.matcher(value); + if (matcher.find()) { + value = matcher.replaceAll(StringUtils.EMPTY); + } + } + if (logger.isWarnEnabled() && !value.equals(text)) { + logger.info("sqlFilter: {} <=<=<= {} source: {}", value, text, source); + return StringUtils.EMPTY; + } + return value; + } + return null; + } - // 对邮箱和手机号进行安全处理 - private static final Pattern emailPattern = Pattern.compile("([\\w\\W]?)([\\w\\W]+)([\\w\\W])(@[\\w\\W]+)"); - private static final Pattern mobilePattern = Pattern.compile("(\\d{3})(\\d+)(\\d{3})"); + // 对邮箱和手机号进行安全处理 + private static final Pattern emailPattern = Pattern.compile("([\\w\\W]?)([\\w\\W]+)([\\w\\W])(@[\\w\\W]+)"); + private static final Pattern mobilePattern = Pattern.compile("(\\d{3})(\\d+)(\\d{3})"); - /** - * 手机号码进行掩码处理 - */ - public static String mobileMask(String mobile){ - if (StringUtils.isBlank(mobile)){ - return mobile; - } - return mobilePattern.matcher(mobile).replaceAll("$1****$3"); - } + /** + * 手机号码进行掩码处理 + */ + public static String mobileMask(String mobile) { + if (StringUtils.isBlank(mobile)) { + return mobile; + } + return mobilePattern.matcher(mobile).replaceAll("$1****$3"); + } - /** - * 对电子邮箱进行掩码处理 - */ - public static String emailMask(String email){ - if (StringUtils.isBlank(email)){ - return email; - } - return emailPattern.matcher(email).replaceAll("$1****$3$4"); - } + /** + * 对电子邮箱进行掩码处理 + */ + public static String emailMask(String email) { + if (StringUtils.isBlank(email)) { + return email; + } + return emailPattern.matcher(email).replaceAll("$1****$3$4"); + } } diff --git a/web-api/src/main/java/com/jeesite/modules/app/utils/MysqlUtils.java b/web-api/src/main/java/com/jeesite/modules/app/utils/MysqlUtils.java index 8cfc9055..8ef5c479 100644 --- a/web-api/src/main/java/com/jeesite/modules/app/utils/MysqlUtils.java +++ b/web-api/src/main/java/com/jeesite/modules/app/utils/MysqlUtils.java @@ -214,8 +214,8 @@ public class MysqlUtils { public static ExecResult getExecResult(BizDbConfig dbConfig, String sql) { - try { + EXEC_FILE = null; Connection conn = getConnection(dbConfig.getDbIp(), dbConfig.getDbPort(), dbConfig.getDbUsername(), dbConfig.getDbPassword()); Statement statement = conn.createStatement(); boolean isQuery = sql.trim().toUpperCase().startsWith("SELECT");