51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

Spring Boot 3.3 整合 Jackson 轻松搞定接口数据脱敏

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,并实现自定义序列化器,我们可以轻松实现接口数据的自动脱敏处理。这种方式不仅可以大幅提升数据的安全性,还能减少手动脱敏的工作量。结合身份证号的脱敏处理,可以应对更加严格的隐私保护需求。在实际项目中,根据具体业务需求调整脱敏策略和实现方式,可以灵活应对各种场景的需求。

赞(5)
未经允许不得转载:工具盒子 » Spring Boot 3.3 整合 Jackson 轻松搞定接口数据脱敏