From 940a120b8d0e33b5280edcbd81e46cba53c6e80c Mon Sep 17 00:00:00 2001 From: smallchill Date: Sat, 4 Jan 2025 22:13:49 +0800 Subject: [PATCH] =?UTF-8?q?:zap:=20=E6=96=B0=E5=A2=9E=E8=84=B1=E6=95=8F?= =?UTF-8?q?=E5=B7=A5=E5=85=B7=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/tool/sensitive/SensitiveConfig.java | 51 ++++ .../core/tool/sensitive/SensitiveType.java | 43 +++ .../core/tool/sensitive/SensitiveUtil.java | 286 ++++++++++++++++++ .../core/tool/sensitive/SensitiveWord.java | 56 ++++ 4 files changed, 436 insertions(+) create mode 100644 blade-core-tool/src/main/java/org/springblade/core/tool/sensitive/SensitiveConfig.java create mode 100644 blade-core-tool/src/main/java/org/springblade/core/tool/sensitive/SensitiveType.java create mode 100644 blade-core-tool/src/main/java/org/springblade/core/tool/sensitive/SensitiveUtil.java create mode 100644 blade-core-tool/src/main/java/org/springblade/core/tool/sensitive/SensitiveWord.java diff --git a/blade-core-tool/src/main/java/org/springblade/core/tool/sensitive/SensitiveConfig.java b/blade-core-tool/src/main/java/org/springblade/core/tool/sensitive/SensitiveConfig.java new file mode 100644 index 0000000..3d63468 --- /dev/null +++ b/blade-core-tool/src/main/java/org/springblade/core/tool/sensitive/SensitiveConfig.java @@ -0,0 +1,51 @@ +/** + * Copyright (c) 2018-2099, Chill Zhuang 庄骞 (bladejava@qq.com). + *

+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springblade.core.tool.sensitive; + +import lombok.Builder; +import lombok.Data; + +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Pattern; + +/** + * 敏感信息处理配置类 + * + * @author BladeX + */ +@Builder +@Data +public class SensitiveConfig { + // 启用的内置正则脱敏类型 + private Set sensitiveTypes; + + // 启用的内置敏感词分组 + private Set sensitiveWords; + + // 自定义敏感词列表 + private List customSensitiveWords; + + // 自定义正则表达式脱敏规则 + private Map customPatterns; + + // 自定义替换文本(可选,有默认值) + private String replacement; + + // 是否按行处理 + private boolean processLineByLine; +} diff --git a/blade-core-tool/src/main/java/org/springblade/core/tool/sensitive/SensitiveType.java b/blade-core-tool/src/main/java/org/springblade/core/tool/sensitive/SensitiveType.java new file mode 100644 index 0000000..e642ca5 --- /dev/null +++ b/blade-core-tool/src/main/java/org/springblade/core/tool/sensitive/SensitiveType.java @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2018-2099, Chill Zhuang 庄骞 (bladejava@qq.com). + *

+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springblade.core.tool.sensitive; + +import lombok.Getter; + +/** + * 脱敏类型枚举. + * + * @author BladeX + */ +@Getter +public enum SensitiveType { + GLOBAL("全局", "(.{2}).*(.{2})", "$1****$2"), + MOBILE("手机号", "(\\d{3})\\d{4}(\\d{4})", "$1****$2"), + EMAIL("电子邮箱", "(\\w{2})\\w+(@\\w+\\.\\w+)", "$1****$2"), + ID_CARD("身份证号", "(\\d{4})\\d{10}(\\w{4})", "$1**********$2"), + BANK_CARD("银行卡号", "(\\d{4})\\d+(\\d{4})", "$1****$2"), + ; + + private final String desc; + private final String regex; + private final String replacement; + + SensitiveType(String desc, String regex, String replacement) { + this.desc = desc; + this.regex = regex; + this.replacement = replacement; + } +} diff --git a/blade-core-tool/src/main/java/org/springblade/core/tool/sensitive/SensitiveUtil.java b/blade-core-tool/src/main/java/org/springblade/core/tool/sensitive/SensitiveUtil.java new file mode 100644 index 0000000..db5d97d --- /dev/null +++ b/blade-core-tool/src/main/java/org/springblade/core/tool/sensitive/SensitiveUtil.java @@ -0,0 +1,286 @@ +/** + * Copyright (c) 2018-2099, Chill Zhuang 庄骞 (bladejava@qq.com). + *

+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springblade.core.tool.sensitive; + +import org.springblade.core.tool.utils.CollectionUtil; +import org.springblade.core.tool.utils.StringUtil; + +import java.util.*; +import java.util.regex.Pattern; + +/** + * 敏感信息处理工具类 + *

+ * 支持以下功能: + * 1. 内置敏感类型处理(手机号、邮箱、身份证等) + * 2. 自定义正则表达式处理 + * 3. 敏感词处理 + * 4. 支持按行处理或整体处理 + * 5. 支持自定义替换符 + * 6. 支持泛型返回值 + *

+ * + * @author BladeX + */ +public class SensitiveUtil { + private static final String DEFAULT_REPLACEMENT = "******"; + private static final String LINE_SEPARATOR = System.lineSeparator(); + + // 预编译的正则表达式 + private static final Map PATTERN_CACHE = new EnumMap<>(SensitiveType.class); + + // 预编译的默认配置 + private static final SensitiveConfig DEFAULT_CONFIG = SensitiveConfig.builder() + .sensitiveTypes(EnumSet.of( + SensitiveType.MOBILE, + SensitiveType.ID_CARD, + SensitiveType.EMAIL, + SensitiveType.BANK_CARD + )) + .sensitiveWords(EnumSet.of( + SensitiveWord.SECURE, + SensitiveWord.AUTHENTICATION + )) + .processLineByLine(true) + .replacement(DEFAULT_REPLACEMENT) + .build(); + + static { + // 预编译所有内置的正则表达式 + for (SensitiveType type : SensitiveType.values()) { + PATTERN_CACHE.put(type, Pattern.compile(type.getRegex())); + } + } + + /** + * 使用默认配置处理敏感信息 + * + * @param content 待处理内容 + * @return 处理后的结果 + */ + public static String process(String content) { + return process(content, DEFAULT_CONFIG); + } + + /** + * 使用自定义配置处理敏感信息 + * + * @param content 待处理内容 + * @param config 自定义配置 + * @return 处理后的结果 + */ + public static String process(String content, SensitiveConfig config) { + if (StringUtil.isBlank(content)) { + return content; + } + + String processedContent = content; + String placeholder = StringUtil.isBlank(config.getReplacement()) ? + DEFAULT_REPLACEMENT : config.getReplacement(); + + // 1. 处理内置敏感类型 + if (!CollectionUtil.isEmpty(config.getSensitiveTypes())) { + processedContent = processRegexPatterns(processedContent, config.getSensitiveTypes()); + } + + // 2. 处理自定义正则 + if (!CollectionUtil.isEmpty(config.getCustomPatterns())) { + processedContent = processCustomPatterns(processedContent, + config.getCustomPatterns(), + placeholder); + } + + // 3. 处理敏感词 + if (!CollectionUtil.isEmpty(config.getSensitiveWords())) { + List words = getSensitiveWords(config.getSensitiveWords()); + processedContent = processSensitiveWords(processedContent, + words, + placeholder, + config.isProcessLineByLine()); + } + + return processedContent; + } + + /** + * 处理单个敏感类型 + * + * @param content 待处理内容 + * @param type 敏感类型 + * @return 处理后的结果 + */ + public static String process(String content, SensitiveType type) { + if (StringUtil.isBlank(content) || type == null) { + return content; + } + return processRegexPatterns(content, Collections.singleton(type)); + } + + /** + * 处理多个敏感类型 + * + * @param content 待处理内容 + * @param types 敏感类型集合 + * @return 处理后的结果 + */ + public static String process(String content, Set types) { + if (StringUtil.isBlank(content) || CollectionUtil.isEmpty(types)) { + return content; + } + return processRegexPatterns(content, types); + } + + /** + * 使用自定义正则处理(使用默认替换符) + * + * @param content 待处理内容 + * @param regex 正则表达式 + * @return 处理后的结果 + */ + public static String processWithRegex(String content, String regex) { + return processWithRegex(content, regex, DEFAULT_REPLACEMENT); + } + + /** + * 使用自定义正则处理(使用自定义替换符) + * + * @param content 待处理内容 + * @param regex 正则表达式 + * @param replacement 替换内容 + * @return 处理后的结果 + */ + public static String processWithRegex(String content, String regex, String replacement) { + if (StringUtil.isBlank(content) || StringUtil.isBlank(regex)) { + return content; + } + Pattern pattern = Pattern.compile(regex); + return pattern.matcher(content).replaceAll(replacement); + } + + /** + * 处理敏感词(使用默认配置) + * + * @param content 待处理内容 + * @param words 敏感词列表 + * @return 处理后的结果 + */ + public static String processWithWords(String content, List words) { + return processWithWords(content, words, DEFAULT_REPLACEMENT, true); + } + + /** + * 处理敏感词(使用完整参数) + * + * @param content 待处理内容 + * @param words 敏感词列表 + * @param placeholder 替换符 + * @param processLineByLine 是否按行处理 + * @return 处理后的结果 + */ + public static String processWithWords(String content, + List words, + String placeholder, + boolean processLineByLine) { + if (StringUtil.isBlank(content) || CollectionUtil.isEmpty(words)) { + return content; + } + return processSensitiveWords(content, words, placeholder, processLineByLine); + } + + /** + * 处理正则表达式 + */ + private static String processRegexPatterns(String content, Set types) { + String result = content; + for (SensitiveType type : types) { + Pattern pattern = PATTERN_CACHE.get(type); + result = pattern.matcher(result).replaceAll(type.getReplacement()); + } + return result; + } + + /** + * 处理自定义正则表达式 + */ + private static String processCustomPatterns(String content, + Map patterns, + String placeholder) { + String result = content; + for (Pattern pattern : patterns.values()) { + result = pattern.matcher(result).replaceAll(placeholder); + } + return result; + } + + /** + * 获取敏感词列表 + */ + private static List getSensitiveWords(Set groups) { + List words = new ArrayList<>(); + for (SensitiveWord group : groups) { + words.addAll(group.getWords()); + } + return words; + } + + /** + * 处理敏感词 + */ + private static String processSensitiveWords(String content, + List words, + String placeholder, + boolean processLineByLine) { + return processLineByLine ? + maskSensitiveLines(content, words, placeholder) : + maskSensitiveContent(content, words, placeholder); + } + + /** + * 按行处理敏感词 + */ + private static String maskSensitiveLines(String content, + List words, + String placeholder) { + String[] lines = content.split(LINE_SEPARATOR); + StringBuilder result = new StringBuilder(); + + for (int i = 0; i < lines.length; i++) { + String line = lines[i]; + boolean containsSensitive = words.stream() + .anyMatch(word -> line.toLowerCase().contains(word.toLowerCase())); + + result.append(containsSensitive ? placeholder : line); + + if (i < lines.length - 1) { + result.append(LINE_SEPARATOR); + } + } + + return result.toString(); + } + + /** + * 处理整体内容中的敏感词 + */ + private static String maskSensitiveContent(String content, + List words, + String placeholder) { + boolean containsSensitive = words.stream() + .anyMatch(word -> content.toLowerCase().contains(word.toLowerCase())); + + return containsSensitive ? placeholder : content; + } +} diff --git a/blade-core-tool/src/main/java/org/springblade/core/tool/sensitive/SensitiveWord.java b/blade-core-tool/src/main/java/org/springblade/core/tool/sensitive/SensitiveWord.java new file mode 100644 index 0000000..86b5247 --- /dev/null +++ b/blade-core-tool/src/main/java/org/springblade/core/tool/sensitive/SensitiveWord.java @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2018-2099, Chill Zhuang 庄骞 (bladejava@qq.com). + *

+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springblade.core.tool.sensitive; + +import lombok.Getter; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * 敏感词分组枚举. + * + * @author BladeX + */ +@Getter +public enum SensitiveWord { + + // 安全敏感词组 + SECURE(Arrays.asList( + // 认证信息类 + "password", "pwd", "token", "secret", "bearer", "key", + // API相关 + "api_key", "access_token", "refresh_token", "auth_token", + // 加密相关 + "private_key", "public_key", "salt", "hash", + // 安全相关 + "security", "certificate", "credentials", + // 数据库相关 + "connection_string", "jdbc", "database_url" + )), + + // 身份验证相关敏感词 + AUTHENTICATION(Arrays.asList( + "otp", "verification_code", "auth_code", "mfa_token" + )); + + private final List words; + + SensitiveWord(List words) { + this.words = Collections.unmodifiableList(words); + } +}