Kaynağa Gözat

新增数据脱敏过滤注解

RuoYi 2 yıl önce
ebeveyn
işleme
fa2a28e877

+ 7 - 0
pom.xml

@@ -164,6 +164,13 @@
164 164
                 <version>${ruoyi.version}</version>
165 165
             </dependency>
166 166
 
167
+            <!-- 数据脱敏 -->
168
+            <dependency>
169
+                <groupId>com.ruoyi</groupId>
170
+                <artifactId>ruoyi-common-sensitive</artifactId>
171
+                <version>${ruoyi.version}</version>
172
+            </dependency>
173
+
167 174
             <!-- 权限范围 -->
168 175
             <dependency>
169 176
                 <groupId>com.ruoyi</groupId>

+ 1 - 0
ruoyi-common/pom.xml

@@ -15,6 +15,7 @@
15 15
         <module>ruoyi-common-seata</module>
16 16
         <module>ruoyi-common-swagger</module>
17 17
         <module>ruoyi-common-security</module>
18
+        <module>ruoyi-common-sensitive</module>
18 19
         <module>ruoyi-common-datascope</module>
19 20
         <module>ruoyi-common-datasource</module>
20 21
     </modules>

+ 46 - 0
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
20 20
     /** 下划线 */
21 21
     private static final char SEPARATOR = '_';
22 22
 
23
+    /** 星号 */
24
+    private static final char ASTERISK = '*';
25
+
23 26
     /**
24 27
      * 获取参数不为空值
25 28
      * 
@@ -161,6 +164,49 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils
161 164
     }
162 165
 
163 166
     /**
167
+     * 替换指定字符串的指定区间内字符为"*"
168
+     *
169
+     * @param str 字符串
170
+     * @param startInclude 开始位置(包含)
171
+     * @param endExclude 结束位置(不包含)
172
+     * @return 替换后的字符串
173
+     */
174
+    public static String hide(CharSequence str, int startInclude, int endExclude)
175
+    {
176
+        if (isEmpty(str))
177
+        {
178
+            return NULLSTR;
179
+        }
180
+        final int strLength = str.length();
181
+        if (startInclude > strLength)
182
+        {
183
+            return NULLSTR;
184
+        }
185
+        if (endExclude > strLength)
186
+        {
187
+            endExclude = strLength;
188
+        }
189
+        if (startInclude > endExclude)
190
+        {
191
+            // 如果起始位置大于结束位置,不替换
192
+            return NULLSTR;
193
+        }
194
+        final char[] chars = new char[strLength];
195
+        for (int i = 0; i < strLength; i++)
196
+        {
197
+            if (i >= startInclude && i < endExclude)
198
+            {
199
+                chars[i] = ASTERISK;
200
+            }
201
+            else
202
+            {
203
+                chars[i] = str.charAt(i);
204
+            }
205
+        }
206
+        return new String(chars);
207
+    }
208
+
209
+    /**
164 210
      * 截取字符串
165 211
      * 
166 212
      * @param str 字符串

+ 27 - 0
ruoyi-common/ruoyi-common-sensitive/pom.xml

@@ -0,0 +1,27 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3
+         xmlns="http://maven.apache.org/POM/4.0.0"
4
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5
+    <parent>
6
+        <groupId>com.ruoyi</groupId>
7
+        <artifactId>ruoyi-common</artifactId>
8
+        <version>3.6.4</version>
9
+    </parent>
10
+    <modelVersion>4.0.0</modelVersion>
11
+    
12
+    <artifactId>ruoyi-common-sensitive</artifactId>
13
+
14
+    <description>
15
+        ruoyi-common-sensitive数据脱敏
16
+    </description>
17
+
18
+    <dependencies>
19
+
20
+        <!-- RuoYi Common Security -->
21
+        <dependency>
22
+            <groupId>com.ruoyi</groupId>
23
+            <artifactId>ruoyi-common-security</artifactId>
24
+        </dependency>
25
+
26
+    </dependencies>
27
+</project>

+ 24 - 0
ruoyi-common/ruoyi-common-sensitive/src/main/java/com/ruoyi/common/sensitive/annotation/Sensitive.java

@@ -0,0 +1,24 @@
1
+package com.ruoyi.common.sensitive.annotation;
2
+
3
+import java.lang.annotation.ElementType;
4
+import java.lang.annotation.Retention;
5
+import java.lang.annotation.RetentionPolicy;
6
+import java.lang.annotation.Target;
7
+import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
8
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
9
+import com.ruoyi.common.sensitive.config.SensitiveJsonSerializer;
10
+import com.ruoyi.common.sensitive.enums.DesensitizedType;
11
+
12
+/**
13
+ * 数据脱敏注解
14
+ *
15
+ * @author ruoyi
16
+ */
17
+@Retention(RetentionPolicy.RUNTIME)
18
+@Target(ElementType.FIELD)
19
+@JacksonAnnotationsInside
20
+@JsonSerialize(using = SensitiveJsonSerializer.class)
21
+public @interface Sensitive
22
+{
23
+    DesensitizedType desensitizedType();
24
+}

+ 67 - 0
ruoyi-common/ruoyi-common-sensitive/src/main/java/com/ruoyi/common/sensitive/config/SensitiveJsonSerializer.java

@@ -0,0 +1,67 @@
1
+package com.ruoyi.common.sensitive.config;
2
+
3
+import java.io.IOException;
4
+import java.util.Objects;
5
+import com.fasterxml.jackson.core.JsonGenerator;
6
+import com.fasterxml.jackson.databind.BeanProperty;
7
+import com.fasterxml.jackson.databind.JsonMappingException;
8
+import com.fasterxml.jackson.databind.JsonSerializer;
9
+import com.fasterxml.jackson.databind.SerializerProvider;
10
+import com.fasterxml.jackson.databind.ser.ContextualSerializer;
11
+import com.ruoyi.common.security.utils.SecurityUtils;
12
+import com.ruoyi.common.sensitive.annotation.Sensitive;
13
+import com.ruoyi.common.sensitive.enums.DesensitizedType;
14
+import com.ruoyi.system.api.model.LoginUser;
15
+
16
+/**
17
+ * 数据脱敏序列化过滤
18
+ *
19
+ * @author ruoyi
20
+ */
21
+public class SensitiveJsonSerializer extends JsonSerializer<String> implements ContextualSerializer
22
+{
23
+    private DesensitizedType desensitizedType;
24
+
25
+    @Override
26
+    public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException
27
+    {
28
+        if (desensitization())
29
+        {
30
+            gen.writeString(desensitizedType.desensitizer().apply(value));
31
+        }
32
+        else
33
+        {
34
+            gen.writeString(value);
35
+        }
36
+    }
37
+
38
+    @Override
39
+    public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property)
40
+            throws JsonMappingException
41
+    {
42
+        Sensitive annotation = property.getAnnotation(Sensitive.class);
43
+        if (Objects.nonNull(annotation) && Objects.equals(String.class, property.getType().getRawClass()))
44
+        {
45
+            this.desensitizedType = annotation.desensitizedType();
46
+            return this;
47
+        }
48
+        return prov.findValueSerializer(property.getType(), property);
49
+    }
50
+
51
+    /**
52
+     * 是否需要脱敏处理
53
+     */
54
+    private boolean desensitization()
55
+    {
56
+        try
57
+        {
58
+            LoginUser securityUser = SecurityUtils.getLoginUser();
59
+            // 管理员不脱敏
60
+            return !securityUser.getSysUser().isAdmin();
61
+        }
62
+        catch (Exception e)
63
+        {
64
+            return true;
65
+        }
66
+    }
67
+}

+ 59 - 0
ruoyi-common/ruoyi-common-sensitive/src/main/java/com/ruoyi/common/sensitive/enums/DesensitizedType.java

@@ -0,0 +1,59 @@
1
+package com.ruoyi.common.sensitive.enums;
2
+
3
+import java.util.function.Function;
4
+import com.ruoyi.common.sensitive.utils.DesensitizedUtil;
5
+
6
+/**
7
+ * 脱敏类型
8
+ *
9
+ * @author ruoyi
10
+ */
11
+public enum DesensitizedType
12
+{
13
+    /**
14
+     * 姓名,第2位星号替换
15
+     */
16
+    USERNAME(s -> s.replaceAll("(\\S)\\S(\\S*)", "$1*$2")),
17
+
18
+    /**
19
+     * 密码,全部字符都用*代替
20
+     */
21
+    PASSWORD(DesensitizedUtil::password),
22
+
23
+    /**
24
+     * 身份证,中间10位星号替换
25
+     */
26
+    ID_CARD(s -> s.replaceAll("(\\d{4})\\d{10}(\\d{4})", "$1** **** ****$2")),
27
+
28
+    /**
29
+     * 手机号,中间4位星号替换
30
+     */
31
+    PHONE(s -> s.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2")),
32
+
33
+    /**
34
+     * 电子邮箱,仅显示第一个字母和@后面的地址显示,其他星号替换
35
+     */
36
+    EMAIL(s -> s.replaceAll("(^.)[^@]*(@.*$)", "$1****$2")),
37
+
38
+    /**
39
+     * 银行卡号,保留最后4位,其他星号替换
40
+     */
41
+    BANK_CARD(s -> s.replaceAll("\\d{15}(\\d{3})", "**** **** **** **** $1")),
42
+
43
+    /**
44
+     * 车牌号码,包含普通车辆、新能源车辆
45
+     */
46
+    CAR_LICENSE(DesensitizedUtil::carLicense);
47
+
48
+    private final Function<String, String> desensitizer;
49
+
50
+    DesensitizedType(Function<String, String> desensitizer)
51
+    {
52
+        this.desensitizer = desensitizer;
53
+    }
54
+
55
+    public Function<String, String> desensitizer()
56
+    {
57
+        return desensitizer;
58
+    }
59
+}

+ 51 - 0
ruoyi-common/ruoyi-common-sensitive/src/main/java/com/ruoyi/common/sensitive/utils/DesensitizedUtil.java

@@ -0,0 +1,51 @@
1
+package com.ruoyi.common.sensitive.utils;
2
+
3
+import com.ruoyi.common.core.utils.StringUtils;
4
+
5
+/**
6
+ * 脱敏工具类
7
+ *
8
+ * @author ruoyi
9
+ */
10
+public class DesensitizedUtil
11
+{
12
+    /**
13
+     * 密码的全部字符都用*代替,比如:******
14
+     *
15
+     * @param password 密码
16
+     * @return 脱敏后的密码
17
+     */
18
+    public static String password(String password)
19
+    {
20
+        if (StringUtils.isBlank(password))
21
+        {
22
+            return StringUtils.EMPTY;
23
+        }
24
+        return StringUtils.repeat('*', password.length());
25
+    }
26
+
27
+    /**
28
+     * 车牌中间用*代替,如果是错误的车牌,不处理
29
+     *
30
+     * @param carLicense 完整的车牌号
31
+     * @return 脱敏后的车牌
32
+     */
33
+    public static String carLicense(String carLicense)
34
+    {
35
+        if (StringUtils.isBlank(carLicense))
36
+        {
37
+            return StringUtils.EMPTY;
38
+        }
39
+        // 普通车牌
40
+        if (carLicense.length() == 7)
41
+        {
42
+            carLicense = StringUtils.hide(carLicense, 3, 6);
43
+        }
44
+        else if (carLicense.length() == 8)
45
+        {
46
+            // 新能源车牌
47
+            carLicense = StringUtils.hide(carLicense, 3, 7);
48
+        }
49
+        return carLicense;
50
+    }
51
+}