如何在SpringBoot项目上让接口返回数据脱敏,一个注解即可
1 背景
需求是某些接口返回的信息,涉及到敏感数据的必须进行脱敏操作
2 思路
①要做成可配置多策略的脱敏操作,要不然一个个接口进行脱敏操作,重复的工作量太多,很显然违背了“多写一行算我输”的程序员规范。思来想去,定义数据脱敏注解和数据脱敏逻辑的接口, 在返回类上,对需要进行脱敏的属性加上,并指定对应的脱敏策略操作。
②接下来我只需要拦截控制器返回的数据,找到带有脱敏注解的属性操作即可,一开始打算用 @ControllerAdvice 去实现,但发现需要自己去反射类获取注解。当返回对象比较复杂,需要递归去反射,性能一下子就会降低,于是换种思路,我想到平时使用的 @JsonFormat,跟我现在的场景很类似,通过自定义注解跟字段解析器,对字段进行自定义解析,tql。
3 实现代码
3.1自定义数据注解,并可以配置数据脱敏策略:
package com.wkf.workrecord.tools.desensitization;import java.lang.annotation.*;/*** 注解类* @author wuKeFan* @date 2023-02-20 09:36:39*/@Target({ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataMasking {DataMaskingFunc maskFunc() default DataMaskingFunc.NO_MASK;}3.2 自定义 Serializer,参考 jackson 的 StringSerializer,下面的示例只针对 String 类型进行脱敏
DataMaskingOperation.class:
package com.wkf.workrecord.tools.desensitization;/*** 接口脱敏操作接口类* @author wuKeFan* @date 2023-02-20 09:37:48*/
public interface DataMaskingOperation {String MASK_CHAR = "*";String mask(String content, String maskChar);}DataMaskingFunc.class:
package com.wkf.workrecord.tools.desensitization;import org.springframework.util.StringUtils;/*** 脱敏转换操作枚举类* @author wuKeFan* @date 2023-02-20 09:38:35*/
public enum DataMaskingFunc {/*** 脱敏转换器*/NO_MASK((str, maskChar) -> {return str;}),ALL_MASK((str, maskChar) -> {if (StringUtils.hasLength(str)) {StringBuilder sb = new StringBuilder();for (int i = 0; i < str.length(); i++) {sb.append(StringUtils.hasLength(maskChar) ? maskChar : DataMaskingOperation.MASK_CHAR);}return sb.toString();} else {return str;}});private final DataMaskingOperation operation;private DataMaskingFunc(DataMaskingOperation operation) {this.operation = operation;}public DataMaskingOperation operation() {return this.operation;}}DataMaskingSerializer.class:
package com.wkf.workrecord.tools.desensitization;import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
import com.fasterxml.jackson.databind.ser.std.StdScalarSerializer;import java.io.IOException;
import java.util.Objects;/*** 自定义Serializer* @author wuKeFan* @date 2023-02-20 09:39:47*/
public final class DataMaskingSerializer extends StdScalarSerializer<Object> {private final DataMaskingOperation operation;public DataMaskingSerializer() {super(String.class, false);this.operation = null;}public DataMaskingSerializer(DataMaskingOperation operation) {super(String.class, false);this.operation = operation;}public boolean isEmpty(SerializerProvider prov, Object value) {String str = (String)value;return str.isEmpty();}public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException {if (Objects.isNull(operation)) {String content = DataMaskingFunc.ALL_MASK.operation().mask((String) value, null);gen.writeString(content);} else {String content = operation.mask((String) value, null);gen.writeString(content);}}public final void serializeWithType(Object value, JsonGenerator gen, SerializerProvider provider, TypeSerializer typeSer) throws IOException {this.serialize(value, gen, provider);}public JsonNode getSchema(SerializerProvider provider) {return this.createSchemaNode("string", true);}public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException {this.visitStringFormat(visitor, typeHint);}
}3.3 自定义 AnnotationIntrospector,适配我们自定义注解返回相应的 Serializer
package com.wkf.workrecord.tools.desensitization;import com.fasterxml.jackson.databind.introspect.Annotated;
import com.fasterxml.jackson.databind.introspect.NopAnnotationIntrospector;
import lombok.extern.slf4j.Slf4j;/*** @author wuKeFan* @date 2023-02-20 09:43:41*/
@Slf4j
public class DataMaskingAnnotationIntroSpector extends NopAnnotationIntrospector {@Overridepublic Object findSerializer(Annotated am) {DataMasking annotation = am.getAnnotation(DataMasking.class);if (annotation != null) {return new DataMaskingSerializer(annotation.maskFunc().operation());}return null;}}3.4 覆盖 ObjectMapper:
package com.wkf.workrecord.tools.desensitization;import com.fasterxml.jackson.databind.AnnotationIntrospector;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.introspect.AnnotationIntrospectorPair;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;/*** 覆盖 ObjectMapper* @author wuKeFan* @date 2023-02-20 09:44:35*/
@Configuration(proxyBeanMethods = false)
public class DataMaskConfiguration {@Configuration(proxyBeanMethods = false)@ConditionalOnClass({Jackson2ObjectMapperBuilder.class})static class JacksonObjectMapperConfiguration {JacksonObjectMapperConfiguration() {}@Bean@PrimaryObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {ObjectMapper objectMapper = builder.createXmlMapper(false).build();AnnotationIntrospector ai = objectMapper.getSerializationConfig().getAnnotationIntrospector();AnnotationIntrospector newAi = AnnotationIntrospectorPair.pair(ai, new DataMaskingAnnotationIntroSpector());objectMapper.setAnnotationIntrospector(newAi);return objectMapper;}}}
3.5 返回对象加上注解:
package com.wkf.workrecord.tools.desensitization;import lombok.Data;import java.io.Serializable;/*** 需要脱敏的实体类* @author wuKeFan* @date 2023-02-20 09:35:52*/
@Data
public class User implements Serializable {/*** 主键ID*/private Long id;/*** 姓名*/@DataMasking(maskFunc = DataMaskingFunc.ALL_MASK)private String name;/*** 年龄*/private Integer age;/*** 邮箱*/@DataMasking(maskFunc = DataMaskingFunc.ALL_MASK)private String email;}4 测试
我们写一个Controller测试一下看是不是我们需要的效果
4.1 测试的Controller类DesensitizationController.class如下:
package com.wkf.workrecord.tools.desensitization;import com.biboheart.brick.model.BhResponseResult;
import com.wkf.workrecord.utils.ResultVOUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;/*** 测试接口脱敏测试控制类* @author wuKeFan* @date 2022-06-21 17:23*/
@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("/desensitization/")
public class DesensitizationController {@RequestMapping(value = "test", method = {RequestMethod.GET, RequestMethod.POST})public BhResponseResult<User> test() {User user = new User();user.setAge(1);user.setEmail("123456789@qq.com");user.setName("吴名氏");user.setId(1L);return ResultVOUtils.success(user);}}
4.2 PostMan接口请求,效果符合预期,如图:

相关文章:
如何在SpringBoot项目上让接口返回数据脱敏,一个注解即可
1 背景需求是某些接口返回的信息,涉及到敏感数据的必须进行脱敏操作2 思路①要做成可配置多策略的脱敏操作,要不然一个个接口进行脱敏操作,重复的工作量太多,很显然违背了“多写一行算我输”的程序员规范。思来想去,定…...
python 之 海龟绘图(turtle)
注:从个人博客园移植而来 使用简介 python 2.6引入的一个简单的绘图工具,俗称为海龟绘图。3.x以上使用的话,可通过pip进行安装,命令为: pip/pip3 install turtle如果出现如下错误: 解决方式: …...
RT-Thread SPI使用教程
RT-Thread SPI 使用教程 实验环境使用的是正点原子的潘多拉开发板。 SPI从机设备使用的是BMP280温湿度大气压传感器。 使用RT-Thread Studio搭建基础功能。 1. 创建工程 使用RT-Thread Studio IDE创建芯片级的工程。创建完成后,可以直接编译下载进行测试。 2.…...
shiro使用——整合spring
shiro使用——整合spring 1. 引入相关配置 <dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>1.9.1</version></dependency>2. 自定义Realm类 继承AuthorizingRealm 并重写相…...
2023-02-20 leetcode-AccountsMerge
摘要: 记录对leetcode-AccountsMerge的反思 要求: Given a list accounts, each element accounts[i] is a list of strings, where the first element accounts[i][0] is a name, and the rest of the elements are emails representing emails of the account. * Now, w…...
中国高速公路行业市场规模及未来发展趋势
中国高速公路行业市场规模及未来发展趋势编辑中国高速公路行业市场规模正在迅速增长。随着中国经济的快速发展和城市化的加速,对交通基础设施的需求也在不断增加。高速公路是最有效的交通工具,可以大大缩短交通时间,提高出行效率。因此&#…...
佳能iC MF645CX彩色激光多功能打印机报E302-0001故障码检修
故障现象: 一台佳能iC MF645CX彩色激光多功能一体机开机报E302-0001故障代码,如果设备未恢复,请联系经销商或客户支持中心。 维修分析: 佳能iC MF645CX彩色激光多功能一体机开机报E302-0001故障代码的...
加密越来越简单——用JavaScript实现数据加密和解密
加密越来越简单——用JavaScript实现数据加密和解密概念常用算法1. MD5加密算法2. SHA-1加密算法3. AES加密算法代码示例结论总结在当今互联网的世界中,安全性越来越受到关注,数据加密成为了必不可少的一环。Javascript作为前端开发的主要语言之一&#…...
线程池的使用场景
学习整理线程池的使用场景。...
图像分割算法
图像分割算法阈值分割固定阈值自适应阈值大津阈值(OTSU)最大熵阈值连通域分析区域生长分水岭阈值分割、连通域分析、区域生长、分水岭 阈值分割 固定阈值、自适应阈值(adaptiveThreshold)、大津阈值(OTSU)、最大熵阈值(KSW) 固定阈值 固定阈值的调用函数: //Input…...
《mysql技术内幕:innodb存储引擎》笔记
任何时候Why都比What重要;不要相信任何的“神话”,学会自己思考;不要墨守成规,大部分人都知道的事情可能是错误的;不要相信网上的传言,去测试,根据自己的实践做出决定;花时间充分地思考,敢于提出质疑。1.MYSQL被设计为一个单进程多…...
windows与linux系统ntp服务器配置
一. windows 打开windows终端(管理员),顺序输入以下指令 net stop w32time w32tm /unregister w32tm /register net start w32time w32tm /resync net stop w32time net start w32timewin r 输入regedit,打开注册表。 ①将HKEY_…...
html常用font-family设置字体样式
<table border"1" cellpadding"0" cellspacing"0" ><tr><td><h3 style"font-family: 黑体;">黑体:SimHei</h3></td><td><h3 style"font-family: 华文黑体;">华…...
数据库事务AICD以及隔离级别
目录一.事务的ACID二.隔离级别三.并发事务中的问题1.脏写2.脏读3.不可重复读4.幻读四.MVCC机制五.读锁与写锁六.大事务的影响七.事务优化一.事务的ACID 原子性(Atomicity):一个事务中的所有操作,要么全部成功,要么失败全部回滚,不…...
(4)VScode之ssh基础配置
VScode和SSH基础配置问题集合 Author:onceday date:2022年8月31日 本文记录linux的ssh和vscode开发环境搭建之路。 参考文档: 离线安装vscode Once Day CSDN博客关于x86、x86_64/x64、amd64和arm64/aarch64 Bogon 简书arm64和aarch64之间…...
springcloud-1环境搭建、service provider
搭建项目环境创建新项目,选择archetype-webapp,创建一个父项目,该父项目会去管理多个微服务模块(module)项目设置Settings->Editor->File Encoding:Global/Project Encoding 改为UTF-8Default encoding for properties files:默认属性文…...
光谱仪工作过程及重要参数定义
标题光谱仪工作过程CCD、PDA薄型背照式BT-CCD狭缝Slit暗电流Dark Current分辨率Resolution色散Dispersion光栅和闪耀波长Grating波长精度、重复性和准确度Precision带宽Band widthF数F/#光谱仪工作过程 CCD、PDA 电荷耦合器件(Charger Coupled Device,缩…...
W800|iot|HLK-W800-KIT-PRO|AliOS|阿里云| |官方demo|学习(1):板载AliOS系统快速上手
板载系统简介 HLK-W800-KIT-PRO 是海凌科电子面向开发者,采用了联盛德 w800 方案,带有一个RGB三色灯,集成了 CHT8305C 温湿度传感器的多功能开发板,用户可以在上面学习、研究嵌入式系统和物联网产品的开发,本套设备运行…...
字节终面,一道Linux题难住我了
以下是一道难道系数中高并且高频出现的linux面试题,题目具体要求如下: linux面试题: 某文件有多列数据,空格隔开,统计第n列单词,打印出现频率最高的5个单词。 解答这道面试题需要用到3个linux命令ÿ…...
三、NetworkX工具包实战2——可视化【CS224W】(Datawhale组队学习)
开源内容:https://github.com/TommyZihao/zihao_course/tree/main/CS224W 子豪兄B 站视频:https://space.bilibili.com/1900783/channel/collectiondetail?sid915098 斯坦福官方课程主页:https://web.stanford.edu/class/cs224w NetworkX…...
利用最小二乘法找圆心和半径
#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...
设计模式和设计原则回顾
设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...
Linux 文件类型,目录与路径,文件与目录管理
文件类型 后面的字符表示文件类型标志 普通文件:-(纯文本文件,二进制文件,数据格式文件) 如文本文件、图片、程序文件等。 目录文件:d(directory) 用来存放其他文件或子目录。 设备…...
Objective-C常用命名规范总结
【OC】常用命名规范总结 文章目录 【OC】常用命名规范总结1.类名(Class Name)2.协议名(Protocol Name)3.方法名(Method Name)4.属性名(Property Name)5.局部变量/实例变量(Local / Instance Variables&…...
(二)原型模式
原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...
ios苹果系统,js 滑动屏幕、锚定无效
现象:window.addEventListener监听touch无效,划不动屏幕,但是代码逻辑都有执行到。 scrollIntoView也无效。 原因:这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作,从而会影响…...
【Go语言基础【13】】函数、闭包、方法
文章目录 零、概述一、函数基础1、函数基础概念2、参数传递机制3、返回值特性3.1. 多返回值3.2. 命名返回值3.3. 错误处理 二、函数类型与高阶函数1. 函数类型定义2. 高阶函数(函数作为参数、返回值) 三、匿名函数与闭包1. 匿名函数(Lambda函…...
深入浅出深度学习基础:从感知机到全连接神经网络的核心原理与应用
文章目录 前言一、感知机 (Perceptron)1.1 基础介绍1.1.1 感知机是什么?1.1.2 感知机的工作原理 1.2 感知机的简单应用:基本逻辑门1.2.1 逻辑与 (Logic AND)1.2.2 逻辑或 (Logic OR)1.2.3 逻辑与非 (Logic NAND) 1.3 感知机的实现1.3.1 简单实现 (基于阈…...
群晖NAS如何在虚拟机创建飞牛NAS
套件中心下载安装Virtual Machine Manager 创建虚拟机 配置虚拟机 飞牛官网下载 https://iso.liveupdate.fnnas.com/x86_64/trim/fnos-0.9.2-863.iso 群晖NAS如何在虚拟机创建飞牛NAS - 个人信息分享...
Chrome 浏览器前端与客户端双向通信实战
Chrome 前端(即页面 JS / Web UI)与客户端(C 后端)的交互机制,是 Chromium 架构中非常核心的一环。下面我将按常见场景,从通道、流程、技术栈几个角度做一套完整的分析,特别适合你这种在分析和改…...
