如何在SpringBoot项目上让接口返回数据脱敏,一个注解即可

news/2024/4/19 14:48:20/文章来源:https://blog.csdn.net/qq_37284798/article/details/129118284

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接口请求,效果符合预期,如图:

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

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

相关文章

【linux】——gcc/g++,make/makefile的简单使用

目录 1.gcc的基本使用 2.Linux下的静态库和动态库的理解 3.Linux项目自动化构建工具——make/makefile 1.gcc的基本使用 gcc是专门用来编译c语言的 g是专门用来编译c的&#xff0c;但是g也能够用来编译c语言 预处理&#xff08;进行宏替换&#xff09; 预处理功能主要包括宏…

【前端提效】-- VsCode 实用插件推荐

EditorConfig for VS Code ***** 作用&#xff1a;多人协同开发&#xff0c;规范缩进风格&#xff0c;缩进大小&#xff0c;tab长度以及字符集等&#xff0c;解决不同IDE的编码范设置&#xff0c;在这里配置&#xff08;.editorconfig&#xff09;的代码规范规则优先级高于编辑…

java诊断与调优常用命令jmap、jstack、jstat使用实战

java应用运行过程中难免会出现问题&#xff0c;特别是在生产环境&#xff0c;发生异常或宕机情况&#xff0c;需要诊断与分析&#xff0c;定位原因&#xff0c;进行优化&#xff0c;避免下次再次出现问题。 虽然现在有很多可视化工具&#xff0c;使用起来比命令行更方便&#x…

Julia 语言环境安装

Julia 语言支持以下系统&#xff1a; LinuxFreeBSDmacOSWindowsAndroid Julia 安装包下载地址为&#xff1a;Download Julia。 Github 源码地址&#xff1a;GitHub - JuliaLang/julia: The Julia Programming Language。 国内镜像地址&#xff1a;Index of /julia-releases/…

逻辑回归—二元分类问题的操作顺序

对于二元分类问题来说&#xff0c;分类的结果和数据的特征之间仍呈现相关关系&#xff0c;但是y的值不再是连续的&#xff0c;是0&#xff5e;1的跃迁。但是在这个过程中&#xff0c;什么仍然是连续的呢&#xff1f;”是概率&#xff0c;概率是逐渐升高的&#xff0c;当达到一个…

AI制药 - TMScore(US-align)、RMSD、Sequence 源码

欢迎关注我的CSDN:https://spike.blog.csdn.net/ 本文地址:https://blog.csdn.net/caroline_wendy/article/details/129125467 参考文档:Nature Methods | 蛋白、RNA、DNA及其复合物结构的比对算法US-align 官网地址:https://zhanggroup.org/US-align/ TMScore TMScore,…

文件系统与动静态库的基本了解

目录文件系统与动静态库的基本了解文件系统了解Access Modify Changeinode硬链接软链接静态库与动态库概念静态库的制作使用静态库动态库的制作使用动态库总结如何制作文件系统与动静态库的基本了解 文件系统 了解Access Modify Change 当文件没有被打开时&#xff0c;他们存…

数据挖掘,计算机网络、操作系统刷题笔记50

数据挖掘&#xff0c;计算机网络、操作系统刷题笔记50 2022找工作是学历、能力和运气的超强结合体&#xff0c;遇到寒冬&#xff0c;大厂不招人&#xff0c;可能很多算法学生都得去找开发&#xff0c;测开 测开的话&#xff0c;你就得学数据库&#xff0c;sql&#xff0c;orac…

(考研湖科大教书匠计算机网络)第五章传输层-第八节1:TCP连接管理理论部分(三次握手与四次挥手)

获取pdf&#xff1a;密码7281专栏目录首页&#xff1a;【专栏必读】考研湖科大教书匠计算机网络笔记导航此部分内容借鉴博主【小林coding】 &#xff0c;其对计算机网络内容的图解可以说是深入浅出&#xff0c;尤其是三次握手和四次挥手这一部分&#xff0c;堪称全网最佳。所这…

webpack5打包工具的使用

目录 -----------------------------基础篇------------------------------- 一、为什么需要打包工具 二、基本使用 1、模式 2、使用步骤 三、基本配置 1、五大核心概念 2、准备 Webpack 配置文件 3、运行指令 四、开发模式 五、处理样式资源 1、处理CSS资源 2、处…

100份简历才找一个合适的,2023,软件测试岗位饱和了吗?

各大互联网公司的接连裁员&#xff0c;政策限制的行业接连消失&#xff0c;让今年的求职雪上加霜&#xff0c;想躺平却没有资本&#xff0c;还有人说软件测试岗位饱和了&#xff0c;对此很多求职者深信不疑&#xff0c;因为投出去的简历回复的越来越少了。 另一面企业招人真的…

闪光桐人の实习日记(2023年2月20-27日)

前往闪闪の小窝以获得更好的阅读和评论体验 文章目录2023年2月20日&#xff08;Vue入门&#xff09;概念Vue基础Vue中的MVVMVue的体验Vue的生命周期Vue指令Vue组件VueRouter前后端路由的区别工作原理两种模式比较route跟router的区别路由属性导航守卫Vuex概述5种基本对象基本使…

Qt线程QThread详解

目录前言1.QThread介绍2.QThread示例一3.QThread示例二4.线程同步前言 在程序中使用线程可以提高程序的性能、并发性、响应性和稳定性&#xff0c;使得程序设计更加灵活和简单。但是&#xff0c;线程编程也有一些挑战&#xff0c;如线程安全性和死锁等问题需要格外注意。我们使…

【1】linux命令每日分享——mkdir

大家好&#xff0c;这里是sdust-vrlab&#xff0c;Linux是一种免费使用和自由传播的类UNIX操作系统&#xff0c;Linux的基本思想有两点&#xff1a;一切都是文件&#xff1b;每个文件都有确定的用途&#xff1b;linux涉及到IT行业的方方面面&#xff0c;在我们日常的学习中&…

【机器学习】决策树-ID3算法

1.ID3算法 ID3算法利用信息增益进行特征的选择进行树的构建。信息熵的取值范围为0~1&#xff0c;值越大&#xff0c;越不纯&#xff0c;相反值越小&#xff0c;代表集合纯度越高。信息增益反映的是给定条件后不确定性减少的程度。每一次对决策树进行分叉选取属性的时候&#x…

网络计划--时间参数的计算和优化

根据网络图的基本概念和原则绘制出网络图之后&#xff0c;我们可以计算网络图中有关的时间参数&#xff0c;主要目的是找出关键路线&#xff0c;为网络计划的优化、调整和执行提供明确的时间概念。如下图中从始点①到终点⑧共有4条路线&#xff0c;可以分别计算出每条路线所需的…

使用maven搭建父子工程项目

创建父子工程&#xff0c;可以通过父工程来引入jar&#xff0c;定义统一的版本号等。更方便对整个项目的jar包实现统一化管理&#xff0c;让项目的层次更加清晰。一、创建父工程第一步&#xff1a;file–>new–>project–>maven默认使用jdk1.8&#xff0c;不引入任何j…

音视频基础之视频主要概念

视频主要概念 **视频码率&#xff1a;**kb/s&#xff0c;是指视频文件在单位时间内使用的数据流量&#xff0c;也叫码流率。码率越大&#xff0c;说明单位时间内取样率越大&#xff0c;数据流精度就越高。 **视频帧率&#xff1a;**fps&#xff0c;通常说一个视频的25帧&…

ur3+robotiq ft sensor+robotiq 2f 140配置rviz仿真环境

ur3robotiq ft sensorrobotiq 2f 140配置rviz仿真环境 搭建环境&#xff1a; ubuntu: 20.04 ros: Nonetic sensor: robotiq_ft300 gripper: robotiq_2f_140_gripper UR: UR3 在安装sensor和gripper之前&#xff0c;先简单配置一下UR机械臂的仿真环境&#xff0c;可参考这篇博…

jenkins下载与简单使用

1.jenkins下载 因为我仍然使用的是jdk1.8进行开发&#xff0c;所以我下载的是jenkins2.332.1版本&#xff08;jenkins2.346.1版本在2022年末不再支持java8&#xff0c;如果项目使用的是jdk11可以继续使用该jenkins版本&#xff09;&#xff0c;更多版本下载请点击jenkins下载 …