利用Hutool+自定义注解实现数据脱敏

利用Hutool+自定义注解实现数据脱敏

前言

我们在使用手机银行的时候经常能看到APP上会将银行卡的卡号中间部分给隐藏掉使用 ***** 来代替,在某些网站上查看一些业务密码时(例如签到密码等)也会使用 ***** 来隐藏掉真正的密码,那么这种方式是如何实现的呢?

Hutool

Hutool是一个小而全的Java工具类库,通过静态方法封装,降低相关API的学习成本,提高工作效率,使Java拥有函数式语言般的优雅,让Java语言也可以“甜甜的”。

Hutool中的工具方法来自每个用户的精雕细琢,它涵盖了Java开发底层代码中的方方面面,它既是大型项目开发中解决小问题的利器,也是小型项目中的效率担当;

Hutool是项目中 util 包友好的替代,它节省了开发人员对项目中公用类和公用工具方法的封装时间,使开发专注于业务,同时可以最大限度的避免封装不完善带来的bug。

我们这篇文章的实现思路就基于Hutool来实现,在Hutool中提供了一个名为 DesensitizedUtil 的工具类,我们使用这个工具类来加密。

首先我们先来看一下这个类里的具体实现,如下:

image.png

我们可以看到映入眼帘的除了一个无参构造之外就是一个名为 desensitized 的方法,这个方法就是我们加密的主要方法,里面利用了 switch…case 方法来区分不同的加密方法。我们可以来写一个单元测试来测试一下通过这个方法加密后是什么样的。

image.png

以上为加密后的信息,里面我使用了不同的类型来进行加密,目前最新版的Hutool支持脱敏加密的类型如下:

  1. 用户ID
  2. 中文名
  3. 密码
  4. 地址
  5. 邮箱
  6. 座机号
  7. 手机号
  8. 中国大陆的车牌号
  9. 银行卡号
  10. IPv4地址
  11. IPv6地址
  12. 自定义脱敏

实现

通过以上的示例我们就可以开始编写我们自己的脱敏操作了,首先我们要先根据以上Hutool中提供的脱敏类型来编写我们自己的类型**(如嫌麻烦也可省略此步骤,直接使用DesensitizedUtil中的DesensitizedType)**

编写数据脱敏类型

/*** @author Bummon* @description 数据脱敏策略* @date 2023-09-01 17:43*/
public enum DataMaskingType {/*** 用户ID*/USER_ID,/*** 中文名*/CHINESE_NAME,/*** 身份证号*/ID_CARD,/*** 座机*/FIXED_PHONE,/*** 手机号*/MOBILE_PHONE,/*** 地址*/ADDRESS,/*** 邮箱*/EMAIL,/*** 密码*/PASSWORD,/*** 中国大陆车牌号*/CAR_LICENSE,/*** 银行卡号*/BANK_CARD,/*** IPv4地址*/IPV4,/*** IPv6地址*/IPV6,/*** 自定义类型*/CUSTOM;}

编写自定义注解

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;/*** @author Bummon* @description 数据脱敏自定义注解* @date 2023-09-01 18:01*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonSerialize(using = DataMaskingSerialize.class)
public @interface DataMasking {/*** 数据脱敏类型*/DataMaskingType type() default DataMaskingType.CUSTOM;/*** 脱敏开始位置(包含)*/int start() default 0;/*** 脱敏结束位置(不包含)*/int end() default 0;}

需要注意的是:当DataMaskingType为 CUSTOM 时,才需要填写 startend ,且这两个参数才会生效,且 start 中是包含当前下标的字符的,而 end 不包含当前下标的字符。

编写自定义序列化类

import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.DesensitizedUtil;
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;/*** @author Bummon* @description* @date 2023-09-01 18:14*/
@AllArgsConstructor
@NoArgsConstructor
public class DataMaskingSerialize extends JsonSerializer implements ContextualSerializer {private DataMaskingType type;private Integer start;private Integer end;@Overridepublic void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {String value = (String) o;switch (type) {//userIdcase USER_ID:jsonGenerator.writeString(String.valueOf(DesensitizedUtil.desensitized(value, DesensitizedUtil.DesensitizedType.USER_ID)));break;//中文名case CHINESE_NAME:jsonGenerator.writeString(DesensitizedUtil.desensitized(value, DesensitizedUtil.DesensitizedType.CHINESE_NAME));break;//身份证号case ID_CARD:jsonGenerator.writeString(DesensitizedUtil.desensitized(value, DesensitizedUtil.DesensitizedType.ID_CARD));break;//座机case FIXED_PHONE:jsonGenerator.writeString(DesensitizedUtil.desensitized(value, DesensitizedUtil.DesensitizedType.FIXED_PHONE));break;//手机号case MOBILE_PHONE:jsonGenerator.writeString(DesensitizedUtil.desensitized(value, DesensitizedUtil.DesensitizedType.MOBILE_PHONE));break;//地址case ADDRESS:jsonGenerator.writeString(DesensitizedUtil.desensitized(value, DesensitizedUtil.DesensitizedType.ADDRESS));break;//邮箱case EMAIL:jsonGenerator.writeString(DesensitizedUtil.desensitized(value, DesensitizedUtil.DesensitizedType.EMAIL));break;case BANK_CARD:jsonGenerator.writeString(DesensitizedUtil.desensitized(value, DesensitizedUtil.DesensitizedType.BANK_CARD));break;//密码case PASSWORD:jsonGenerator.writeString(DesensitizedUtil.desensitized(value, DesensitizedUtil.DesensitizedType.PASSWORD));break;//中国大陆车牌号case CAR_LICENSE:jsonGenerator.writeString(DesensitizedUtil.desensitized(value, DesensitizedUtil.DesensitizedType.CAR_LICENSE));break;case IPV4:jsonGenerator.writeString(DesensitizedUtil.desensitized(value, DesensitizedUtil.DesensitizedType.IPV4));break;case IPV6:jsonGenerator.writeString(DesensitizedUtil.desensitized(value, DesensitizedUtil.DesensitizedType.IPV6));break;//自定义case CUSTOM:jsonGenerator.writeString(CharSequenceUtil.hide(value, start, end));break;default:break;}}@Overridepublic JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) throws JsonMappingException {if (Objects.nonNull(beanProperty)) {//判断是否为string类型if (Objects.equals(beanProperty.getType().getRawClass(), String.class)) {DataMasking anno = beanProperty.getAnnotation(DataMasking.class);if (Objects.isNull(anno)) {anno = beanProperty.getContextAnnotation(DataMasking.class);}if (Objects.nonNull(anno)) {return new DataMaskingSerialize(anno.type(), anno.start(), anno.end());}}return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty);}return serializerProvider.findNullValueSerializer(null);}
}

我们继承于 JsonSerializer 并实现了 ContextualSerializer 中的方法,并对我们自定义注解声明的字段进行拦截和脱敏加密操作,接下来我们可以来测试一下效果。

测试

因为是实例化的时候才会被脱敏,那我们就创建一个实体类来存放我们需要加密的信息。

编写测试实体类

import com.bummon.mask.DataMasking;
import com.bummon.mask.DataMaskingType;
import lombok.*;/*** @author Bummon* @description* @date 2023-09-01 18:29*/
@Data
@Builder
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class TestEntity {@DataMasking(type = DataMaskingType.USER_ID)private Integer userId;@DataMasking(type = DataMaskingType.CHINESE_NAME)private String userName;@DataMasking(type = DataMaskingType.ADDRESS)private String address;@DataMasking(type = DataMaskingType.ID_CARD)private String idCard;@DataMasking(type = DataMaskingType.FIXED_PHONE)private String fixedPhone;@DataMasking(type = DataMaskingType.MOBILE_PHONE)private String mobilePhone;@DataMasking(type = DataMaskingType.EMAIL)private String email;@DataMasking(type = DataMaskingType.PASSWORD)private String password;@DataMasking(type = DataMaskingType.CAR_LICENSE)private String carLicense;@DataMasking(type = DataMaskingType.BANK_CARD)private String bankCard;@DataMasking(type = DataMaskingType.IPV4)private String ipv4;@DataMasking(type = DataMaskingType.IPV6)private String ipv6;@DataMasking(type = DataMaskingType.CUSTOM,start = 3,end = 9)private String custom;/*** 不进行数据脱敏的字段*/private String noMask;}

编写测试Controller

import com.bummon.entity.TestEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;/*** @author Bummon* @description* @date 2023-09-01 18:39*/
@RestController
public class TestController {@GetMapping("/test")public TestEntity test() {return TestEntity.builder().userId(1234567890).userName("张三").password("12").address("河南省郑州市中原区").email("xxxx@xx.com").fixedPhone("0838-5553792").mobilePhone("13888888888").carLicense("豫P3U253").bankCard("1679374639283740").idCard("412711223344556677").ipv4("192.168.1.236").ipv6("abcd:1234:aCA9:123:4567:089:0:0000").custom("289073458794").noMask("我是不需要数据脱敏的字段").build();}}

接下来我们启动项目来看测试一下得到的是否为我们预期的数据:

image.png

我们可以看到,我们加了注解的字段都被正确的脱敏了,而没加注解的字段会正常显示。

总结

我们使用了HutoolDesensitizedUtil中的 desensitized 方法来实现数据脱敏,在 CUSTOM 类型的脱敏字段中,startend 两个属性是必填的,且 start 包含当前下标,而 end 不包含当前下标。

本文结束,感谢观看。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/122398.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

重磅! AIFS+MLOps两大AI基核技术前沿洞察报告出炉!

近日&#xff0c;中国国际服务贸易交易会&#xff08;以下简称“服贸会”&#xff09;在京盛大举办&#xff0c;在服贸会“2023中国AIGC创新发展论坛”和“2023中国金融科技论坛”两大论坛的成果发布环节&#xff0c;九章云极DataCanvas公司与IDC重磅发布「AIFS人工智能基础软件…

linux安装minio以及springboot整合使用

文章目录 1.linux安装minio2.springboot整合minio使用 1.linux安装minio 1.新建文件夹 mkdir /home/minio # 数据文件夹 mkdir /home/minio/data # 创建日志文件夹 mkdir /home/minio/log2.进入文件夹 cd /home/minio3.下载minio&#xff0c;链接可从官网获取 wget https://…

.net core 上传文件大小限制

微软官网文档中给的解释是.net core 默认上传文件大小限制是30M&#xff0c;所以即便你项目里没有限制&#xff0c;这里也有个默认限制。 官网链接地址 总结了一下解决办法&#xff1a; 1.首先项目里添加一个web.config自定义配置文件 在配置文件中加上这段配置 <!--//…

MySql学习笔记03——DQL(数据查询)基本命令

DQL 导入数据 首先使用use database进入数据库中&#xff0c;然后使用命令 source D:\mysql_learning\mysql_learning\document\bjpowernode.sql注意文件名不能有双引号&#xff0c;命令结尾没有分号。 SQL脚本 .sql文件是SQL脚本文件&#xff0c;它里面的内容都是SQL语句…

【数学建模】数据预处理

为什么需要数据预处理 数学建模是将实际问题转化为数学模型来解决的过程&#xff0c;而数据预处理是数学建模中非常重要的一步。以下是为什么要进行数据预处理的几个原因&#xff1a; 数据质量&#xff1a;原始数据往往存在噪声、异常值、缺失值等问题&#xff0c;这些问题会对…

MATLAB 的 figure 用法总结

文章目录 Syntax&#xff1a;DescriptionExamples1.figure2.figure(Name,Value)Position 属性: 设置 Figure 的位置和大小Name 属性: 设置 Figure 的名称NumberTitle 属性: 取消 Figure 名称里默认的数字units 属性color 属性 3.f figure(___)4.Working with Multiple Figures…

C++ 多态语法点

前置知识点 成员变量和成员函数分开存储&#xff0c;只有非静态成员变量才属于类的对象上。 静态成员变量和静态成员函数没有在类上存储。 非静态成员函数也不属于类的对象上 class Animal {public:virtual void speak(){cout<<"动物在说话"<<endl;}}v…

sqlserver数据库链接mysql服务器访问数据

sqlserver数据库链接mysql服务器访问数据 关于SqlServer数据库怎么链接mysql数据库我一直不明白&#xff0c;今天项目碰到一个问题需要链接&#xff0c;我就研究了一下&#xff0c;然后就成功了&#xff0c;在这里记录一下。也欢迎朋友互相学习交流借鉴。 1.使用navicat打开S…

2023年信息安全管理与评估(赛项)评分标准第三阶段夺旗挑战CTF(网络安全渗透)

全国职业院校技能大赛 高职组 信息安全管理与评估 &#xff08;赛项&#xff09; 评分标准 第三阶段 夺旗挑战CTF&#xff08;网络安全渗透&#xff09; 竞赛项目赛题 本文件为信息安全管理与评估项目竞赛-第三阶段赛题&#xff0c;内容包括&#xff1a;夺旗挑战CTF&#xff08…

【LeetCode】84.柱状图中最大的矩形

题目 给定 n 个非负整数&#xff0c;用来表示柱状图中各个柱子的高度。每个柱子彼此相邻&#xff0c;且宽度为 1 。 求在该柱状图中&#xff0c;能够勾勒出来的矩形的最大面积。 示例 1: 输入&#xff1a;heights [2,1,5,6,2,3] 输出&#xff1a;10 解释&#xff1a;最大的…

python+selenium自动化测试项目实战

说明&#xff1a;本项目采用流程控制思想&#xff0c;未引用unittest&pytest等单元测试框架 一.项目介绍 目的 测试某官方网站登录功能模块可以正常使用 用例 1.输入格式正确的用户名和正确的密码&#xff0c;验证是否登录成功&#xff1b; 2.输入格式正确的用户名和不…

C++ Opencv视频检测

使用OpenCV进行视频检测的一般步骤如下&#xff1a;导入OpenCV库和视频文件。 对每一个视频帧进行对象检测。可以使用诸如Haar特征分类器、Cascade分类器或深度学习模型等技术进行对象检测。 #include <opencv2/imgcodecs.hpp> #include <opencv2/highgui.hpp> …

安防视频监控/视频汇聚平台EasyCVR服务重启,海康SDK设备无法上线是什么原因?

TSINGSEE青犀视频监控汇聚平台EasyCVR可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。旭帆科技平台既具备传统安防视频监控…

MybatisPlus框架教程:入门、条件构造器、接口操作、代码生成器

MybatisPlus框架 文章目录 MybatisPlus框架快速上手条件构造器接口基本操作新版代码生成器 前面我们体验了JPA带来的快速开发体验&#xff0c;但是我们发现&#xff0c;面对一些复杂查询时&#xff0c;JPA似乎有点力不从心&#xff0c;反观稍微麻烦一点的Mybatis却能够手动编写…

el-dialog设置高度、使用resetFields清除表单项无效问题

初学者容易踩坑的的el-dialog、el-form问题 1. el-dialog设置高度2. el-form中表单项对不齐3. 使用resetFields清除表单项无效 1. el-dialog设置高度 在el-dialog中里面添加一个div设置固定高度&#xff0c;或者限制最小的高度。 <el-dialogtitle"选择图标"v-mod…

数据结构 - 单链表

文章目录 目录 文章目录 一、什么是链表? 线性表: 顺序表: 二、链表的分类和实现 分类: 实现: 1.创建节点类 2.创建单链表 1.addTail(尾增) 2.删除节点值为key的第一个节点 3.插入节点(在指定位置) 4.获取链表长度 总结 前言 大家好,这篇博客给大家讲一下什么是…

『虫无涯→_→读书推荐02期』|全面系统的〖Effective软件测试〗带你完成所有不同类型的测试,GO

目录 我看的书 我的书评/推荐理由 书籍的作者 书籍内容 赠书活动 我看的书 首次看到这本书的封面的时候&#xff0c;我被那个数字惊呆了&#xff0c;【助理软件研发提升10倍质量】&#xff0c;这对我产生了足够了吸引力。因为这个数字是非常的客观的&#xff1b;至于书…

Matlab信号处理2:方波信号的合成与分解

周期信号可展开为傅里叶级数&#xff0c;因此方波信号可用若干谐波去拟合。以下是Matlab的实现&#xff1a; %% 方波信号的分解% 1.生成方波信号 % 方波信号周期、基波频率 T0 2; w0 (2 * pi) / T0; % 方波信号值为1的区间 T1 0.5; % 绘图周期&#xff1a;(2*n1)个周期 n …

化繁为简 面板式空调网关亮相上海智能家居展 智哪儿专访青岛中弘赵哲海

面对中央空调协议不开放和智能家居协议不统一的问题&#xff0c;青岛中弘选择中央空调控制器这一细分赛道入局智能家居市场&#xff0c;始终贯彻“所有空调&#xff0c;一个网关”的产品技术理念&#xff0c;逐渐探索出一条中弘的发展路径和商业模式。 在2023年的SSHT上海国际智…

【css】z-index与层叠上下文

z-index属性用来设置元素的堆叠顺序&#xff0c;使用z-index有一个大的前提&#xff1a;z-index所作用元素的样式列表中必须有position属性并且属性值为absolute、relative或fixed中的一个&#xff0c;否则z-index无效。 层叠上下文 MDN讲解 我们给元素设置的z-index都是有一…