利用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数据脱敏的资料请关注脚本之家其它相关文章!