From 1140a6c3339019d38af581f602fc72889ef30b58 Mon Sep 17 00:00:00 2001
From: RuoYi <yzz_ivy@163.com>
Date: 星期一, 08 四月 2024 13:16:27 +0800
Subject: [PATCH] 新增数据脱敏过滤注解
---
ruoyi-common/src/main/java/com/ruoyi/common/utils/StringUtils.java | 46 +++++++++
ruoyi-common/src/main/java/com/ruoyi/common/annotation/Sensitive.java | 24 ++++
ruoyi-common/src/main/java/com/ruoyi/common/utils/DesensitizedUtil.java | 49 +++++++++
ruoyi-common/src/main/java/com/ruoyi/common/config/serializer/SensitiveJsonSerializer.java | 67 +++++++++++++
ruoyi-common/src/main/java/com/ruoyi/common/enums/DesensitizedType.java | 59 +++++++++++
5 files changed, 245 insertions(+), 0 deletions(-)
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Sensitive.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Sensitive.java
new file mode 100644
index 0000000..c0621e9
--- /dev/null
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Sensitive.java
@@ -0,0 +1,24 @@
+package com.ruoyi.common.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.config.serializer.SensitiveJsonSerializer;
+import com.ruoyi.common.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/src/main/java/com/ruoyi/common/config/serializer/SensitiveJsonSerializer.java b/ruoyi-common/src/main/java/com/ruoyi/common/config/serializer/SensitiveJsonSerializer.java
new file mode 100644
index 0000000..e819a1d
--- /dev/null
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/config/serializer/SensitiveJsonSerializer.java
@@ -0,0 +1,67 @@
+package com.ruoyi.common.config.serializer;
+
+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.annotation.Sensitive;
+import com.ruoyi.common.core.domain.model.LoginUser;
+import com.ruoyi.common.enums.DesensitizedType;
+import com.ruoyi.common.utils.SecurityUtils;
+
+/**
+ * 数据脱敏序列化过滤
+ *
+ * @author ruoyi
+ */
+public class SensitiveJsonSerializer extends JsonSerializer<String> 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.getUser().isAdmin();
+ }
+ catch (Exception e)
+ {
+ return true;
+ }
+ }
+}
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/DesensitizedType.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/DesensitizedType.java
new file mode 100644
index 0000000..4508122
--- /dev/null
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/DesensitizedType.java
@@ -0,0 +1,59 @@
+package com.ruoyi.common.enums;
+
+import java.util.function.Function;
+import com.ruoyi.common.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<String, String> desensitizer;
+
+ DesensitizedType(Function<String, String> desensitizer)
+ {
+ this.desensitizer = desensitizer;
+ }
+
+ public Function<String, String> desensitizer()
+ {
+ return desensitizer;
+ }
+}
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/DesensitizedUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/DesensitizedUtil.java
new file mode 100644
index 0000000..f8a4c02
--- /dev/null
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/DesensitizedUtil.java
@@ -0,0 +1,49 @@
+package com.ruoyi.common.utils;
+
+/**
+ * 脱敏工具类
+ *
+ * @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;
+ }
+}
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/StringUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/StringUtils.java
index 215d115..fc6c6b5 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/StringUtils.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/StringUtils.java
@@ -23,6 +23,9 @@
/** 下划线 */
private static final char SEPARATOR = '_';
+ /** 星号 */
+ private static final char ASTERISK = '*';
+
/**
* 获取参数不为空值
*
@@ -164,6 +167,49 @@
}
/**
+ * 替换指定字符串的指定区间内字符为"*"
+ *
+ * @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);
+ }
+
+ /**
* 截取字符串
*
* @param str 字符串
--
Gitblit v1.9.2