当前位置: 首页 > news >正文

一个注解让 Spring Boot 项目接口返回数据脱敏

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 {
        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 {
     
        @Override
        public 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
            @Primary
            ObjectMapper 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 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接口请求,效果符合预期,如图:

————————————————
版权声明:本文为CSDN博主「吴名氏.」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_37284798/article/details/129118284

相关文章:

一个注解让 Spring Boot 项目接口返回数据脱敏

1 背景 需求是某些接口返回的信息&#xff0c;涉及到敏感数据的必须进行脱敏操作 2 思路 ①要做成可配置多策略的脱敏操作&#xff0c;要不然一个个接口进行脱敏操作&#xff0c;重复的工作量太多&#xff0c;很显然违背了“多写一行算我输”的程序员规范。思来想去&#xff…...

测试人员的KPI怎么设置

关于测试部的KPI&#xff0c;简单列举下自己所经历部门的考核指标&#xff1a; 工作量。根据平时跟踪的需求&#xff0c;编写的用例&#xff0c;提交的bug数等综合评估&#xff1b; 负责项目的质量。上线项目是否出现重大功能的事故&#xff0c; 如果出现了事故&#xff0c;分…...

Databend 开源周报第 116 期

Databend 是一款现代云数仓。专为弹性和高效设计&#xff0c;为您的大规模分析需求保驾护航。自由且开源。即刻体验云服务&#xff1a;https://app.databend.cn 。 Whats On In Databend 探索 Databend 本周新进展&#xff0c;遇到更贴近你心意的 Databend 。 特性预览&#…...

mongodb-gridfs下载文件报Sort exceeded memory limit of 104857600 bytes异常

报错详细信息 com.mongodb.MongoQueryException: Query failed with error code 292 and error message Executor error during find command :: caused by :: Sort exceeded memory limit of 104857600 bytes, but did not opt in to external sorting. on server 11.51.141.…...

分享一下微信小程序里怎么实现扫码点餐链接

在当今数字化时代&#xff0c;扫码点餐已经成为了餐饮行业的一种趋势。通过微信小程序&#xff0c;实现扫码点餐功能&#xff0c;可以为餐厅带来诸多便利和优势。本文将详细介绍如何在微信小程序中实现扫码点餐功能&#xff0c;帮助餐厅提高服务效率和质量&#xff0c;提升用户…...

安卓开发环境安装教程

在本教程中&#xff0c;我将向您介绍如何在Windows操作系统上安装Android开发环境。Android开发环境包括Java Development Kit&#xff08;JDK&#xff09;&#xff0c;Android Studio IDE和相应的SDK工具。跟随以下步骤&#xff0c;您将能够搭建安卓开发环境并开始开发自己的应…...

深入探究Selenium定位技巧及最佳实践

在使用Selenium进行Web自动化测试时&#xff0c;准确地定位元素是非常重要的一步。Selenium提供了多种元素定位方法&#xff0c;本文将深入探究这八大元素定位方法&#xff0c;帮助读者更好地理解和应用Selenium的定位技巧。 1. ID定位 ID是元素在HTML中的唯一标识符&#xff…...

如何正确安装psycopg2,No module named ‘psycopg2._psycopg‘解决

二、psycopg2安装方法 psycopg2可以通过多种方式安装&#xff0c;我们这里介绍两种常用的方式&#xff1a;通过pip安装和手动安装。 1、通过pip安装psycopg2 如果已经安装了pip&#xff0c;那么在命令行中输入以下命令即可完成psycopg2的安装&#xff1a; pip install psyc…...

go WriteFile文件追加写入(适合小文件)

go 在做文件追加写入时一般用os.OpenFile 指定 FileMode 为 os.O_APPEND. 如官方文档示例: f, err : os.OpenFile("access.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)if err ! nil {log.Fatal(err)}if _, err : f.Write([]byte("appended some data\n&…...

history 模式上线需要注意什么事项?

结论先行&#xff1a; 首先&#xff0c;需要在服务器上对所有的路由路径进行配置&#xff0c;避免在访问路由时出现 404 的情况&#xff1b; 其次&#xff0c;需要特别注意安全性和兼容性问题。 因为使用 History 模式会暴露出服务器上的文件路径&#xff0c;因此在部署时需要…...

VMware虚拟机安装Ubuntu22.04教程(2023最新最详细)

目录 简介 1 VMware虚拟机下载与安装 2 Ubuntu操作系统安装与配置 2.1 Ubuntu虚拟机配置 2.2 Ubuntu操作系统安装 简介 Linux是一种自由和开放源代码的操作系统内核&#xff0c;被广泛应用于各种计算机系统中。它以稳定性、安全性和灵活性而闻名&#xff0c;并成为服务器…...

yakit使用爆破编码明文_dnslog使用

yakit使用爆破编码密码 文章目录 yakit使用爆破编码密码yakit使用1 yakit编码密码进行爆破2 准备eval.php文件放入web3 访问http://192.168.225.206/eval.php,使用bp抓包,测试后环境准本好4 使用yakit4.1 进入页面&#xff0c;点击这里进行配置默认端口80834.2 发送到模糊测试4…...

3分钟教你用Python+Appium实现自动化测试

一、环境准备 1.脚本语言&#xff1a;Python3.x IDE&#xff1a;安装Pycharm 2.安装Java JDK 、Android SDK 3.adb环境&#xff0c;path添加E:\Software\Android_SDK\platform-tools 4.安装Appium for windows&#xff0c;官网地址 Redirecting 点击下载按钮会到GitHub…...

qt的一些自绘控件

https://download.csdn.net/download/venice0708/88469835...

类图表示法

设计模式&#xff0c;用设计图表示的话&#xff0c;主要用到类图。常见UML类图如下&#xff1a; 1、类图&#xff1a;矩形框&#xff0c;代表一个类&#xff08;Class&#xff09;。类图分为三层&#xff0c;第一层显示类的名称&#xff0c;如果是抽象类&#xff0c;则用斜体显…...

大模型训练框架

一文搞定分布式训练&#xff1a;dataparallel、distirbuted、deepspeed、accelerate、transformers、horovod - 知乎代码地址&#xff1a;taishan1994/pytorch-distributed-NLP: pytorch分布式训练 (github.com)pytorch-distributed-NLPpytorch单机多卡分布式训练-中文文本分类…...

好用的Visio绘图文件工具 VSD Viewer最新 for mac

VSD Viewer是一款可以查看Microsoft Visio绘图文件的工具&#xff0c;适用于Windows和macOS操作系统。它具有以下优点&#xff1a; 直观易用&#xff1a;VSD Viewer的用户界面非常简单直观&#xff0c;易于使用。支持多种文件格式&#xff1a;VSD Viewer支持多种Visio文件格式…...

三代自动驾驶系统及主流科技公司自动驾驶技术方案简介

截止目前&#xff0c;按技术特点&#xff0c;自动驾驶技术大致经历了三代发展&#xff1a;第一代自动驾驶技术以后融合感知技术&#xff0c;高精度地图&#xff0c;基于惯导、GPS定位系统&#xff0c;预测模块&#xff0c;基于优化、搜索的规控等组成。第一代比较成熟的自动驾驶…...

mac安装nodejs,跑vue程序

1. 下载node.js for mac&#xff0c;地址&#xff1a;Node.js。一路安装就可以了&#xff0c;无需修改。 2. mac终端&#xff0c;查看node和npm的版本。 3. 配置环境变量&#xff0c; vim .bash_profile增加PATH$PATH:/usr/local/bin/ 4. 但是毕竟npm安装一些东西还是太慢了所…...

VC++程序崩溃时,使用Visual Studio静态分析dump文件

1、通过Visual Studio直接把Dump文件打开 2、点击【仅限本机进行调试】&#xff0c;启动Dump 3.1、本机调试启动后&#xff0c;如果程序运行模块和pdb文件在同一个目录的&#xff0c;直接定位到异常代码行 3.2、如果显示找不到pdb文件&#xff0c;则需要通过【新建路径】设置…...

AI行业的“隐形赛道”:AI伦理与合规人才缺口到底有多大

一、AI狂飙下的“隐形刚需”&#xff1a;被忽视的伦理与合规赛道当软件测试从业者还在为功能测试、性能测试的技术迭代焦头烂额时&#xff0c;AI行业的另一股暗流正汹涌袭来——伦理与合规人才的缺口&#xff0c;正成为制约AI产业可持续发展的隐形瓶颈。从ChatGPT引发生成式AI热…...

Unity加载倾斜摄影模型踩坑记:从3MX/OSGB文件到流畅渲染,我解决了这几个问题

Unity倾斜摄影模型加载实战&#xff1a;从3MX/OSGB到跨平台渲染的深度解决方案 第一次在Unity中加载倾斜摄影模型时&#xff0c;那种期待和忐忑交织的心情至今难忘。作为智慧城市项目的核心展示环节&#xff0c;我们需要将航拍生成的3MX和OSGB格式模型无缝集成到Unity场景中。本…...

终极Windows APK安装器:3分钟学会在电脑上安装Android应用

终极Windows APK安装器&#xff1a;3分钟学会在电脑上安装Android应用 【免费下载链接】APK-Installer An Android Application Installer for Windows 项目地址: https://gitcode.com/GitHub_Trending/ap/APK-Installer 你是否想在Windows电脑上直接运行Android应用&am…...

SillyTavern角色卡片系统:从图片到智能伙伴的魔法之旅

SillyTavern角色卡片系统&#xff1a;从图片到智能伙伴的魔法之旅 【免费下载链接】SillyTavern LLM Frontend for Power Users. 项目地址: https://gitcode.com/GitHub_Trending/si/SillyTavern 你是否曾想过&#xff0c;一张普通的图片如何能变成一个会思考、会对话、…...

Multi-head Self-Attention Machanism

3. 多头自注意力机制&#xff08;Multi-head Self-Attention Machanism&#xff09; 多头注意力机制是在自注意力机制的基础上发展起来的&#xff0c;是自注意力机制的变体&#xff0c;旨在增强模型的表达能力和泛化能力。它通过使用多个独立的注意力头&#xff0c;分别计算注…...

Spring AI + Ollama 深度实战:从 RAG 问答到 Graph Agent 全流程指南

场景 Spring AI RAG 检索增强生成&#xff1a;概念、实战与完整代码&#xff1a; https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/161055108 基于上面的基础&#xff0c;实现Graph工作流编排的简单示例。 大语言模型&#xff08;LLM&#xff09;在实际应用中面…...

巅峰共鸣,实力同频|盖茨中国热烈祝贺张雪机车WSBK捷克站双冠耀世,改写37年垄断史!

引擎轰鸣震彻赛道&#xff0c;中国红闪耀世界舞台&#xff01;2026 年 5 月 17 日&#xff0c;WSBK 捷克莫斯特站 WorldSSP 组别圆满落幕&#xff0c;中国品牌张雪机车再创历史&#xff0c;车手 Valentin Debise 驾驶自研 ZX820RR 赛车&#xff0c;包揽两回合冠军&#xff0c;斩…...

3种智能解析技术:VideoDownloadHelper如何突破网页视频下载限制

3种智能解析技术&#xff1a;VideoDownloadHelper如何突破网页视频下载限制 【免费下载链接】VideoDownloadHelper Chrome Extension to Help Download Video for Some Video Sites. 项目地址: https://gitcode.com/gh_mirrors/vi/VideoDownloadHelper 在当今数字内容爆…...

第七届先进金属材料国际研讨会(AMM 2026)

第七届先进金属材料国际研讨会(AMM 2026) The 7th Intl Conference on Advanced Metallic Materials(AMM 2026) 2026年8月7-9日 中国昆明 &#x1f4c5; 重要信息 会议官网&#xff1a;https://www.academicx.org/AMM/2026/ 会议时间&#xff1a;2026年8月7-9日 会议地点…...

CodeDroidAI:本地化AI代码助手的设计原理与工程实践

1. 项目概述&#xff1a;一个面向开发者的AI代码助手最近在GitHub上看到一个挺有意思的项目&#xff0c;叫“FMXExpress/CodeDroidAI”。光看这个名字&#xff0c;可能有点摸不着头脑&#xff0c;但如果你是个经常和代码打交道的开发者&#xff0c;尤其是对提升编码效率、探索A…...