当前位置: 当前位置:首页 >IT科技 >抛弃AOP!SpringBoot + YAML 零侵入数据脱敏神操作! 正文

抛弃AOP!SpringBoot + YAML 零侵入数据脱敏神操作!

2025-11-04 19:14:10 来源:多维IT资讯作者:数据库 点击:346次

兄弟们,抛弃今天咱们来聊个老生常谈但又总让人头疼的侵入话题 —— 数据脱敏。先跟大家唠唠我之前踩过的数据神操坑啊:去年做一个用户中心项目,产品经理拍着桌子说 “用户手机号、脱敏身份证号必须脱敏!抛弃日志里不能有明文,侵入接口返回也不能漏!数据神操”。脱敏我当时一拍胸脯 “小意思,抛弃AOP 搞定!侵入”,数据神操结果呢?脱敏

写了个@SensitiveField注解,又搞了个切面拦截 Controller 返回值,抛弃用反射遍历字段处理。侵入一开始挺顺利,数据神操直到遇到嵌套对象 —— 比如User里套了个Address,Address里有个contactPhone要脱敏,我那切面直接懵了,递归反射写了三层才搞定;后来又遇到集合,List<User>得循环处理每个元素,代码越改越乱,最后切面里全是 if-else,跟个迷宫似的。

抛弃AOP!SpringBoot + YAML 零侵入数据脱敏神操作!

更坑的是上线后,运维说 “你这接口响应慢了 100ms”,查了半天发现是反射次数太多,尤其是服务器托管高并发的时候,CPU 占用直接上去了。当时我就想:就不能有个不用写切面、不用改业务代码,甚至连实体类都不用动的脱敏方案吗?

还真让我找到了!今天就给大家分享这个 “偷懒神器”——SpringBoot + YAML 零侵入数据脱敏方案。不用 AOP,不用加注解,改改配置文件就能搞定,新手看一遍也能上手,看完你绝对想收藏!

一、先搞懂:为啥要做数据脱敏?别等踩坑才后悔

在讲方案之前,先跟没接触过脱敏的兄弟补补课 —— 别觉得脱敏是 “多此一举”,等出了问题你就知道有多重要了。

举个真实案例:前两年某电商平台,开发在日志里打印了用户的银行卡号(明文),结果被黑客通过日志漏洞爬走了,最后不仅赔了用户钱,还被监管罚了几百万。你说这亏不亏?

咱们日常开发里,需要脱敏的场景主要有 3 个:

接口返回:给前端返回用户信息时,手机号不能是13800138000,得是1388000;身份证号不能是110101199001011234,亿华云计算得是110101****1234日志打印:不管是业务日志还是异常日志,只要有敏感信息,必须脱敏,不然日志文件就是 “定时炸弹”数据库存储:这个得区分情况 —— 像手机号、邮箱可以存明文(但响应和日志要脱敏),但银行卡号、身份证号这种高敏感信息,数据库里最好存加密后的结果,脱敏只负责 “前端展示”

简单说:脱敏的核心是 “该看的人能看,不该看的人看不到”,既保证用户信息安全,又不影响业务正常运行。

之前用 AOP 做脱敏,虽然能实现功能,但有 3 个致命问题:

侵入性强:得给实体类加注解,改业务代码,万一后续要改脱敏规则,牵一发动全身代码复杂:处理嵌套对象、集合、基本类型,反射逻辑写得头晕,还容易出 bug性能拉胯:反射次数多,高并发场景下接口响应变慢,CPU 占用飙升

而今天要讲的源码库方案,完美解决这 3 个问题 ——零侵入、配置化、轻量级,咱们一步步来拆解。

二、核心原理:SpringBoot 自带的 “响应拦截神器”,比 AOP 更轻

很多兄弟不知道,SpringMVC 里有个叫ResponseBodyAdvice的接口,它能在 “响应体返回给前端之前” 拦截处理,相当于给响应加了个 “过滤器”。

咱们之前用 AOP,还得自己写切面、定义切点(比如拦截所有@RestController的方法),而ResponseBodyAdvice是 Spring 官方提供的扩展点,不用处理复杂的切面表达式,也不用考虑拦截顺序,比 AOP 更简单、更轻量。

举个通俗的例子:如果把接口响应比作 “快递”,ResponseBodyAdvice就是 “快递分拣员”,在快递送到用户(前端)手里之前,先检查一下里面有没有 “敏感物品”(敏感字段),有就按规则 “包装一下”(脱敏),再送出去。

整个方案的核心逻辑就是:

用ResponseBodyAdvice拦截所有接口响应从 YAML 配置里读取 “哪些接口、哪些字段需要脱敏”对响应体里的敏感字段按规则处理把处理后的响应体返回给前端

全程不用改业务代码,不用加注解,所有规则都在 YAML 里配置,这就是 “零侵入” 的关键!

三、实战步骤:从 0 到 1 实现,复制代码就能用

咱们先定个目标:实现两个接口的脱敏需求

接口 1:GET /api/user/get,返回用户信息,需要脱敏phone(手机号)和idCard(身份证号)接口 2:POST /api/order/list,返回订单列表,需要脱敏bankCard(银行卡号)和user.phone(嵌套字段)

环境准备:JDK 1.8+,SpringBoot 2.7.x(其他版本也能用,差别不大)

第一步:引入依赖,就两个,不多加

首先创建一个 SpringBoot 项目,然后在pom.xml里加两个依赖:

spring-boot-starter-web:必备,不用多说hutool-all:国产工具包,里面有很多现成的脱敏方法,省得咱们自己写正则 复制<dependencies> <!-- SpringBoot Web核心依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Hutool工具包:简化脱敏、字符串处理 --> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.20</version> <!-- 用最新版就行 --> </dependency> <!-- 可选:如果用Lombok,加这个,能少写getter/setter --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> </dependencies>1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.

Hutool 不是必须的,如果你不想引入第三方包,自己写正则也能实现脱敏,后面会讲怎么自定义。

第二步:写 YAML 配置,脱敏规则全在这里定

最关键的一步来了!咱们在application.yml里配置脱敏规则,不用改任何 Java 代码。

先看配置结构,我都加了注释,一看就懂:

复制# 应用基础配置 spring: application: name: sensitive-demo # 多环境配置:开发环境可以关闭脱敏,方便调试 profiles: active: dev # 脱敏核心配置:dev环境(开发) --- spring: config: activate: on-profile: dev # 开发环境关闭脱敏,方便调试接口,看明文数据 sensitive: enabled: false # 脱敏核心配置:prod环境(生产) --- spring: config: activate: on-profile: prod sensitive: enabled: true # 生产环境开启脱敏 # 接口脱敏映射:按接口配置需要脱敏的字段 mappings: # 第一个接口:获取用户信息 - path: /api/user/get method: GET # 请求方法:GET/POST/PUT/DELETE,不区分大小写 fields: # 需要脱敏的字段 - name: phone # 字段名:对应响应体里的phone字段 rule: mobile # 脱敏规则:mobile(手机号) - name: idCard # 字段名:身份证号 rule: idCard # 脱敏规则:idCard(身份证号) # 第二个接口:获取订单列表 - path: /api/order/list method: POST fields: - name: bankCard # 字段名:银行卡号 rule: bankCard # 脱敏规则:bankCard(银行卡号) - name: user.phone # 嵌套字段:order里的user对象的phone字段 rule: mobile # 可以继续加更多接口... # 自定义脱敏规则:如果Hutool的规则不够用,自己加 custom-rules: # 比如自定义邮箱脱敏规则:zhangsan@163.com → zh****@163.com - name: email regex: "([a-zA-Z0-9_]{2})[a-zA-Z0-9_]*@([a-zA-Z0-9.]+)" # 正则表达式 replacement: "$1****@$2" # 替换规则:$1是第一个分组,$2是第二个分组1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.

这里有几个关键点要说明:

多环境区分:开发环境(dev)关闭脱敏,方便调试;生产环境(prod)开启,保证安全。不用每次改代码,切换环境就行。接口映射:每个mapping对应一个接口,path是接口路径,method是请求方法,fields是需要脱敏的字段。嵌套字段:支持user.phone这种嵌套字段,不管嵌套多少层,用 “.” 分隔就行。脱敏规则:内置了mobile、idCard、bankCard三种规则(后面会讲怎么实现),还支持自定义规则(比如上面的email)。

第三步:把 YAML 配置映射成 Java 对象

SpringBoot 不能直接读取 YAML 里的复杂结构(比如mappings列表),所以咱们要写个配置类,把 YAML 配置映射成 Java 对象,方便后续使用。

用@ConfigurationProperties注解就能实现,代码很简单:

复制import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; import java.util.List; /** * 脱敏配置类:把YAML里的sensitive配置映射成Java对象 */ @Component @ConfigurationProperties(prefix = "sensitive") // 对应YAML里的sensitive节点 @Data // Lombok注解,省得写getter/setter public class SensitiveProperties { /** * 是否开启脱敏功能:true=开启,false=关闭 */ private boolean enabled = false; /** * 接口脱敏映射列表 */ private List<SensitiveMapping> mappings; /** * 自定义脱敏规则列表 */ private List<CustomRule> customRules; /** * 单个接口的脱敏配置 */ @Data public static class SensitiveMapping { /** * 接口路径:比如/api/user/get */ private String path; /** * 请求方法:GET/POST/PUT/DELETE,不区分大小写 */ private String method; /** * 该接口需要脱敏的字段列表 */ private List<SensitiveField> fields; } /** * 单个字段的脱敏配置 */ @Data public static class SensitiveField { /** * 字段名:支持嵌套字段,比如user.phone */ private String name; /** * 脱敏规则:比如mobile、idCard、bankCard,或自定义规则名 */ private String rule; } /** * 自定义脱敏规则 */ @Data public static class CustomRule { /** * 规则名:比如email,在fields.rule里引用 */ private String name; /** * 正则表达式:用来匹配敏感字段 */ private String regex; /** * 替换规则:比如$1****@$2 */ private String replacement; } }1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.51.52.53.54.55.56.57.58.59.60.61.62.63.64.65.66.67.68.69.70.71.72.73.74.

这里用了 Lombok 的@Data注解,如果你没加 Lombok 依赖,自己写 getter 和 setter 就行,功能一样。

第四步:实现脱敏工具类,规则全在这里

接下来写个脱敏工具类,负责实现具体的脱敏逻辑 —— 包括内置规则(手机号、身份证号、银行卡号)和自定义规则(从 YAML 里读)。

咱们用 Hutool 的DesensitizedUtil来实现内置规则,省得自己写正则,效率更高:

复制import cn.hutool.core.util.DesensitizedUtil; import cn.hutool.core.util.StrUtil; import org.springframework.stereotype.Component; import javax.annotation.Resource; import java.util.HashMap; import java.util.Map; import java.util.regex.Pattern; /** * 脱敏工具类:实现各种脱敏规则 */ @Component public class SensitiveUtil { /** * 自定义脱敏规则缓存:key=规则名,value=正则Pattern */ private final Map<String, Pattern> customRulePatterns = new HashMap<>(); /** * 自定义脱敏替换规则缓存:key=规则名,value=替换字符串 */ private final Map<String, String> customRuleReplacements = new HashMap<>(); @Resource private SensitiveProperties sensitiveProperties; /** * 初始化:把YAML里的自定义规则加载到缓存 */ public void init() { if (sensitiveProperties.getCustomRules() == null) { return; } // 遍历自定义规则,编译正则表达式并缓存 for (SensitiveProperties.CustomRule customRule : sensitiveProperties.getCustomRules()) { customRulePatterns.put( customRule.getName(), Pattern.compile(customRule.getRegex()) ); customRuleReplacements.put( customRule.getName(), customRule.getReplacement() ); } } /** * 核心方法:根据规则脱敏字符串 * @param value 原始字符串(比如手机号13800138000) * @param rule 脱敏规则(比如mobile) * @return 脱敏后的字符串(比如138****8000) */ public String desensitize(String value, String rule) { // 1. 空值直接返回,避免空指针 if (StrUtil.isBlank(value)) { return value; } // 2. 处理内置规则 switch (rule.toLowerCase()) { case "mobile": // 手机号脱敏:138****8000 return DesensitizedUtil.mobilePhone(value); case "idcard": // 身份证号脱敏:110101

作者:IT科技类资讯
------分隔线----------------------------
头条新闻
图片新闻
新闻排行榜