SpringBoot中优雅的实现隐私数据脱敏(提供Gitee源码)
前言:在实际项目开发中,可能会对一些用户的隐私信息进行脱敏操作,传统的方式很多都是用replace方法进行手动替换,这样会由很多冗余的代码并且后续也不好维护,本期就讲解一下如何在SpringBoot中优雅的通过序列化的方式去实现数据的脱敏操作!
目录
一、导入pom依赖
二、DesensitizationEnum枚举类
三、Desensitization自定义注解
四、DesensitizationSerialize脱敏序列化器
五、User实体类
六、UserController请求层
七、运行测试
八、Gitee源码
九、总结
一、导入pom依赖
完整代码:
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.18</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies>
二、DesensitizationEnum枚举类
在DesensitizationSerialize序列化类中,会根据脱敏注解的type值,也就是DesensitizationEnum 中的类型,来判断需要使用哪种脱敏方式。
这边我就简单定义了5个枚举类型:
完整代码:
package com.example.desensitization.constant;public enum DesensitizationEnum {/*** 自定义*/CUSTOM_RULE,/*** 身份证号码*/ID_CARD_NO,/*** 电话号码*/PHONE,/*** 地址*/ADDRESS,/*** 银行卡号*/BANK_CARD_NO,
}
三、Desensitization自定义注解
这个是自定义的注解@Desensitization,用于标注需要进行脱敏的字段。
主要包含以下元注解和属性:
1、@Target(ElementType.FIELD):表示该注解只能用于字段上。
2、@Retention(RetentionPolicy.RUNTIME):表示该注解可以保留到运行时。
3、@JacksonAnnotationsInside:是一个Jackson的元注解,表示该注解可以作为Json序列化的注解。
4、@JsonSerialize:标注使用DesensitizationSerialize来进行序列化。
5、DesensitizationEnum type:需要脱敏的类型,对应枚举中的脱敏类型。
6、int start/end:可选的起始位置和结束位置,对于脱敏类型为字符串时有效。
完整代码:
package com.example.desensitization.annotation;import com.example.desensitization.constant.DesensitizationEnum;
import com.example.desensitization.serialize.DesensitizationSerialize;
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
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;@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonSerialize(using = DesensitizationSerialize.class)
public @interface Desensitization {DesensitizationEnum type();int start() default 0;int end() default 0;}
四、DesensitizationSerialize脱敏序列化器
1、继承JsonSerializer<String>JsonSerializer是Jackson的序列化器基类,实现了将对象序列化为JSON的核心方法,这里继承它是为了实现字符串的自定义序列化。
2、实现ContextualSerializer接口ContextualSerializer可以让序列化器基于上下文环境进行定制化,实现这个接口后,可以实现createContextual()方法。
关键代码:
@AllArgsConstructor
@NoArgsConstructor
public class DesensitizationSerialize extends JsonSerializer<String> implements ContextualSerializer {private DesensitizationEnum type;private Integer start;private Integer end;
}
自定义序列化器方式可以实现非侵入式的灵活脱敏,对业务代码零侵入,且不依赖Spring等框架,更适合编写独立的应用服务。当然,AOP实现也有其适用场景,可以作为另一种可选方案。
createContextual()方法:
1、Controller的user()方法被调用,构建并返回了一个User对象。
2、开始对User对象进行JSON序列化,会先调用我们定义的DesensitizationSerialize中createContextual()方法,如果这个实体类被createContextual()方法处理过,则以后不会再走该方法,直接走serialize()方法。
3、DesensitizationSerialize会检查当前类字段是否有@Desensitization注解,如果有N个,则根据注解的type、start和end参数创建N个DesensitizationSerialize实例。
关键代码:
@Overridepublic JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty)throws JsonMappingException {if (beanProperty != null) {// 获取当前正在处理的字段的类型,判断如果是 String 类型则进行后续脱敏逻辑处理if (Objects.equals(beanProperty.getType().getRawClass(), String.class)) {// 通过 beanProperty 获取在字段上标注的 @Desensitization 注解Desensitization desensitization = beanProperty.getAnnotation(Desensitization.class);// 如果没有就尝试获取类注解if (desensitization == null) {desensitization = beanProperty.getContextAnnotation(Desensitization.class);}// 不为nullif (desensitization != null) {// 如果获取到了注解,则根据注解的 type、start 和 end 参数创建 DesensitizationSerialize 实例,这是脱敏处理的序列化器return new DesensitizationSerialize(desensitization.type(), desensitization.start(),desensitization.end());}}// 直接返回默认的序列化器return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty);}// 直接返回默认的序列化器return serializerProvider.findNullValueSerializer(null);}
serialize方法:
JsonGenerator是Jackjson提供的JSON生成器类。在自定义序列化器的serialize()方法中,会传入JsonGenerator实例。serialize()方法需要通过JsonGenerator将脱敏后的字符串写入到结果JSON中。
CharSequenceUtil和DesensitizedUtil都是hutool提供的工具类。
整体流程是:
1、根据注解的参数动态选择脱敏策略。
2、调用对应 Hutool 的脱敏函数处理字符串。
3、将脱敏结果写入JSON。
关键代码:
@Overridepublic void serialize(String s, JsonGenerator jsonGenerator, SerializerProvider serializerProvider)throws IOException {switch (type){//自定义case CUSTOM_RULE:jsonGenerator.writeString(CharSequenceUtil.hide(s, start, end));break;//身份证case ID_CARD_NO:jsonGenerator.writeString(DesensitizedUtil.idCardNum(s, 2, 6));break;//手机号case PHONE:jsonGenerator.writeString(DesensitizedUtil.mobilePhone(s));break;//地址case ADDRESS:jsonGenerator.writeString(DesensitizedUtil.address(s, 2));break;// 银行卡脱敏case BANK_CARD_NO:jsonGenerator.writeString(DesensitizedUtil.bankCard(s));break;default:}}
综上,整体的执行逻辑如下:
1、Controller返回一个实体对象。
2、如果实体对象是第一次进行脱敏,则会调用createContextual()方法。
3、获取当前实体对象所有使用Desensitization注解的字符串字段,创建对应的DesensitizationSerialize实例,实现脱敏处理的序列化器。
4、执行serialize()方法中switch的处理逻辑,由JsonGenerator将脱敏后的字符串写入到结果JSON中。
5、返回Json数据。
完整代码:
package com.example.desensitization.serialize;import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.DesensitizedUtil;
import com.example.desensitization.annotation.Desensitization;
import com.example.desensitization.constant.DesensitizationEnum;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import java.io.IOException;
import java.util.Objects;@AllArgsConstructor
@NoArgsConstructor
public class DesensitizationSerialize extends JsonSerializer<String> implements ContextualSerializer {private DesensitizationEnum type;private Integer start;private Integer end;@Overridepublic void serialize(String s, JsonGenerator jsonGenerator, SerializerProvider serializerProvider)throws IOException {switch (type){//自定义case CUSTOM_RULE:jsonGenerator.writeString(CharSequenceUtil.hide(s, start, end));break;//身份证case ID_CARD_NO:jsonGenerator.writeString(DesensitizedUtil.idCardNum(s, 2, 6));break;//手机号case PHONE:jsonGenerator.writeString(DesensitizedUtil.mobilePhone(s));break;//地址case ADDRESS:jsonGenerator.writeString(DesensitizedUtil.address(s, 2));break;// 银行卡脱敏case BANK_CARD_NO:jsonGenerator.writeString(DesensitizedUtil.bankCard(s));break;default:}}@Overridepublic JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty)throws JsonMappingException {if (beanProperty != null) {// 获取当前正在处理的字段的类型,判断如果是 String 类型则进行后续脱敏逻辑处理if (Objects.equals(beanProperty.getType().getRawClass(), String.class)) {// 通过 beanProperty 获取在字段上标注的 @Desensitization 注解Desensitization desensitization = beanProperty.getAnnotation(Desensitization.class);// 如果没有就尝试获取类注解if (desensitization == null) {desensitization = beanProperty.getContextAnnotation(Desensitization.class);}// 不为nullif (desensitization != null) {// 如果获取到了注解,则根据注解的 type、start 和 end 参数创建 DesensitizationSerialize 实例,这是脱敏处理的序列化器return new DesensitizationSerialize(desensitization.type(), desensitization.start(),desensitization.end());}}// 直接返回默认的序列化器return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty);}// 直接返回默认的序列化器return serializerProvider.findNullValueSerializer(null);}
}
五、User实体类
给想要脱敏的字段加上 @Desensitization(type = DesensitizationEnum.枚举类型)注解即可。
完整代码:
package com.example.desensitization.domain;import com.example.desensitization.annotation.Desensitization;
import com.example.desensitization.constant.DesensitizationEnum;
import lombok.Builder;
import lombok.Data;@Data
@Builder
public class User {/*** 主键*/private String id;/*** 用户名*/private String name;/*** 身份证号码*/@Desensitization(type = DesensitizationEnum.ID_CARD_NO)private String idCardNo;/*** 电话号码*/@Desensitization(type = DesensitizationEnum.PHONE)private String phone;/*** 地址*/@Desensitization(type = DesensitizationEnum.CUSTOM_RULE,start = 2,end = 5)private String address;/*** 银行卡号*/@Desensitization(type = DesensitizationEnum.BANK_CARD_NO)private String bankCardNo;}
六、UserController请求层
完整代码:
package com.example.desensitization.controller;import com.example.desensitization.domain.User;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.UUID;@RestController
@RequestMapping
public class UserController {@GetMapping("/user")public User user(){User user = User.builder().id(UUID.randomUUID().toString()).name("张三").idCardNo("32089809285012823").phone("13919819285").bankCardNo("62427292012731238812").address("江苏省南通市").build();return user;}}
七、运行测试
浏览器直接访问:http://localhost:8080/user

可以看到隐私的信息都进行了数据脱敏的处理!
八、Gitee源码
源码地址:SpringBoot中优雅的实现隐私数据脱敏
九、总结
以上就是我对于SpringBoot中如何优雅的实现隐私数据脱敏的完整教程,如有问题,欢迎评论区留言!
相关文章:
SpringBoot中优雅的实现隐私数据脱敏(提供Gitee源码)
前言:在实际项目开发中,可能会对一些用户的隐私信息进行脱敏操作,传统的方式很多都是用replace方法进行手动替换,这样会由很多冗余的代码并且后续也不好维护,本期就讲解一下如何在SpringBoot中优雅的通过序列化的方式去…...
Elasticsearch集群shard过多后导致的性能问题分析
1.问题现象 上午上班以后发现ES日志集群状态不正确,集群频繁地重新发起选主操作。对外不能正常提供数据查询服务,相关日志数据入库也产生较大延时 2.问题原因 相关日志 查看ES集群日志如下: 00:00:51开始集群各个节点与当时的master节点…...
Unity框架学习--5 事件中心管理器
作用:访问其它脚本时,不直接访问,而是通过发送一条“命令”,让监听了这条“命令”的脚本自动执行对应的逻辑。 原理: 1、让脚本向事件中心添加事件,监听对应的“命令”。 2、发送“命令”,事件…...
(二)结构型模式:3、过滤器模式(Filter、Criteria Pattern)(C++示例)
目录 1、过滤器模式(Filter、Criteria Pattern)含义 2、过滤器模式应用场景 3、过滤器模式主要几个关键角色 4、C实现过滤器模式的示例 1、过滤器模式(Filter、Criteria Pattern)含义 (1)过滤器模式是…...
谷歌在Chrome浏览器中推进抗量子加密技术
近日,Chromium博客上发表的一篇博文称,为了加强网络安全,应对迫在眉睫的量子计算机威胁,谷歌各个团队密切合作,为网络向抗量子密码学的过渡做好准备。 谷歌的Chrome团队在博客中写道,该项目涉及修订技术标准…...
Kotlin的数组
在 Kotlin 中,数组是一种固定大小的有序集合,可以存储相同类型的元素。Kotlin 提供了两种类型的数组:原生数组和数组类。以下是 Kotlin 中数组的详细使用方法: 1.创建数组 Kotlin 支持使用 arrayOf() 函数来创建数组:…...
centos 安装docker
1.更新你的系统: sudo yum update -y2.安装必需的软件包: Docker 需要 yum-utils, device-mapper-persistent-data 和 lvm2 软件包来运行。安装它们: sudo yum install -y yum-utils device-mapper-persistent-data lvm23.设置 Docker 的仓库: 使用以下命令添加 D…...
Oracle-如何判断字符串包含中文字符串(汉字),删除中文内容及保留中文内容
今天遇见一个问题需要将字段中包含中文字符串的筛选出来 --建表 CREATE TABLE HADOOP1.AAA ( ID VARCHAR2(255) ); --添加字段INSERT INTO HADOOP1.AAA(ID)VALUES(理解);....--查询表内容SELECT * FROM HADOOP1.AAA;在网上查找了一下有以下三种方式: 第一种&#…...
File 类的用法, InputStream和Reader, OutputStream和Writer 的用法
前言 普通的文件长这样: 其实目录也是一种特殊文件: 一、文件前缀知识 (一)绝对路径和相对路径 以盘符开头的的路径,叫做绝对路径,如:D:\360Downloads\cat.jpg 以.或..开头的路径,…...
AtCoder Beginner Contest 315 Task:A/B/C/E
A - tcdr 处理字符串简单题,题目要求去除字符串中的a,e,i,o,u即可 #include<iostream> using namespace std; int main() {string s;cin>>s;for(int i0;i<s.length();i){if(s[i]a||s[i]e||s[i]i||s[i]o||s[i]u)continue;cout<<s[i];} }B - T…...
【项目实践】基于LSTM的一维数据扩展与预测
基于LSTM的一维数据拟合扩展 一、引(fei)言(hua) 我在做Sri Lanka生态系统服务价值计算时,中间遇到了一点小问题。从世界粮农组织(FAO)上获得Sri Lanka主要农作物产量和价格数据时,其中的主要作物Sorghum仅有2001-2006年的数据,而Millet只有…...
webshell实践,在nginx上实现负载均衡
1、配置多台虚拟机,用作服务器 在不同的虚拟机上安装httpd服务 我采用了三台虚拟机进行服务器设置:192.168.240.11、192.168.240.12、192.168.240.13 [rootnode0-8 /]# yum install httpd -y #使用yum安装httpd服务#开启httpd服务 [rootnode0-8 /]# …...
LVS+Keepalived集群
keepalived Keepalived及其工作原理 Keepalived 是一个基于VRRP协议来实现的LVS服务高可用方案,可以解决静态路由出现的单点故障问题 在一个LVS服务集群中通常有主服务器(MASTER)和备份服务器(BACKUP)两种角色的服务…...
Java的网络编程
网络编程 两台设备之间通过网络实现数据传输,将数据通过网络从一台设备传输到另一台设备 网络 两台或多台设备通过一定物理设备连接起来构成了网络 网络又分为: 局域网:覆盖范围最小,仅仅覆盖一个教室或一个机房城域网:覆盖范围较大,可以…...
kafka配置远程连接
要想实现在本地连接服务器的kafka,则必须在远程kafka配置远程连接 默认的 kafka 配置是无法远程访问的,解决该问题有几个方案。 方案1 advertised.listenersPLAINTEXT://IP:9092 注意必须是 ip,不能是 hostname 方案2 advertised.listene…...
css实现渐变色border
方式1 div {border: 4px solid;border-image: linear-gradient(to right, #8f41e9, #578aef) 1; }/* 或者 */ div {border: 4px solid;border-image-source: linear-gradient(to right, #8f41e9, #578aef);border-image-slice: 1; }作者:MudOnTire 链接:…...
管理 IBM Spectrum LSF
管理 IBM Spectrum LSF 了解如何管理 IBM Spectrum LSF 集群,控制守护程序,更改集群配置以及使用主机和队列。 管理 LSF 作业和作业调度策略。 查看作业信息和控制作业。 了解如何配置资源并将其分配给 LSF 作业。 了解如何在 LSF 集群中提交࿰…...
117页数字化转型与产业互联网发展趋势及机会分析报告PPT
导读:原文《》(获取来源见文尾),本文精选其中精华及架构部分,逻辑清晰、内容完整,为快速形成售前方案提供参考。 喜欢文章,您可以点赞评论转发本文,了解更多内容请私信:方…...
【JavaWeb】实训的长篇笔记(上)
JavaWeb的实训是学校的一门课程,老师先讲解一些基础知识,然后让我们自己开发一个比较简单的Web程序。可涉及的知识何其之多,不是实训课的 3 周时间可以讲得完的,只是快速带过。他说:重点是Web开发的流程。 我的实训草草…...
如何使用Docker安装AWVS?
前言 还记得很早的时候使用AWVS,还需要找位置,贴补丁,放文件,现在慢慢掌握Docker后发现,使用Docker去部署一些东西就很方便,当然也包括AWVS。 我们今天带大家通过Docker部署AWVS(有中文哦&…...
SkyWalking 10.2.0 SWCK 配置过程
SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外,K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案,全安装在K8S群集中。 具体可参…...
rknn优化教程(二)
文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK,开始写第二篇的内容了。这篇博客主要能写一下: 如何给一些三方库按照xmake方式进行封装,供调用如何按…...
【Linux】C语言执行shell指令
在C语言中执行Shell指令 在C语言中,有几种方法可以执行Shell指令: 1. 使用system()函数 这是最简单的方法,包含在stdlib.h头文件中: #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...
Linux简单的操作
ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...
linux 错误码总结
1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...
鱼香ros docker配置镜像报错:https://registry-1.docker.io/v2/
使用鱼香ros一件安装docker时的https://registry-1.docker.io/v2/问题 一键安装指令 wget http://fishros.com/install -O fishros && . fishros出现问题:docker pull 失败 网络不同,需要使用镜像源 按照如下步骤操作 sudo vi /etc/docker/dae…...
DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”
目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...
R语言速释制剂QBD解决方案之三
本文是《Quality by Design for ANDAs: An Example for Immediate-Release Dosage Forms》第一个处方的R语言解决方案。 第一个处方研究评估原料药粒径分布、MCC/Lactose比例、崩解剂用量对制剂CQAs的影响。 第二处方研究用于理解颗粒外加硬脂酸镁和滑石粉对片剂质量和可生产…...
【Redis】笔记|第8节|大厂高并发缓存架构实战与优化
缓存架构 代码结构 代码详情 功能点: 多级缓存,先查本地缓存,再查Redis,最后才查数据库热点数据重建逻辑使用分布式锁,二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...
FFmpeg:Windows系统小白安装及其使用
一、安装 1.访问官网 Download FFmpeg 2.点击版本目录 3.选择版本点击安装 注意这里选择的是【release buids】,注意左上角标题 例如我安装在目录 F:\FFmpeg 4.解压 5.添加环境变量 把你解压后的bin目录(即exe所在文件夹)加入系统变量…...
