From fa2a28e87761659820d3de5ec082e9fc2fc97513 Mon Sep 17 00:00:00 2001 From: RuoYi Date: Mon, 8 Apr 2024 13:16:52 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=95=B0=E6=8D=AE=E8=84=B1?= =?UTF-8?q?=E6=95=8F=E8=BF=87=E6=BB=A4=E6=B3=A8=E8=A7=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 7 ++ ruoyi-common/pom.xml | 1 + .../ruoyi/common/core/utils/StringUtils.java | 46 +++++++++++++ ruoyi-common/ruoyi-common-sensitive/pom.xml | 27 ++++++++ .../sensitive/annotation/Sensitive.java | 24 +++++++ .../config/SensitiveJsonSerializer.java | 67 +++++++++++++++++++ .../sensitive/enums/DesensitizedType.java | 59 ++++++++++++++++ .../sensitive/utils/DesensitizedUtil.java | 51 ++++++++++++++ 8 files changed, 282 insertions(+) create mode 100644 ruoyi-common/ruoyi-common-sensitive/pom.xml create mode 100644 ruoyi-common/ruoyi-common-sensitive/src/main/java/com/ruoyi/common/sensitive/annotation/Sensitive.java create mode 100644 ruoyi-common/ruoyi-common-sensitive/src/main/java/com/ruoyi/common/sensitive/config/SensitiveJsonSerializer.java create mode 100644 ruoyi-common/ruoyi-common-sensitive/src/main/java/com/ruoyi/common/sensitive/enums/DesensitizedType.java create mode 100644 ruoyi-common/ruoyi-common-sensitive/src/main/java/com/ruoyi/common/sensitive/utils/DesensitizedUtil.java diff --git a/pom.xml b/pom.xml index 6a4a75428..2a0937ef8 100644 --- a/pom.xml +++ b/pom.xml @@ -164,6 +164,13 @@ ${ruoyi.version} + + + com.ruoyi + ruoyi-common-sensitive + ${ruoyi.version} + + com.ruoyi diff --git a/ruoyi-common/pom.xml b/ruoyi-common/pom.xml index 42efa9da5..3129e2d6c 100644 --- a/ruoyi-common/pom.xml +++ b/ruoyi-common/pom.xml @@ -15,6 +15,7 @@ ruoyi-common-seata ruoyi-common-swagger ruoyi-common-security + ruoyi-common-sensitive ruoyi-common-datascope ruoyi-common-datasource diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/StringUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/StringUtils.java index 917bb82f9..cc9f12b85 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/StringUtils.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/StringUtils.java @@ -20,6 +20,9 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils /** 下划线 */ private static final char SEPARATOR = '_'; + /** 星号 */ + private static final char ASTERISK = '*'; + /** * 获取参数不为空值 * @@ -160,6 +163,49 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils return (str == null ? "" : str.trim()); } + /** + * 替换指定字符串的指定区间内字符为"*" + * + * @param str 字符串 + * @param startInclude 开始位置(包含) + * @param endExclude 结束位置(不包含) + * @return 替换后的字符串 + */ + public static String hide(CharSequence str, int startInclude, int endExclude) + { + if (isEmpty(str)) + { + return NULLSTR; + } + final int strLength = str.length(); + if (startInclude > strLength) + { + return NULLSTR; + } + if (endExclude > strLength) + { + endExclude = strLength; + } + if (startInclude > endExclude) + { + // 如果起始位置大于结束位置,不替换 + return NULLSTR; + } + final char[] chars = new char[strLength]; + for (int i = 0; i < strLength; i++) + { + if (i >= startInclude && i < endExclude) + { + chars[i] = ASTERISK; + } + else + { + chars[i] = str.charAt(i); + } + } + return new String(chars); + } + /** * 截取字符串 * diff --git a/ruoyi-common/ruoyi-common-sensitive/pom.xml b/ruoyi-common/ruoyi-common-sensitive/pom.xml new file mode 100644 index 000000000..f5f5c1d3a --- /dev/null +++ b/ruoyi-common/ruoyi-common-sensitive/pom.xml @@ -0,0 +1,27 @@ + + + + com.ruoyi + ruoyi-common + 3.6.4 + + 4.0.0 + + ruoyi-common-sensitive + + + ruoyi-common-sensitive数据脱敏 + + + + + + + com.ruoyi + ruoyi-common-security + + + + \ No newline at end of file diff --git a/ruoyi-common/ruoyi-common-sensitive/src/main/java/com/ruoyi/common/sensitive/annotation/Sensitive.java b/ruoyi-common/ruoyi-common-sensitive/src/main/java/com/ruoyi/common/sensitive/annotation/Sensitive.java new file mode 100644 index 000000000..30b24b3dc --- /dev/null +++ b/ruoyi-common/ruoyi-common-sensitive/src/main/java/com/ruoyi/common/sensitive/annotation/Sensitive.java @@ -0,0 +1,24 @@ +package com.ruoyi.common.sensitive.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import com.fasterxml.jackson.annotation.JacksonAnnotationsInside; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.ruoyi.common.sensitive.config.SensitiveJsonSerializer; +import com.ruoyi.common.sensitive.enums.DesensitizedType; + +/** + * 数据脱敏注解 + * + * @author ruoyi + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +@JacksonAnnotationsInside +@JsonSerialize(using = SensitiveJsonSerializer.class) +public @interface Sensitive +{ + DesensitizedType desensitizedType(); +} diff --git a/ruoyi-common/ruoyi-common-sensitive/src/main/java/com/ruoyi/common/sensitive/config/SensitiveJsonSerializer.java b/ruoyi-common/ruoyi-common-sensitive/src/main/java/com/ruoyi/common/sensitive/config/SensitiveJsonSerializer.java new file mode 100644 index 000000000..88bfe00b1 --- /dev/null +++ b/ruoyi-common/ruoyi-common-sensitive/src/main/java/com/ruoyi/common/sensitive/config/SensitiveJsonSerializer.java @@ -0,0 +1,67 @@ +package com.ruoyi.common.sensitive.config; + +import java.io.IOException; +import java.util.Objects; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.ContextualSerializer; +import com.ruoyi.common.security.utils.SecurityUtils; +import com.ruoyi.common.sensitive.annotation.Sensitive; +import com.ruoyi.common.sensitive.enums.DesensitizedType; +import com.ruoyi.system.api.model.LoginUser; + +/** + * 数据脱敏序列化过滤 + * + * @author ruoyi + */ +public class SensitiveJsonSerializer extends JsonSerializer implements ContextualSerializer +{ + private DesensitizedType desensitizedType; + + @Override + public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException + { + if (desensitization()) + { + gen.writeString(desensitizedType.desensitizer().apply(value)); + } + else + { + gen.writeString(value); + } + } + + @Override + public JsonSerializer createContextual(SerializerProvider prov, BeanProperty property) + throws JsonMappingException + { + Sensitive annotation = property.getAnnotation(Sensitive.class); + if (Objects.nonNull(annotation) && Objects.equals(String.class, property.getType().getRawClass())) + { + this.desensitizedType = annotation.desensitizedType(); + return this; + } + return prov.findValueSerializer(property.getType(), property); + } + + /** + * 是否需要脱敏处理 + */ + private boolean desensitization() + { + try + { + LoginUser securityUser = SecurityUtils.getLoginUser(); + // 管理员不脱敏 + return !securityUser.getSysUser().isAdmin(); + } + catch (Exception e) + { + return true; + } + } +} diff --git a/ruoyi-common/ruoyi-common-sensitive/src/main/java/com/ruoyi/common/sensitive/enums/DesensitizedType.java b/ruoyi-common/ruoyi-common-sensitive/src/main/java/com/ruoyi/common/sensitive/enums/DesensitizedType.java new file mode 100644 index 000000000..94ceada6a --- /dev/null +++ b/ruoyi-common/ruoyi-common-sensitive/src/main/java/com/ruoyi/common/sensitive/enums/DesensitizedType.java @@ -0,0 +1,59 @@ +package com.ruoyi.common.sensitive.enums; + +import java.util.function.Function; +import com.ruoyi.common.sensitive.utils.DesensitizedUtil; + +/** + * 脱敏类型 + * + * @author ruoyi + */ +public enum DesensitizedType +{ + /** + * 姓名,第2位星号替换 + */ + USERNAME(s -> s.replaceAll("(\\S)\\S(\\S*)", "$1*$2")), + + /** + * 密码,全部字符都用*代替 + */ + PASSWORD(DesensitizedUtil::password), + + /** + * 身份证,中间10位星号替换 + */ + ID_CARD(s -> s.replaceAll("(\\d{4})\\d{10}(\\d{4})", "$1** **** ****$2")), + + /** + * 手机号,中间4位星号替换 + */ + PHONE(s -> s.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2")), + + /** + * 电子邮箱,仅显示第一个字母和@后面的地址显示,其他星号替换 + */ + EMAIL(s -> s.replaceAll("(^.)[^@]*(@.*$)", "$1****$2")), + + /** + * 银行卡号,保留最后4位,其他星号替换 + */ + BANK_CARD(s -> s.replaceAll("\\d{15}(\\d{3})", "**** **** **** **** $1")), + + /** + * 车牌号码,包含普通车辆、新能源车辆 + */ + CAR_LICENSE(DesensitizedUtil::carLicense); + + private final Function desensitizer; + + DesensitizedType(Function desensitizer) + { + this.desensitizer = desensitizer; + } + + public Function desensitizer() + { + return desensitizer; + } +} diff --git a/ruoyi-common/ruoyi-common-sensitive/src/main/java/com/ruoyi/common/sensitive/utils/DesensitizedUtil.java b/ruoyi-common/ruoyi-common-sensitive/src/main/java/com/ruoyi/common/sensitive/utils/DesensitizedUtil.java new file mode 100644 index 000000000..9c542d682 --- /dev/null +++ b/ruoyi-common/ruoyi-common-sensitive/src/main/java/com/ruoyi/common/sensitive/utils/DesensitizedUtil.java @@ -0,0 +1,51 @@ +package com.ruoyi.common.sensitive.utils; + +import com.ruoyi.common.core.utils.StringUtils; + +/** + * 脱敏工具类 + * + * @author ruoyi + */ +public class DesensitizedUtil +{ + /** + * 密码的全部字符都用*代替,比如:****** + * + * @param password 密码 + * @return 脱敏后的密码 + */ + public static String password(String password) + { + if (StringUtils.isBlank(password)) + { + return StringUtils.EMPTY; + } + return StringUtils.repeat('*', password.length()); + } + + /** + * 车牌中间用*代替,如果是错误的车牌,不处理 + * + * @param carLicense 完整的车牌号 + * @return 脱敏后的车牌 + */ + public static String carLicense(String carLicense) + { + if (StringUtils.isBlank(carLicense)) + { + return StringUtils.EMPTY; + } + // 普通车牌 + if (carLicense.length() == 7) + { + carLicense = StringUtils.hide(carLicense, 3, 6); + } + else if (carLicense.length() == 8) + { + // 新能源车牌 + carLicense = StringUtils.hide(carLicense, 3, 7); + } + return carLicense; + } +}