利用Jackson实现数据脱敏的示例详解

 更新时间:2023年05月26日 10:46:33   作者:饕餮者也  
在我们的企业项目中,为了保护用户隐私,数据脱敏成了必不可少的操作,那么我们怎么优雅的利用Jackson实现数据脱敏呢,本文就来和大家详细聊聊,希望对大家有所帮助

在我们的企业项目中,为了保护用户隐私,数据脱敏成了必不可少的操作,那么我们怎么优雅的去实现它呢?

一、简介

什么是序列化?

序列化就是把对象转化为可传输的字节序列过程,该字节序列包括对象的数据、对象的类型、数据的类型

在使用Jackson序列化进行数据脱敏时,就是在序列化过程中拿到对象的数据类型和对象的数据完成的操作。

二、实现

上边介绍完什么是序列化,那我们接下来就来做一个数据脱敏的小Demo吧。

1. 脱敏枚举

/**
 * @author hob
 * @date 2022/10/23 18:05
 * @description: <h1>脱敏枚举</h1>
 */
public enum DesensitizationEnum {
    /**
     * 真实姓名
     */
    REAL_NAME(val -> val.replaceAll("(.).*", "$1**")),
    /**
     * 身份证号码
     */
    ID_CARD(val -> val.replaceAll("(\d{4})\d{10}(\w{4})", "$1****$2")),
    /**
     * 住址
     */
    ADDRESS(val -> StringUtils.left(val, 3).concat(StringUtils.removeStart(StringUtils
            .leftPad(StringUtils.right(val, val.length()-11), StringUtils.length(val), "*"), "***"))),
    /**
     * 手机号
     */
    PHONE(val -> val.replaceAll("(\d{3})\d{4}(\d{4})", "$1****$2"));
    private final Function<String, String> function;
    DesensitizationEnum(Function<String, String> function) {
        this.function = function;
    }
    public Function<String, String> getFunction() {
        return function;
    }
}

在上边的脱敏枚举中,使用了Java 8 的函数式接口Function,该接口的作用就是接受一个输入参数,返回一个结果,例如Function<String, Integer>,就是将 “10” 转换为 10 的过程,也就是将字符串转为整数。使用Function接口的目的就是将原字符串,转换为脱敏后的新字符串,上边的操作可以理解为val值给了第一个Sting, lambada转换完成给了第二个String。是不是很方便呢.

2. 脱敏注解

/**
 * @author hob
 * @date 2022/10/23 18:09
 * @description: <h1>脱敏注解</h1>
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@JacksonAnnotationsInside
@JsonSerialize(using = DesensitizationSerialize.class)
public @interface Desensitization {
    /**
     * 脱敏类型 枚举
     * @return
     */
    DesensitizationEnum type();
}

@JacksonAnnotationsInside: 这个注解用来标记Jackson复合注解,当你使用多个Jackson注解组合成一个自定义注解时会用到它。

3. 脱敏

/**
 * @author hob
 * @date 2022/10/23 18:12
 * @description: <h1>脱敏序列化</h1>
 */
public class DesensitizationSerialize extends JsonSerializer<String> implements ContextualSerializer {
    private DesensitizationEnum type;
    /**
     * 序列化
     *
     * @param s
     * @param jsonGenerator
     * @param serializerProvider
     * @throws IOException
     */
    @Override
    public void serialize(String s, JsonGenerator jsonGenerator,
                          SerializerProvider serializerProvider) throws IOException {
        jsonGenerator.writeString(type.getFunction().apply(s));
    }
    /**
     * 在序列化时获取字段注解属性
     *
     * @param serializerProvider
     * @param beanProperty
     * @return
     * @throws JsonMappingException
     */
    @Override
    public JsonSerializer<?> createContextual(SerializerProvider serializerProvider,
                                              BeanProperty beanProperty) throws JsonMappingException {
        // 主要判断字符串,不是字符串的话就跳过
        if (Objects.nonNull(beanProperty) && Objects.equals(beanProperty.getType().getRawClass(), String.class)) {
            Desensitization desensitization = beanProperty.getAnnotation(Desensitization.class);
            if (!ObjectUtils.isEmpty(desensitization)) {
                // 如果属性上有Desensitization注解,就获取枚举类型
                this.type = desensitization.type();
                return this;
            }
            return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty);
        }
        return serializerProvider.findNullValueSerializer(beanProperty);
    }
}

在DesensitizationSerialize类中继承了JsonSerializer类重写了serialize方法,该方法的作用就是序列化,还实现了ContextualSerializer接口,重写了createContextual方法,该方法的作用就是在序列化时获取字段注解属性。先执行createContextual方法后执行serialize方法。

4.将要脱敏的字段标注注解

/**
 * 用户表信息
 * @TableName user
 */
@Data
@TableName(value ="user")
public class User implements Serializable {
    /**
     * 主键
     */
    @TableId(type = IdType.AUTO)
    private Long id;
    /**
     * 用户名
     */
    private String username;
    /**
     * 密码
     */
    private String password;
    /**
     * 身份证号
     */
    @Desensitization(type = DesensitizationEnum.ID_CARD)
    private String idCard;
    /**
     * 真实姓名
     */
    @Desensitization(type = DesensitizationEnum.REAL_NAME)
    private String realName;
    /**
     * 性别
     */
    private String gender;
    /**
     * 地址
     */
    @Desensitization(type = DesensitizationEnum.ADDRESS)
    private String address;
    /**
     * 手机号
     */
    @Desensitization(type = DesensitizationEnum.PHONE)
    private String phone;
    /**
     * 年龄
     */
    private String age;
    @TableField(exist = false)
    private static final long serialVersionUID = 1L;
}

5. controller测试类

/**
 * @author hob
 * @date 2022/10/23 18:03
 * @description: <h1>测试脱敏</h1>
 */
@RestController
public class UserController {
    @Autowired
    private UserService userService;
    /**
     * 获取用户列表
     *
     * @param userParams 查询参数
     * @return
     */
    @GetMapping("/list")
    public List<User> getUserList(UserParams userParams) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        if (StringUtils.isNotBlank(userParams.getAddress())) {
            queryWrapper.lambda().eq(User::getAddress, userParams.getAddress());
        }
        return userService.list(queryWrapper);
    }
}

结果如下

先看一下数据库中的数据

脱敏之前返回的数据

脱敏之后返回的数据

怎么样,这样的数据脱敏是不是很优雅呢?

依赖

<?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.5</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.hob99</groupId>
    <artifactId>springboot-jackson-desensitization</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot-jackson-desensitization</name>
    <description>springboot-jackson-desensitization</description>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <!-- web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- mysql -->
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
        </dependency>
        <!-- druid -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.2.13</version>
        </dependency>
        <!-- mybatis -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.2</version>
        </dependency>
        <!-- commons-lang3工具类 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.12.0</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>
    </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>
            <!--编译跳过测试文件检查的生命周期-->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <configuration>
                    <skip>true</skip>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

用到的依赖版本及插件

依赖:

  • springboot 2.7.5
  • mysql5.7
  • mybatis-plus3.5.2
  • commons-lang3工具包3.12.0

插件:

  • RestfulTool: 测试接口
  • lombok: 生成getter, setter
  • mybatisX: 快速生成代码

JDK版本: 17

以上就是利用Jackson实现数据脱敏的示例详解的详细内容,更多关于Jackson数据脱敏的资料请关注脚本之家其它相关文章!

相关文章

  • Java微信授权登陆的实现示例

    Java微信授权登陆的实现示例

    微信授权登录,官方文档写的比较简洁。所以对于会的人一目了然,而对于新手刚入门的人来说是举步维艰。本文详细的介绍了Java微信授权登陆的实现示例,感兴趣的朋友可以了解一下
    2021-06-06
  • Java实现线程的四种方式解析

    Java实现线程的四种方式解析

    这篇文章主要介绍了Java实现线程的四种方式解析,线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程,一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序,需要的朋友可以参考下
    2023-10-10
  • 6种Java创建对象的方式总结

    6种Java创建对象的方式总结

    在Java中,创建对象可以使用多种方式,本文将详细介绍以下六种创建对象的方式,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起了解一下
    2023-04-04
  • 浅谈MyBatis执行SQL的两种方式

    浅谈MyBatis执行SQL的两种方式

    本文介绍MyBatis执行SQL语句的2种方式,主要是SqlSession和Mapper接口以及它们的区别,具有一定的参考价值,感兴趣的可以了解一下
    2021-08-08
  • Jdk中没有jre文件夹怎么办?如何解决?

    Jdk中没有jre文件夹怎么办?如何解决?

    这篇文章主要介绍了Jdk中没有jre文件夹怎么办?如何解决的问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-02-02
  • IDEA中GitLab的使用详解

    IDEA中GitLab的使用详解

    这篇文章主要介绍了IDEA中GitLab的使用,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-07-07
  • Java图形用户界面设计(Swing)的介绍

    Java图形用户界面设计(Swing)的介绍

    看到多数人提到 Java 就以为是网络开发,其实不是这样的,Java 也可以开发应用程序,而且可以开发出漂亮的图形用户界面的应用程序,因此,我写下这篇文章,希望能带你进入 Java 图形用户界面设计之门。
    2016-07-07
  • java抓取网页数据示例

    java抓取网页数据示例

    要通java获取整个网页的html内容,或者某个网络文件的内容,可以使用java提供的HttpURLConnection类来实现对网页内容的抓取
    2014-03-03
  • SpringBoot 自动扫描第三方包及spring.factories失效的问题解决

    SpringBoot 自动扫描第三方包及spring.factories失效的问题解决

    这篇文章主要介绍了SpringBoot 自动扫描第三方包及spring.factories失效的问题,本文给大家分享最新解决方法,需要的朋友可以参考下
    2023-05-05
  • 重新启动IDEA时maven项目SSM框架文件变色所有@注解失效

    重新启动IDEA时maven项目SSM框架文件变色所有@注解失效

    这篇文章主要介绍了重新启动IDEA时maven项目SSM框架文件变色所有@注解失效,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-03-03

最新评论