Data desensitization? Look at my line of comments to get it done!

This article mainly shares what data desensitization is, and how to elegantly use an annotation in a project to achieve data desensitization and empower the project. Hope it can help you.

What is data desensitization

Data desensitization is a technology that protects data privacy and security by removing or replacing some information in sensitive data. Its main purpose is to ensure that data can still be used in various scenarios, while protecting sensitive information and preventing data leakage and misuse. Data desensitization is usually used to process data sets containing personally identifiable information and other sensitive information, such as mobile phone numbers, names, addresses, bank cards, ID numbers, license plate numbers, and so on.

In the process of data desensitization, different algorithms and technologies are usually used to process data according to different needs and scenarios. For example, for the ID number, you can use to 掩码算法(masking)keep the first few digits, and replace the other digits with "X" or "*"; for the name, you can use to 伪造(pseudonymization)算法replace the real name with a pseudonym generated randomly.

Next, I will talk about the data desensitization mask operation for everyone, let us learn together.

appetizer

The following introduces to you the use of two different tool classes for data desensitization, and our topic today uses one annotation to solve the main two tool classes for data desensitization problems. Come and learn from me.

Use the Hutool tool class to implement data masking

For example, we now want to desensitize the data of the mobile phone number, the first three and the last four are not masked, and all others are masked with *

As shown in the code below,

We have defined a mobile phone number: 17677772345, which requires data desensitization.

Called Hutool's information desensitization tool class.

insert image description here

Let's run it and see the result. A simple data desensitization is achieved.

insert image description here

Hutool information desensitization tools

According to the demo above, you can see that I used Hutool's information desensitization tools to desensitize the mobile phone number mask. So let's take a look at Hutool's information desensitization tools.

Official website documentation: https://hutool.cn/docs/#/core/tools/information desensitization tool-DesensitizedUtil

Take a look at the introduction on the official website. It supports a variety of desensitized data types to meet most of our needs. If you need to customize, it also provides a custom method to achieve.

picture

The following is the desensitization rule of the definition number in it. Simple data desensitization can be achieved by calling directly. The reason for introducing it here is because the core of data desensitization is to use the tools provided by our Hutool. Implementation, support for custom hiding.

picture

Data serialization desensitization using Jackson

First create an entity class, this entity class has only one mobile phone number for testing.

Explanation of annotations:

@Data: The annotations of lombok generate get, set and other methods.
@JsonSerialize(using =TestJacksonSerialize.class): The function of this annotation is to customize the serialization, which can be used on annotations, methods, fields, classes, runtime, etc., according to the provided serialization class Override method to implement custom serialization. You can take a look at the source code below, and friends who are interested can go to find out, and it can also solve many scenarios in our daily development.

picture

@Data
public class TestDTO implements Serializable {
    
    
    /**
     * 手机号
     */
    @JsonSerialize(using = TestJacksonSerialize.class)
    private String phone;
}

Then create a TestJacksonSerialize class to implement custom serialization.

This class mainly inherits JsonSerializer, because the type we need to serialize here is String generic, we choose String. Note that if you use this annotation on a class, this is the class you want to serialize.

Rewrite the serialization method, the implementation inside is very simple to call our Hutool tool class to desensitize the mobile phone number data.

public class TestJacksonSerialize extends JsonSerializer<String> {
    
    

    @Override
    @SneakyThrows
    public void serialize(String str, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) {
    
    
        // 使用我们的hutool工具类进行手机号脱敏
        jsonGenerator.writeString(DesensitizedUtil.fixedPhone(String.valueOf(str)));
    }
}

Let's test it, because this annotation is valid at runtime, we define an interface to test.

@RestController
@RequestMapping("/test")
public class TestApi {
    
    

    @GetMapping
    public TestDTO test(){
    
    
        TestDTO testDTO = new TestDTO();
        testDTO.setPhone("17677772345");
        return testDTO;
    }
}

picture

You can see that the test is successful. After the introduction of the above two tool classes, think about how we can define our own annotations through the two tool classes to achieve data desensitization.

Annotation realizes data desensitization

Let's think about it. Now that there are tool classes, how can we implement an annotation to solve data desensitization elegantly?

Please read the following, let me take you to learn together.

1. Define an annotation

Define a Desensitization annotation.

@Retention(RetentionPolicy.RUNTIME):运行时生效。
@Target(ElementType.FIELD):可用在字段上。
@JacksonAnnotationsInside:此注解可以点进去看一下是一个元注解,主要是用户打包其他注解一起使用。
@JsonSerialize:上面说到过,该注解的作用就是可自定义序列化,可以用在注解上,方法上,字段上,类上,运行时生效等等,根据提供的序列化类里面的重写方法实现自定义序列化。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@JacksonAnnotationsInside
@JsonSerialize(using = DesensitizationSerialize.class)
public @interface Desensitization {
    
    
    /**
     * 脱敏数据类型,只要在CUSTOMER的时候,startInclude和endExclude生效
     */
    DesensitizationTypeEnum type() default DesensitizationTypeEnum.CUSTOMER;

    /**
     * 开始位置(包含)
     */
    int startInclude() default 0;

    /**
     * 结束位置(不包含)
     */
    int endExclude() default 0;
}

You can see that this annotation has three values, one is that the enumeration class defines our desensitized data type. A start position and an end position.

The enumeration class will be explained to you later. If you choose a custom type, the following start position and end position will take effect.

The start and end positions are parameters required by the custom masking implementation provided by our Hutool tool. You can see this method. It needs to be pointed out that this method hard-codes the mask value. If our scene needs other mask values, the implementation is also very simple. Copy out the source code of Hutool and replace its hard coding, and it can be realized.

picture

2. Create an enumeration class

This enumeration class is the type of our data desensitization, including most scenarios. And it can meet our daily development.

public enum DesensitizationTypeEnum {
    
    
    //自定义
    CUSTOMER,
    //用户id
    USER_ID,
    //中文名
    CHINESE_NAME,
    //身份证号
    ID_CARD,
    //座机号
    FIXED_PHONE,
    //手机号
    MOBILE_PHONE,
    //地址
    ADDRESS,
    //电子邮件
    EMAIL,
    //密码
    PASSWORD,
    //中国大陆车牌,包含普通车辆、新能源车辆
    CAR_LICENSE,
    //银行卡
    BANK_CARD
}

3. Create our custom serialization class

This class is the key to our data desensitization. It mainly inherits our JsonSerializer and implements my ContextualSerializer. The methods of both of them are rewritten.

@NoArgsConstructor:Lombok 无参构造生成。
@AllArgsConstructor:Lombok 有参生成。
ContextualSerializer:这个类是序列化上下文类,主要是解决我们这个地方获取字段的一些信息,可以看一下源码,他的实现类有很多,Jackson 提供的 @JsonFormat 注解也是实现此类,获取字段的一些信息进行序列化的。有兴趣的朋友可以看一下,多看源码,才能学到 Jackson 的实现方法,才能有今天我们的实现。

Interpretation of the two rewritten methods:

serialize: Override to implement our serialization customization.
createContextual: rewrite the serialization context method, get some information from our fields to judge, and then return the instance. The specific code can be seen in the following code, there are comments.

@NoArgsConstructor
@AllArgsConstructor
public class DesensitizationSerialize extends JsonSerializer<String> implements ContextualSerializer {
    
    
    private DesensitizationTypeEnum type;

    private Integer startInclude;

    private Integer endExclude;
    @Override
    public void serialize(String str, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
    
    
        switch (type) {
    
    
            // 自定义类型脱敏
            case CUSTOMER:
                jsonGenerator.writeString(CharSequenceUtil.hide(str,startInclude,endExclude));
                break;
            // userId脱敏
            case USER_ID:
                jsonGenerator.writeString(String.valueOf(DesensitizedUtil.userId()));
                break;
            // 中文姓名脱敏
            case CHINESE_NAME:
                jsonGenerator.writeString(DesensitizedUtil.chineseName(String.valueOf(str)));
                break;
            // 身份证脱敏
            case ID_CARD:
                jsonGenerator.writeString(DesensitizedUtil.idCardNum(String.valueOf(str), 1, 2));
                break;
            // 固定电话脱敏
            case FIXED_PHONE:
                jsonGenerator.writeString(DesensitizedUtil.fixedPhone(String.valueOf(str)));
                break;
            // 手机号脱敏
            case MOBILE_PHONE:
                jsonGenerator.writeString(DesensitizedUtil.mobilePhone(String.valueOf(str)));
                break;
            // 地址脱敏
            case ADDRESS:
                jsonGenerator.writeString(DesensitizedUtil.address(String.valueOf(str), 8));
                break;
            // 邮箱脱敏
            case EMAIL:
                jsonGenerator.writeString(DesensitizedUtil.email(String.valueOf(str)));
                break;
            // 密码脱敏
            case PASSWORD:
                jsonGenerator.writeString(DesensitizedUtil.password(String.valueOf(str)));
                break;
            // 中国车牌脱敏
            case CAR_LICENSE:
                jsonGenerator.writeString(DesensitizedUtil.carLicense(String.valueOf(str)));
                break;
            // 银行卡脱敏
            case BANK_CARD:
                jsonGenerator.writeString(DesensitizedUtil.bankCard(String.valueOf(str)));
                break;
            default:
        }

    }

    @Override
    public JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) throws JsonMappingException {
    
    
        if (beanProperty != null) {
    
    
            // 判断数据类型是否为String类型
            if (Objects.equals(beanProperty.getType().getRawClass(), String.class)) {
    
    
                // 获取定义的注解
                Desensitization desensitization = beanProperty.getAnnotation(Desensitization.class);
                // 为null
                if (desensitization == null) {
    
    
                    desensitization = beanProperty.getContextAnnotation(Desensitization.class);
                }
                // 不为null
                if (desensitization != null) {
    
    
                    // 创建定义的序列化类的实例并且返回,入参为注解定义的type,开始位置,结束位置。
                    return new DesensitizationSerialize(desensitization.type(), desensitization.startInclude(),
                            desensitization.endExclude());
                }
            }

            return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty);
        }
        return serializerProvider.findNullValueSerializer(null);
    }
}

4. Test

Create a test annotated DTO, this test is as follows.

@Data
public class TestAnnotationDTO implements Serializable {
    
    
    /**
     * 自定义
     */
    @Desensitization(type = DesensitizationTypeEnum.CUSTOMER,startInclude = 5,endExclude = 10)
    private String custom;
    /**
     * 手机号
     */
    @Desensitization(type = DesensitizationTypeEnum.MOBILE_PHONE)
    private String phone;
    /**
     * 邮箱
     */
    @Desensitization(type = DesensitizationTypeEnum.EMAIL)
    private String email;
    /**
     * 身份证
     */
    @Desensitization(type = DesensitizationTypeEnum.ID_CARD)
    private String idCard;
}

New test interface:

@GetMapping("/test-annotation")
public TestAnnotationDTO testAnnotation(){
    
    
    TestAnnotationDTO testAnnotationDTO = new TestAnnotationDTO();
    testAnnotationDTO.setPhone("17677772345");
    testAnnotationDTO.setCustom("111111111111111111");
    testAnnotationDTO.setEmail("[email protected]");
    testAnnotationDTO.setIdCard("4444199810015555");
    return testAnnotationDTO;
}

Test it to see the effect. As shown in the picture below, perfect!

picture

Project pom file

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.10</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.jiaqing</groupId>
    <artifactId>tool-desensitization</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>tool-desensitization</name>
    <description>数据脱敏</description>
    <properties>
        <java.version>1.8</java.version>
        <hutool.version>5.8.5</hutool.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-core</artifactId>
            <version>${hutool.version}</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--json模块-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-json</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

postscript

What I bring to you today is how to implement an annotation for data desensitization.

How to use the hutool tool class for data desensitization.

How to implement custom serialization using @JsonSerialize annotation.

How to use hutool + Jackson to implement your own desensitization annotations.

Guess you like

Origin blog.csdn.net/weixin_39570655/article/details/131823786