Spring Boot 3.3 整合 Jackson 轻松搞定接口数据脱敏
在现代 Web 开发中,数据安全与隐私保护是关键。特别是在涉及用户敏感信息的接口中,数据脱敏是保障数据安全的重要措施。通过对返回客户端的数据进行脱敏处理,可以有效保护用户的隐私。本文将详细介绍如何在 Spring Boot 3.3 项目中整合 Jackson 和 MyBatis-Plus,实现接口数据的自动脱敏处理,并提供完整的代码示例,包括项目配置、数据库初始化、后端实现、以及前端与后端的交互示例。
运行效果:
数据表实际数据:
若想获取项目完整代码以及其他文章的项目源码,且在代码编写时遇到问题需要咨询交流,欢迎加入下方的知识星球。
1. 数据库表结构及数据初始化
用户表 SQL DDL 语句
首先,创建一个 user
表,用于存储用户信息,并包含身份证号字段。以下是 SQL DDL 语句:
CREATE TABLE user (
id BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
name VARCHAR(50) NOT NULL COMMENT '姓名',
phone VARCHAR(20) NOT NULL COMMENT '手机号',
email VARCHAR(100) NOT NULL COMMENT '邮箱',
id_card VARCHAR(18) NOT NULL COMMENT '身份证号',
PRIMARY KEY (id)
) COMMENT='用户表';
初始化数据
接下来,初始化 10 条用户数据:
INSERT INTO user (name, phone, email, id_card) VALUES
('张三', '13812345678', 'zhangsan@example.com', '110101199003071234'),
('李四', '13912345678', 'lisi@example.com', '110101198003071235'),
('王五', '13712345678', 'wangwu@example.com', '110101197003071236'),
('赵六', '13612345678', 'zhaoliu@example.com', '110101196003071237'),
('孙七', '13512345678', 'sunqi@example.com', '110101195003071238'),
('周八', '13412345678', 'zhouba@example.com', '110101194003071239'),
('吴九', '13312345678', 'wujio@example.com', '110101193003071240'),
('郑十', '13212345678', 'zhengshi@example.com', '110101192003071241'),
('钱十一', '13112345678', 'qianshiyi@example.com', '110101191003071242'),
('冯十二', '13012345678', 'fengshier@example.com', '110101190003071243');
2. 项目配置
Maven 依赖(pom.xml)
在 pom.xml
文件中引入 MyBatis-Plus 及其他相关依赖:
<?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>3.3.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.icoderoad</groupId>
<artifactId>sensitive</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>sensitive</name>
<description>sensitive Demo project for Spring Boot</description>
<properties>
<java.version>17</java.version>
<mybatis-plus-boot-starter.version>3.5.7</mybatis-plus-boot-starter.version>
</properties>
<dependencies>
<!-- Spring Boot Web 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Jackson 依赖 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<!-- MyBatis-Plus 依赖 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus-boot-starter.version}</version>
</dependency>
<!-- MySQL 驱动 -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Thymeleaf 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</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>
</plugin>
</plugins>
</build>
</project>
配置文件(application.yml)
application.yml
配置文件中,配置数据库连接及其他基础配置:
server:
port: 8080
spring:
datasource:
url: jdbc:mysql://localhost:3306/sensitive?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
jackson:
property-naming-strategy: SNAKE_CASE
date-format: yyyy-MM-dd HH:mm:ss
mybatis-plus:
mapper-locations: classpath:/mapper/*.xml
3. 实现数据脱敏处理
自定义注解
首先,我们定义一个自定义注解 @Sensitive
来标记需要脱敏的字段。
package com.icoderoad.sensitive.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Sensitive {
// 定义脱敏策略,可以根据需要扩展
SensitiveStrategy strategy() default SensitiveStrategy.DEFAULT;
}
SensitiveStrategy
枚举类定义了脱敏策略:
package com.icoderoad.sensitive.annotation;
public enum SensitiveStrategy {
DEFAULT(value -> value),
PHONE(value -> value.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2")),
EMAIL(value -> value.replaceAll("(\\w?)(\\w+)(\\w)(@\\w+)", "$1****$3$4")),
ID_CARD(value -> value.replaceAll("(\\d{4})\\d{10}(\\w{4})", "$1****$2"));
private final SensitiveOperation operation;
SensitiveStrategy(SensitiveStrategy.SensitiveOperation operation) {
this.operation = operation;
}
public String apply(String value) {
return operation.apply(value);
}
private interface SensitiveOperation {
String apply(String value);
}
}
实现自定义序列化器
接下来,我们实现一个自定义的 Jackson 序列化器,用于处理标记了 @Sensitive
注解的字段。
package com.icoderoad.sensitive.serializer;
import java.io.IOException;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.icoderoad.sensitive.annotation.SensitiveStrategy;
public class SensitiveSerializer<T> extends JsonSerializer<T> {
private final SensitiveStrategy strategy;
public SensitiveSerializer(SensitiveStrategy strategy) {
this.strategy = strategy;
}
@Override
public void serialize(T value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
if (value instanceof String) {
String stringValue = (String) value;
gen.writeString(strategy.apply(stringValue));
} else {
gen.writeObject(value); // 非 String 类型的直接输出
}
}
}
SensitiveBeanSerializerModifier 类
package com.icoderoad.sensitive.config;
import java.util.List;
import org.springframework.stereotype.Component;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
import com.icoderoad.sensitive.annotation.Sensitive;
import com.icoderoad.sensitive.serializer.SensitiveSerializer;
@Component
public class SensitiveBeanSerializerModifier extends BeanSerializerModifier {
@Override
public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties) {
for (BeanPropertyWriter writer : beanProperties) {
Sensitive sensitive = writer.getAnnotation(Sensitive.class);
if (sensitive != null) {
JsonSerializer<Object> serializer = new SensitiveSerializer<>(sensitive.strategy());
writer.assignSerializer(serializer);
}
}
return beanProperties;
}
}
配置 Jackson 模块
我们需要将自定义的序列化器注册到 Jackson 中,才能实现自动数据脱敏处理。可以通过配置类实现:
package com.icoderoad.sensitive.config;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class JacksonConfig {
@Bean
public ObjectMapper objectMapper(SensitiveBeanSerializerModifier sensitiveBeanSerializerModifier) {
ObjectMapper objectMapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.setSerializerModifier(sensitiveBeanSerializerModifier);
objectMapper.registerModule(module);
return objectMapper;
}
}
Mybatis_plus 配置类
package com.icoderoad.sensitive.config;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
@Configuration
@MapperScan({"com.icoderoad.sensitive.mapper"})
public class MybatisPlusConfig {
/**
* 分页插件,自动识别数据库类型
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
MyBatis-Plus 实体类
首先,定义 User
实体类并使用 @Sensitive
注解标记需要脱敏的字段。
package com.icoderoad.sensitive.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import com.example.annotation.Sensitive;
import com.example.annotation.SensitiveStrategy;
@TableName("user")
public class User {
private Long id;
private String name;
@Sensitive(strategy = SensitiveStrategy.PHONE)
private String phone;
@Sensitive(strategy = SensitiveStrategy.EMAIL)
private String email;
@Sensitive(strategy = SensitiveStrategy.ID_CARD)
private String idCard;
// 省略 getter 和 setter 方法
}
Mapper 接口
定义 UserMapper
接口,继承 MyBatis-Plus 提供的 BaseMapper
。
package com.icoderoad.sensitive.mapper;
import org.apache.ibatis.annotations.Mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.icoderoad.sensitive.entity.User;
@Mapper
public interface UserMapper extends BaseMapper<User> {
}
Service 接口
定义 UserService
接口,继承 MyBatis-Plus 提供的 IService
。
package com.icoderoad.sensitive.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.icoderoad.sensitive.entity.User;
public interface UserService extends IService<User> {
}
Service 实现类
实现 UserServiceImpl
类,继承 MyBatis-Plus 提供的 ServiceImpl
。
package com.icoderoad.sensitive.service.impl;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.icoderoad.sensitive.entity.User;
import com.icoderoad.sensitive.mapper.UserMapper;
import com.icoderoad.sensitive.service.UserService;
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}
4. 控制器
将之前的控制器修改为调用 UserService
获取用户列表数据。
package com.icoderoad.sensitive.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.icoderoad.sensitive.entity.User;
import com.icoderoad.sensitive.service.UserService;
@RestController
@RequestMapping("/api/user")
public class UserRestController {
@Autowired
private UserService userService;
@GetMapping("/list")
public List<User> getUserList() {
return userService.list();
}
}
视图控制类
package com.icoderoad.sensitive.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class UserController {
@GetMapping("/")
public String index(Model model) {
return "index";
}
}
5. 前端页面展示
使用 Thymeleaf 模板引擎结合 Bootstrap 来构建一个简单的前端页面,展示接口返回的用户列表信息。
templates/index.html
:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>用户信息列表</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<style>
thead th {
background-color: #007bff;
color: white;
}
tbody tr:nth-child(even) {
background-color: #f8f9fa;
}
</style>
</head>
<body>
<div class="container mt-5">
<h1>用户信息列表</h1>
<table class="table table-bordered">
<thead>
<tr>
<th>姓名</th>
<th>手机号</th>
<th>邮箱</th>
<th>身份证号</th>
</tr>
</thead>
<tbody id="userTable">
</tbody>
</table>
</div>
<script>
$(document).ready(function () {
$.getJSON("/api/user/list", function (data) {
data.forEach(function (user) {
var row = "<tr><td>" + user.name + "</td><td>" + user.phone + "</td><td>" + user.email + "</td><td>" + user.idCard + "</td></tr>";
$("#userTable").append(row);
});
});
});
</script>
</body>
</html>
6. 总结
通过在 Spring Boot 3.3 中整合 Jackson 和 MyBatis-Plus,并实现自定义序列化器,我们可以轻松实现接口数据的自动脱敏处理。这种方式不仅可以大幅提升数据的安全性,还能减少手动脱敏的工作量。结合身份证号的脱敏处理,可以应对更加严格的隐私保护需求。在实际项目中,根据具体业务需求调整脱敏策略和实现方式,可以灵活应对各种场景的需求。