阿昌教你如何优雅的数据脱敏
阿昌教你如何优雅的数据脱敏
Hi,我是阿昌
,最近有一个数据脱敏的需求,要求用户可自定义配置数据权限,并对某种类型数据进行脱敏返回给前端
一、涉及知识点
- SpringMVC
- Java反射
- Java自定义注解
- Java枚举
二、方案选择
1、需求要求
涉及主子账户权限,主账户可在权限中对一些列子账户进行数据权限配置,如姓名/年龄/身份证等,配置后子账户查询页面会根据主账户配置返回脱敏数据;
2、技术方案举例
针对如上类似需求的要求,整理出大致3类方向的技术实现思路
- 直接在代码中硬编码进行吊用脱敏服务或脱敏方案,进行数据脱敏
- 利用自定义注解的方式在SpringMVC生命周期中使用反射/拦截器postHandle等方式进行脱敏
- 利用自定义主角的方式在SpringMVC生命周期的最后JSON结果进行脱敏,用类似replace替换关键词替换字符串,实现脱敏
脱敏的具体逻辑可以直接使用hutool的轮子,如果很个性化就需要增加造轮子;
3、技术方案取舍
针对上面类似的方案进行取舍
- 思路1
- 可灵活变动;
- 但不够优雅;
业务侵入性强
,需要到处修改之前的业务代码,还可能存在修改漏了,或者代码改错的风险(不选择)
- 思路2
- 类属性转换不够灵活,
无法跨数据类型替换
,如int 替换为 str会报错,需要统一定义返回String的Vo对象; - 每次都需要反射解析,需评估
性能消耗
; - 非http场景下,DTO模型标记注解,
服务内部交互序列化脱敏问题
- 类属性转换不够灵活,
- 思路3
替换关键词遗漏
的可能,但可结合nacos进行维护配置关键词;当响应大量json时,字符串replace可能会有性能问题
;消耗内存
如果要替换的字符串较大,而原始字符串也很大,那么在替换过程中会消耗大量的内存。这可能导致内存溢出或性能下降。字符串拼接效率低下
:在替换过程中,可能需要多次拼接字符串。由于String类是不可变的,每次拼接都会创建一个新的字符串对象,这会导致效率低下;可能存在处理时间长,String类的replace方法是通过创建一个新的字符串对象来实现替换的。如果原始字符串很大,那么每次替换都需要创建一个新的字符串对象,这会导致时间复杂度较高;`- 涉及数据安全问题`,无法保证100%替换正确
4、方案选定
上面种种都有问题,最后采用1和2方案结合的案例进行实行;
- 自定义注解;实现对某个需要脱敏字段进行标注
- 业务枚举;来控制对应脱敏逻辑的自定义实现
- 自定义序列化器,集成JsonSerializer + 实现ContextualSerializer;来整合上面的自定义注解 + 业务枚举脱敏逻辑
三、过程
1、自定义注解
@Target(ElementType.FIELD) //作用于字段上
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside // 表示自定义自己的注解Sensitive
@JsonSerialize(using = SensitiveInfoSerializer.class) // 该注解使用序列化的方式
public @interface Sensitive {SensitiveTypeEnum value();
}
2、自定义脱敏序列化器
public class SensitiveInfoSerializer extends JsonSerializer<String> implements ContextualSerializer {SensitiveTypeEnum sensitiveTypeEnum;/*** 方法来源于ContextualSerializer,获取属性上的注解属性,同时返回一个合适的序列化器*/@Overridepublic JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) throws JsonMappingException {// 获取自定义注解Sensitive annotation = beanProperty.getAnnotation(Sensitive.class);// 注解不为空,且标注的字段为Stringif (Objects.nonNull(annotation) && Objects.equals(String.class, beanProperty.getType().getRawClass())) this.sensitiveTypeEnum = annotation.value();//自定义情况,返回本序列化器,将顺利进入到该类中的serialize方法中return this;}// 注解为空,字段不为String,寻找合适的序列化器进行处理return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty);}/*** 方法来源于JsonSerializer<String>:指定返回类型为String类型,serialize()将修改后的数据返回*/@Overridepublic void serialize(String str, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {if (Objects.isNull(sensitiveTypeEnum)) {// 定义策略为空,返回原字符串jsonGenerator.writeString(str);} else {// 定义策略不为空,返回策略处理过的字符串//todo 获取用户自定义数据权限,判断是否需要脱敏jsonGenerator.writeString(sensitiveTypeEnum.desensitized(str));}}
}
3、业务枚举
@AllArgsConstructor
public enum SensitiveTypeEnum {PRICE(1, "价格") {@Overridepublic String desensitized(String sourceStr) {return super.desensitized(sourceStr);}},NAME(2,"姓名"),//.....;private final int id;private final String desc;/*** 加密转化逻辑,如果有特别的加密逻辑,重写即可** @param sourceStr 待脱敏的明文* @return 脱敏后的密文*/public String desensitized(String sourceStr) {return "***";}
}
4、实体类
@Setter
@Getter
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
public class UserVo {/*** 用户名*/@Sensitive(SensitiveTypeEnum.NAME)private String name;
}
四、注意事项
1、自定义脱敏注解不生效
如果上文中提到的每一步都正常操作了,但自定义脱敏注解还是不生效:
那很可能是Spring Boot默认的消息转换器被替换成fastjson了,因为Spring Boot默认是使用jackson
进行序列化的,上面的方案也是
基于jackson的,但如果项目中明确指定了使用fastjson进行序列化,那上面的自定义脱敏注解就不会生效:
@Bean
public HttpMessageConverters httpMessageConverters() {FastJsonConfig fastJsonConfig = new FastJsonConfig();fastJsonConfig.setSerializerFeatures(SerializerFeature.WriteMapNullValue, SerializerFeature.BrowserCompatible);FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter();fastJsonHttpMessageConverter.setFastJsonConfig(fastJsonConfig);return new HttpMessageConverters(fastJsonHttpMessageConverter);
}
fastjson自定义序列化,此时的解决方案是新建过滤器类,实现com.alibaba.fastjson.serializer.ValueFilter接口并重写process方法:
public class CustomerSensitiveValueFilter implements ValueFilter {@Overridepublic Object process(Object object, String name, Object value) {try {Field field = object.getClass().getDeclaredField(name);Sensitive sensitive = field.getAnnotation(Sensitive.class);if (sensitive == null) {return value;}if (!(value instanceof String) || ((String) value).length() == 0) {return value;}String valueStr = (String) value;SensitiveTypeEnum typeEnum = sensitive.value();//todoreturn typeEnum.desensitized(valueStr);} catch (Exception e) {return value;}}}
然后在上面声明httpMessageConverters()的地方新增以下代码:
fastJsonConfig.setSerializeFilters(new CustomerSensitiveValueFilter());
此时,上文中自定义的脱敏注解中,@JacksonAnnotationsInside
和@JsonSerialize(using = SensitiveInfoSerializer.class)
再次运行验证,会发现自定义脱敏注解生效了:
2、注意影响范围
在VO的某个字段上加上@Sensitive(type = SensitiveTypeEnum.NAME)后,所有使用到该VO的接口,在返回数据时,
该字段都会被脱敏,如果列表页接口和详情接口共用了这个VO,但实际情况是列表页该字段需要脱敏,编辑页该字段不需要脱敏,
这种场景就需要特别注意。
3、其他场景
如果有类似用于内部直接EXCEL导出等类似也需要脱敏的场景,上面就会有问题,因为是基于Springmvc的场景;
可在toJSONString方法中自定义指定Filter来走我们自定义的脱敏逻辑Filter;
String s1 = JSON.toJSONString(obj,new CustomerSensitiveValueFilter());
参考内容:
- fastjson自定义序列化
- 自定义注解实现数据序列化时进行数据脱敏(基于springboot默认jackjson)、消息转换器HttpMessageConverter
相关文章:

阿昌教你如何优雅的数据脱敏
阿昌教你如何优雅的数据脱敏 Hi,我是阿昌,最近有一个数据脱敏的需求,要求用户可自定义配置数据权限,并对某种类型数据进行脱敏返回给前端 一、涉及知识点 SpringMVCJava反射Java自定义注解Java枚举 二、方案选择 1、需求要求…...
力扣每日一题80:删除有序数组中的重复项||
题目描述: 给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使得出现次数超过两次的元素只出现两次 ,返回删除后数组的新长度。 不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的…...
SQL——插入已经存在的数据
现在有一套ID为9003的高难度SQL试卷,时长为一个半小时,请你将 2021-01-01 00:00:00 作为发布时间插入到试题信息表examination_info(其表结构如下图),不管该ID试卷是否存在,都要插入成功,请尝试…...

【网络安全 --- 任意文件上传漏洞靶场闯关 6-15关】任意文件上传漏洞靶场闯关,让你更深入了解文件上传漏洞以及绕过方式方法,思路技巧
一,工具资源下载 百度网盘资源下载链接地址: 百度网盘 请输入提取码百度网盘为您提供文件的网络备份、同步和分享服务。空间大、速度快、安全稳固,支持教育网加速,支持手机端。注册使用百度网盘即可享受免费存储空间https://pan…...

阿里云2核2G3M云服务器99元/年,新老同享,续费不涨价!
2023年阿里云双11活动正在火热进行中,推出了一款面向个人开发者、学生、小微企业的年度爆款套餐,2核2G3M云服务器99元/年,新老同享,续费不涨价! 一、活动入口 活动地址:传送门>>> 二、活动详情 …...

UWB 技术在机器人和移动领域的应用题】
多年来,机器人生态系统不断增长,不同的应用程序也在不断增长。如今,机器人出现在许多不同的领域,例如私人家庭、商业场所、仓库和医疗场所。他们要么自主工作,要么与我们并肩工作,帮助我们完成任务。 根据…...
11.1 知识总结(JavaScript)
一、 ECMAScript的历史 年份 名称 描述 1997 ECMAScript 1 第一个版本 1998 ECMAScript 2 版本变更 1999 ECMAScript 3 添加正则表达式 添加try/catch ECMAScript 4 没有发布 2009 ECMAScript 5 添加"strict mode"严格模式 添加JSON支持 2011 EC…...

【Java 进阶篇】Java Web开发:实现验证码功能
在Web应用程序中,验证码(CAPTCHA)是一种常见的安全工具,用于验证用户是否为人类而不是机器。验证码通常以图像形式呈现,要求用户在登录或注册时输入正确的字符。在这篇文章中,我们将详细介绍如何在Java Web…...
C++启动线程的方法
(1)函数指针 情况一:主线程有join,正常执行 #include <thread> #include <iostream>void work(int num) {while(num-- > 0) {std::cout << num << std::endl;} }int main() {std::thread t(work, 5);…...
Distilling the Knowledge in a Neural Network学习笔记
1.主要内容是什么: 这篇论文介绍了一种有效的知识迁移方法——蒸馏,可以将大型模型中的知识转移到小型模型中,从而提高小型模型的性能。这种方法在实际应用中具有广泛的潜力,并且可以应用于各种不同的任务和领域。 论文中首先介绍…...

JVM虚拟机:垃圾回收算法和垃圾回收器之间的关系
GC垃圾回收算法 在前面的课程中我们学习了GC垃圾回收算法,分别为: 引用回收算法 复制算法 标记清除算法 标记整理算法 这些垃圾回收算法是理论,有多种垃圾回收器可以实现这些理论。目前为止没有最完美的垃圾回收器,只能针对具体的情况选择最合适的垃圾回收器,进行分代收集…...
oracle sqlplus的使用 ,查询oracle实例名和服务名,查询oracle容器,切换oracle容器
Oracle的sqlplus是与oracle数据库进行交互的客户端工具(oracle数据库自带的客户端工具),借助sqlplus可以查看、修改数据库记录。在sqlplus中,可以运行sql*plus命令与sql语句。 1。先使用root账户登陆系统后,使用su - o…...

golang工程——opentelemetry简介、架构、概念、追踪原理
opentelemetry 简介 OpenTelemetry,简称OTel,是一个与供应商无关的开源可观测性框架,用于检测、生成、收集和导出 遥测数据,如轨迹、度量、日志。OTel的目标是提供一套标准化的供应商无关SDK、API和工具,用于接 收、…...

Python 自动化(十六)静态文件处理
准备工作 将不同day下的代码分目录管理,方便后续复习查阅 (testenv) [rootlocalhost projects]# ls day01 day02 (testenv) [rootlocalhost projects]# mkdir day03 (testenv) [rootlocalhost projects]# cd day03 (testenv) [rootlocalhost day03]# django-admi…...
C#学习系列之密闭类、接口、结构和类
C#学习系列之密闭类、接口、结构和类 啰嗦密闭类接口结构和类总结 啰嗦 基础学习 密闭类 类似string这种不想再继续继承和修改下去,使用sealed声明。 派生类中用sealed和override,无法继续重写。 接口 接口就是指定一组函数成员,而不实现…...

C++特殊类的设计
文章目录 设计一个类不能被拷贝请设计一个类,只能在堆上创建对象设计一个类只能在栈上去创建对象设计一个类不能被继承设计一个类,只能创建一个对象(单例模式)饿汉模式懒汉模式 单例模式总结饿汉模式懒汉模式 设计一个类不能被拷贝 拷贝一个类对象可以有…...

量化交易Copula建模应对市场低迷
一、简介 传统上,我们依靠相关矩阵来理解资产间的动态。然而,正如过去的市场崩盘所表明的那样,当风暴袭来时,许多模型都会陷入混乱。突然之间,相关性似乎趋于一致,而多样化这一经常被吹捧的风险管理口号似乎并没有提供什么庇护。 这种出乎意料的同步,即资产在经济低迷时…...
美创科技位居IDC MarketScape:中国数据安全管理平台市场「领导者」类别
近日,IDC发布《IDC MarketScape: 中国数据安全管理平台2023年厂商评估》 报告,报告从交付、产品特性、创新能力、研发速度、客户满意度等多个维度对国内厂商进行全面评估。美创科技列为『领导者』类别! ◼︎ 报告中,从关键战略指…...
Go语言变量的使用
基本语法——变量 一、变量的使用 1.1 什么是变量 变量是为存储特定类型的值而提供给内存位置的名称。在go中声明变量有多种语法。 所以变量的本质就是一小块内存,用于存储数据,在程序运行过程中数值可以改变 1.2 声明变量 var名称类型是声明单个变…...
在vitis中bit位赋值如何优化到一拍完成
使用vitis实现硬件代码时,经常遇到不是整拍对齐的情况,比如: ap_uint<128> a; ap_uint<64> b[10]; int pad,pos; /// 计算pos,pad ..... a(pos-1,0) b[pad](63,pos); a(63pos,pos) b[pad1]; a(127,64po…...

python学习day39
图像数据与显存 知识点回顾 1.图像数据的格式:灰度和彩色数据 2.模型的定义 3.显存占用的4种地方 a.模型参数梯度参数 b.优化器参数 c.数据批量所占显存 d.神经元输出中间状态 4.batchisize和训练的关系 import torch import torchvision import torch.nn as nn imp…...

前端异步编程全场景解读
前端异步编程是现代Web开发的核心,它解决了浏览器单线程执行带来的UI阻塞问题。以下从多个维度进行深度解析: 一、异步编程的核心概念 JavaScript的执行环境是单线程的,这意味着在同一时间只能执行一个任务。为了不阻塞主线程,J…...
湖北理元理律师事务所:债务清偿方案中的法律技术革新
文/金融法律研究组 当前债务服务市场存在结构性矛盾:债权人追求快速回款,债务人需要喘息空间。湖北理元理律师事务所通过创新法律技术,在《企业破产法》《民法典》框架下构建梯度清偿模型,实现多方利益平衡。 一、个人债务优化的…...

MySQL 数据库深度剖析:事务、SQL 优化、索引与 Buffer Pool
在当今数据驱动的时代,数据库作为数据存储与管理的核心,其性能与可靠性至关重要。MySQL 作为一款广泛使用的开源数据库,在众多应用场景中发挥着关键作用。在这篇博客中,我将围绕 MySQL 数据库的核心知识展开,涵盖事务及…...

【Ragflow】26.RagflowPlus(v0.4.0):完善解析逻辑/文档撰写模式全新升级
概述 在历经半个月的间歇性开发后,RagflowPlus再次迎来一轮升级,正式发布v0.4.0。 开源地址:https://github.com/zstar1003/ragflow-plus 更新方法 下载仓库最新代码: git clone https://github.com/zstar1003/ragflow-plus.…...

CSS(2)
文章目录 Emmet语法快速生成HTML结构语法 Snipaste快速生成CSS样式语法快速格式化代码 快捷键(VScode)CSS 的复合选择器什么是复合选择器交集选择器后代选择器(重要)子选择器(重要)并集选择器(重要)**链接伪类选择器**focus伪类选…...
Amazon RDS on AWS Outposts:解锁本地化云数据库的混合云新体验
在混合云架构成为企业数字化转型标配的今天,如何在本地数据中心享受云数据库的强大能力,同时满足数据本地化、低延迟访问的严苛需求?Amazon RDS on AWS Outposts 给出了完美答案——将AWS完全托管的云数据库服务无缝延伸至您的机房࿰…...

STM32CubeMX-H7-19-ESP8266通信(中)--单片机控制ESP8266实现TCP地址通信
前言 上篇文章我们已经能够使用串口助手实现esp8266的几种通信,接下来我们使用单片机控制实现。这篇文章会附带教程,增加.c和,.h,把串口和定时器放到对应的编号,然后调用初始化就可以使用了。 先讲解,然后末尾再放源码…...

在 Vue 的template中使用 Pug 的完整教程
在 Vue 的template中使用 Pug 的完整教程 引言 什么是 Pug? Pug(原名 Jade)是一种高效的网页模板引擎,通过缩进式语法和简洁的写法减少 HTML 的冗长代码。Pug 省略了尖括号和闭合标签,使用缩进定义结构,…...
Java编程之组合模式
引言 在软件开发的世界里,我们经常会遇到需要表示"部分-整体"层次结构的场景。比如文件系统中的文件和文件夹、图形界面中的各种组件、企业组织架构中的部门和员工等。这些场景都有一个共同的特点:我们需要以一种统一的方式来处理单个对象和由…...