Jackson 库简介--以及数据脱敏

Jackson 是一个流行的 Java JSON 处理库,它提供了将 Java 对象与 JSON 数据相互转换的功能。Jackson 的主要功能包括:

  1. 序列化将 Java 对象转换为 JSON 字符串。
  2. 反序列化将 JSON 字符串转换为 Java 对象。

Jackson 提供了以下几个核心组件:

  • jackson-core:核心库,提供 JSON 的解析和生成。
  • jackson-databind:数据绑定库,负责将 JSON 数据绑定到 Java 对象。
  • jackson-annotations:包含用于 JSON 处理的注解,如 @JsonProperty@JsonIgnore 等。

基本使用

在使用 Jackson 之前,你需要在 pom.xml 中添加 Jackson 的依赖:

<dependencies>
    <!-- Jackson 核心库 -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-core</artifactId>
        <version>2.13.3</version>
    </dependency>

    <!-- Jackson 数据绑定库 -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.13.3</version>
    </dependency>

    <!-- Jackson 注解库 -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-annotations</artifactId>
        <version>2.13.3</version>
    </dependency>
</dependencies>

序列化示例

import com.fasterxml.jackson.databind.ObjectMapper;

public class Example {
    public static void main(String[] args) throws Exception {
        ObjectMapper objectMapper = new ObjectMapper();
        
        // 创建一个示例对象
        User user = new User();
        user.setName("John");
        user.setAge(30);

        // 将对象转换为 JSON 字符串
        String json = objectMapper.writeValueAsString(user);
        System.out.println(json); // 输出: {"name":"John","age":30}
    }
}

class User {
    private String name;
    private int age;

    // Getter 和 Setter
}

反序列化示例

import com.fasterxml.jackson.databind.ObjectMapper;

public class Example {
    public static void main(String[] args) throws Exception {
        ObjectMapper objectMapper = new ObjectMapper();
        
        // JSON 字符串
        String json = "{\"name\":\"John\",\"age\":30}";

        // 将 JSON 字符串转换为 Java 对象
        User user = objectMapper.readValue(json, User.class);
        System.out.println(user.getName()); // 输出: John
    }
}

class User {
    private String name;
    private int age;

    // Getter 和 Setter
}

实现自定义注解

如果你需要自定义序列化或反序列化逻辑,你可以创建自定义注解和序列化器。以下是详细步骤:

1. 定义自定义注解

创建一个自定义注解,用于标记需要特殊处理的字段。例如,我们可以创建一个 @Sensitive 注解,用于标记需要脱敏处理的字段。

import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@JsonSerialize(using = SensitiveJsonSerializer.class) // 指定自定义序列化器
public @interface Sensitive {
    SensitiveStrategy strategy(); // 指定脱敏策略
}

理解 SensitiveJsonSerializer 类的代码时,可以将其分为几个部分来讲解。这个类实现了 JsonSerializer,用于自定义序列化过程,特别是处理带有 @Sensitive 注解的字段。下面详细解释代码的每个部分:

1. JsonSerializer<String>

SensitiveJsonSerializer 类继承自 Jackson 的 JsonSerializer<String>。这是一个泛型类,用于自定义如何将 String 类型的对象序列化为 JSON。

2. serialize 方法

serialize 方法是 JsonSerializer 类的抽象方法,必须实现。它的作用是定义如何将 Java 对象(在这个例子中是 String)转换为 JSON 字符串。

 

2. 定义脱敏策略

创建一个枚举 SensitiveStrategy,定义不同的脱敏策略。

import java.util.function.Function;

public enum SensitiveStrategy {
    USERNAME(s -> s.replaceAll("(\\S)\\S(\\S*)", "$1*$2")),
    ID_CARD(s -> s.replaceAll("(\\d{4})\\d{10}(\\w{4})", "$1****$2")),
    PHONE(s -> s.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2")),
    ADDRESS(s -> s.replaceAll("(\\S{3})\\S{2}(\\S*)\\S{2}", "$1****$2****"));

    private final Function<String, String> desensitizer;

    SensitiveStrategy(Function<String, String> desensitizer) {
        this.desensitizer = desensitizer;
    }

    public Function<String, String> desensitizer() {
        return desensitizer;
    }
}

理解 strategy.desensitizer().apply(value) 是如何工作的,我们需要详细了解 Function<String, String> 这个接口,以及 SensitiveStrategy 枚举类是如何定义和使用这个接口的。

Function 接口

Function 是 Java 8 引入的一个函数式接口,它位于 java.util.function 包中。这个接口定义了一个方法 apply,用于将一个输入值转换成一个输出值:

@FunctionalInterface
public interface Function<T, R> {
    R apply(T t);
}
  • T 是输入类型
  • R 是输出类型
  • apply 方法接受一个 T 类型的参数,并返回一个 R 类型的结果
SensitiveStrategy 枚举

SensitiveStrategy 枚举中,每个枚举实例都被赋予了一个 Function<String, String> 类型的对象。这个对象用于定义字符串脱敏的具体实现。

public enum SensitiveStrategy {
    USERNAME(s -> s.replaceAll("(\\S)\\S(\\S*)", "$1*$2")),
    ID_CARD(s -> s.replaceAll("(\\d{4})\\d{10}(\\w{4})", "$1****$2")),
    PHONE(s -> s.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2")),
    ADDRESS(s -> s.replaceAll("(\\S{3})\\S{2}(\\S*)\\S{2}", "$1****$2****"));

    private final Function<String, String> desensitizer;

    SensitiveStrategy(Function<String, String> desensitizer) {
        this.desensitizer = desensitizer;
    }

    public Function<String, String> desensitizer() {
        return desensitizer;
    }
}

每个枚举实例都使用一个 Function<String, String> 对象来定义如何进行字符串替换:

  • USERNAME 实例的 Function 对象:s -> s.replaceAll("(\\S)\\S(\\S*)", "$1*$2")
    • 这个函数会将用户名的第二个字符替换为 *
  • ID_CARD 实例的 Function 对象:s -> s.replaceAll("(\\d{4})\\d{10}(\\w{4})", "$1****$2")
    • 这个函数会将身份证号码的中间部分替换为 ****
  • 其他实例类似。

这些 Function 对象都实现了 apply 方法,用于对输入的字符串进行处理。

调用 desensitizer.apply(value)

当你调用 strategy.desensitizer().apply(value) 时,实际上执行了以下步骤:

  1. strategy.desensitizer() 返回 Function<String, String> 类型的 desensitizer 对象。
  2. desensitizer.apply(value) 调用 Function 对象的 apply 方法,对输入的 value 进行处理并返回处理后的结果。
 总结
  • 在序列化过程中,调用 strategy.desensitizer().apply(value) 对字符串进行脱敏处理。
  • 将处理后的字符串写入 JsonGenerator 以生成最终的 JSON 输出。
3. 实现自定义序列化器

创建一个自定义的 JsonSerializer 类,用于处理带有 @Sensitive 注解的字段。

public class SensitiveJsonSerializer extends JsonSerializer<String> implements ContextualSerializer {
    private SensitiveStrategy strategy;

    @Override
    public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        gen.writeString(strategy.desensitizer().apply(value));
    }

    /**
     * 获取属性上的注解属性
     */
    @Override
    public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {
        Sensitive annotation = property.getAnnotation(Sensitive.class);
        System.out.println(annotation);
        if (Objects.nonNull(annotation)&& Objects.equals(String.class, property.getType().getRawClass())) {
            this.strategy = annotation.strategy();
            return this;
        }
        return prov.findValueSerializer(property.getType(), property);
    }
}

这段代码是一个自定义的 Jackson 序列化器,它实现了 JsonSerializer<String>ContextualSerializer 接口,用于在序列化过程中对特定类型的字段进行脱敏处理。让我们逐行解释代码的含义和作用。

  • SensitiveJsonSerializer 继承了 JsonSerializer<String>,表示这是一个自定义的序列化器,专门用于序列化 String 类型的字段。
  • 它还实现了 ContextualSerializer 接口,这使得序列化器能够访问字段的上下文(如注解)并根据上下文进行调整。
  • strategy 是一个 SensitiveStrategy 类型的字段,用于存储从注解中提取的脱敏策略。
 serialize 方法
  • serialize 方法用于序列化对象。
  • value 是需要序列化的字段值。
  • genJsonGenerator,用于将序列化的内容写入输出。
  • serializersSerializerProvider,提供了其他序列化器。
  • 在方法内部,使用脱敏策略 (strategy.desensitizer()) 对字段值 (value) 进行脱敏处理,并将处理后的值写入输出 (gen.writeString)。
createContextual 方法
  • createContextual 方法用于在序列化过程中根据上下文(即字段的注解)配置序列化器。
  • provSerializerProvider,提供了其他序列化器。
  • propertyBeanProperty,表示当前正在序列化的字段。

方法内部,首先获取字段上的 Sensitive 注解(如果有) 

Sensitive annotation = property.getAnnotation(Sensitive.class);

然后,检查注解是否存在且字段类型是否为 String

if (Objects.nonNull(annotation) && Objects.equals(String.class, property.getType().getRawClass())) {
    this.strategy = annotation.strategy();
    return this;
}
    • 如果注解存在并且字段类型是 String,则从注解中提取脱敏策略 (annotation.strategy()) 并赋值给 this.strategy
    • 返回当前的序列化器 (return this;)。
  • 如果没有注解或字段类型不是 String,则使用默认的序列化器 (prov.findValueSerializer(property.getType(), property))。
4. 使用自定义注解

在你的类中使用 @Sensitive 注解来标记需要脱敏处理的字段。

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {
    /**
     * 真实姓名
     */
    @Sensitive(strategy = SensitiveStrategy.USERNAME)
    private String realName;
    /**
     * 地址
     */
    @Sensitive(strategy = SensitiveStrategy.ADDRESS)
    private String address;
    /**
     * 电话号码
     */
    @Sensitive(strategy = SensitiveStrategy.PHONE)
    private String phoneNumber;
    /**
     * 身份证号码
     */
    @Sensitive(strategy = SensitiveStrategy.ID_CARD)
    private String idCard;


}
5.测试 
@RestController
public class TestController {
    @GetMapping("/test")
    public Person test(){
        Person user = new Person();
        user.setRealName("xxxx");
        user.setPhoneNumber(" 15242554546");
        user.setAddress("天津市河西区....");
        user.setIdCard("111111111111111");
        return user;
    }
}

相关推荐

  1. Jackson 简介--以及数据脱敏

    2024-07-20 23:28:03       25 阅读
  2. 快速了解数据脱敏

    2024-07-20 23:28:03       28 阅读
  3. Spring 数据脱敏实现方式

    2024-07-20 23:28:03       38 阅读
  4. 数据脱敏数据库安全风险

    2024-07-20 23:28:03       32 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-20 23:28:03       106 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-20 23:28:03       116 阅读
  3. 在Django里面运行非项目文件

    2024-07-20 23:28:03       95 阅读
  4. Python语言-面向对象

    2024-07-20 23:28:03       103 阅读

热门阅读

  1. cdh社区版免费替代方案。

    2024-07-20 23:28:03       29 阅读
  2. HJ99 自守数

    2024-07-20 23:28:03       31 阅读
  3. vue-print-nb 前端打印的一个实现方案

    2024-07-20 23:28:03       29 阅读
  4. 【Linux的线程篇章 - 线程基础知识储备】

    2024-07-20 23:28:03       24 阅读
  5. 解决网络游戏频繁掉线的策略与实践

    2024-07-20 23:28:03       25 阅读
  6. Qt项目:基于Qt实现的网络聊天室---好友申请

    2024-07-20 23:28:03       23 阅读
  7. 微软全球大蓝屏:必须手工修复

    2024-07-20 23:28:03       32 阅读
  8. 25、气象填色图绘制

    2024-07-20 23:28:03       24 阅读
  9. 【Flutter】 webview_flutter避坑

    2024-07-20 23:28:03       29 阅读
  10. C++的模板(十二):forward模板

    2024-07-20 23:28:03       27 阅读
  11. Kotlin协程最佳实践

    2024-07-20 23:28:03       23 阅读
  12. SQL Server的魔法工坊:打造数据库的自定义函数

    2024-07-20 23:28:03       29 阅读
  13. Qt判定鼠标是否在该多边形的线条上

    2024-07-20 23:28:03       26 阅读