96SEO 2026-02-19 18:56 20
。

所以现在的业务系统开发中都会对用户隐私数据加密之后存储落库#xff0c;同时还要求后端返回数据给前台之前进行数据脱敏。
所谓脱敏处理其实就…1.背景
在当下互联网高速发展的时代下涉及到用户的隐私数据安全越发重要一旦泄露将造成不可估量的后果。
所以现在的业务系统开发中都会对用户隐私数据加密之后存储落库同时还要求后端返回数据给前台之前进行数据脱敏。
所谓脱敏处理其实就是将数据进行混淆隐藏如将用户的手机号脱敏展示为178****5939采用
进行隐藏以免泄露个人隐私信息。
其实我之前就总结过相关联的功能实现《实现数据加密存储、模糊匹配和脱敏》
但是强调一下今天这里并不是重复再讲一遍而是在之前总结的基础上进行延伸拓展重点在于标题里的动态灵活可配置。
那什么是动态灵活可配置呢且听我娓娓道来。
那是一个惬意的下午时光我听着歌敲着一手代码…突然间产品就来到我桌前打断了我短暂的文思泉涌高光时刻企图给我安排一个折磨的活打开了奶茶app暗示没有一杯奶茶解决不了的需求。
是的最后这活我接了也就是我们今天所要讲的数据脱敏由之前的前端脱敏改为后端当然脱敏功能本身并不复杂难实现那为啥说它是折磨人的活呢普通的脱敏功能大概是这样的也就是脱敏上面所说的用户固定隐私数据姓名、手机号、身份证号、地址、身份证...等但是我们的系统需求要求不只是前面的字段还需要支持其他字段一句话生动形象地总结概括就是客户想脱啥就脱啥想在哪脱就在哪脱包括我们系统支持的自定义字段都可以通过配置进行脱敏如下图所示
哪些字段可以脱敏是可配置的脱敏规则也是可配置的比如说什么开头、中间、结尾区间啥的。
脱敏设计到组织架构过滤也就是说需要实现某个部门下的用户看到的数据是脱敏的某个部门下的用户看到的数据是不脱敏的。
这感觉就像后端接口功能菜单权限检验判断当前用户是否有调用某个功能菜单接口的权限。
涉及到角色的判断某些角色需要脱敏(小喽啰不给看防止把客户数据卖了)而某些角色不需要(管理员随便看随便卖)。
这里我们暂且不讨论脱敏功能这么设计是否合理反正产品是这样要求实现的按照上面的列出来的点感觉也还好也不至于上升到折磨的程度折磨的是系统的历史原因要脱敏的字段信息遍布都整个业务系统表单每个表单的接口数据有冗余的有共用的这就意味着每个页面表单接口都需要去梳理一遍。
同时脱敏字段还涉及到编辑更新功能而且之前的更新接口都是一个表单整体提交这就导致一个脱敏字段没有修改但是前端把脱敏数据传回后端来又是一个一个去适配啊~~~难顶。
闹骚发完了言归正传我们接下来看看是如何优雅地实现这个难顶的功能需求。
所谓”优雅“就是多写一行代码都算我输…所以在接口controller层返回之前一个一个地进行脱敏操作是不可取的重复的工作量太多。
思来想去肯定是需要通过切面思想去解决也就是通过对接口返回的VO类需要脱敏的字段使用注解打上标识然后切面统一逻辑处理这时候想到之前总结的接口响应结果结构统一封装返回ControllerAdvice
Boot如何优雅实现结果统一封装和异常统一处理》自行查看但我们发现使用ControllerAdvice意味着每次调接口都需要我们自己去反射类获取注解判断字段是否需要脱敏当返回对象比较复杂需要递归去反射性能一下子就会降低。
反射这个东西确实是框架的灵魂但是在业务接口中用多了也的确对性能有一定影响所以再三斟酌于是换种了中思路我们想到了平时使用的JsonFormat跟我们现在的需求功能场景很类似通过自定义注解跟字段解析器对字段进行自定义解析。
recycleDate;这样就可以把字段recycleDate转换为日期格式不需要我们单独代码处理这就是优雅按照这个思路我们首先需要定义一个注解进行脱敏字段标注
Retention(RetentionPolicy.RUNTIME)
}这个注解很简单只有一个默认属性value()指定字段的脱敏枚举类型MaskEnum
Retention(RetentionPolicy.RUNTIME)运行时生效。
Target(ElementType.FIELD)可用在字段上。
JacksonAnnotationsInside此注解可以点进去看一下是一个元注解主要是用户打包其他注解一起使用。
MaskSerializer.class)该注解的作用就是可自定义序列化可以用在注解上方法上字段上类上运行时生效等等根据提供的序列化类里面的重写方法实现自定义序列化。
关于MaskSerializer就是我们自定义序列化实现脱敏的核心实现所在后面会详细分析。
从注解定义可以我们给脱敏字段使用注解打标识时需要指定该字段脱敏类型是什么所以接下来我们还需要定义脱敏类型枚举类我们系统大概分为两类系统字段如(姓名手机号身份证号…等等)自定义字段(婚姻状况月供金额这些额外信息会存储在数据库一个JSON字段里)。
}注解和脱敏类型我们都定义好看样子万事俱备只欠东风啦我们只需要接下来自定义实现序列化解析器完成脱敏即可但是你可能忘了文章开头一直强调的动态灵活我们的脱敏配置信息不是固定的而是动态配置保存的这就意味着一个接口的某个字段上一次调用还需要脱敏紧接着脱敏配置被改了再调接口该字段就不再需要脱敏要求我们做到动态的同时还要保证实时性。
这如同需要判断功能菜单权限那样通过切面实现判断是否需要脱敏将脱敏配置信息上下文贯穿整次请求这里我们在登录认证的过滤器中实现因为脱敏配置涉及到角色、组织架构自然是要登录之后才能进行是否需要脱敏判断。
stringRedisTemplate;Overridepublic
登录验证通过之后就可以登录用户的公司id设置脱敏配置上下文String
stringRedisTemplate.opsForValue().get(KeyCache.ORG_DESENSITIZATION_SETTING
maskSetting.getDepTeamIds();Boolean
maskSetting.getUserLevelSwitch();Boolean
maskSetting.getAdminLevelSwitch();//
!CollectionUtils.isEmpty(depTeamIds)
(depTeamIds.contains(userSession.getDepId())
depTeamIds.contains(userSession.getTeamId())))
true;}}MaskContextHolder.setMask(isMask);if
{MaskContextHolder.setMaskSetting(maskSetting);}}filterChain.doFilter(servletRequest,
{MaskContextHolder.clear();}}}可以看到我们在过滤器filter中等登录验证校验通过之后再进行脱敏配置的上下文设置这里我们是直接在缓存redis中获取登录用户对应公司的脱敏信息配置脱敏配置在我们SaaS系统是以公司维度配置的也就是配置了脱敏信息那么对这个公司整体用户都有效果而且我们在配置脱敏信息保存落库的同时也会同步保存redis我们每次调接口都需要设置脱敏信息上下文如果这些信息都从数据库获取(由于之前的设计脱敏配置信息要查3张表…)性能自然是吃不消的所以我们需要从缓存redis中取脱敏配置当然上面的实现不太完善因为直接从redis中取有可能缓存缺失这时候就拿不到脱敏配置了所以我们需要有一个兜底实现在缓存中获取不到再去数据库中查询取到之后设置上下文的同时更新缓存redis这样下次我们再从redis中就能获取到了这个兜底实现很简单可自行实现哦接下来看看定义的缓存脱敏配置信息
rules;}脱敏规则DesensitizationRuleCreate:
end;}完成脱敏信息上下文设置之后接下来就是真正自定义实现序列化解析器完成脱敏的时刻啦话不多说先看看实现类
company_address,native_address,
{jsonGenerator.writeObject(t);return;}Boolean
{jsonGenerator.writeObject(t);return;}MaskSetting
MaskContextHolder.getMaskSetting();ListDesensitizationRuleCreate
(CollectionUtils.isEmpty(rules))
{jsonGenerator.writeObject(t);return;}switch
(String)t;DesensitizationRuleCreate
name)).findFirst().orElse(null);jsonGenerator.writeString(MaskUtils.commonMask(s,
(String)t;DesensitizationRuleCreate
idCard)).findFirst().orElse(null);jsonGenerator.writeString(MaskUtils.commonMask(s,
(String)t;DesensitizationRuleCreate
mobile)).findFirst().orElse(null);jsonGenerator.writeString(MaskUtils.commonMask(s,
(String)t;DesensitizationRuleCreate
address)).findFirst().orElse(null);jsonGenerator.writeString(MaskUtils.commonMask(s,
address;}DesensitizationRuleCreate
maskText);});jsonGenerator.writeObject(map);break;}}}/***
这里也是通过反射判断字段是否打上自己标识但是这个方法只会执行一次*
也就是接口第一次调用会执行后面就不需要执行因为这些Spring考虑到这些打注解标识解析就不会再变没别要每次都来解析浪费性能*/Overridepublic
createContextual(SerializerProvider
serializerProvider.findNullValueSerializer(beanProperty);}//
(Objects.equals(beanProperty.getType().getRawClass(),
Objects.equals(beanProperty.getType().getRawClass(),
beanProperty.getAnnotation(MaskField.class);if
beanProperty.getContextAnnotation(MaskField.class);}if
MaskSerializer(maskField.value());}}return
serializerProvider.findValueSerializer(beanProperty.getType(),
}JsonSerializer可以实现的附加接口以获取回调该回调可用于创建序列化程序的上下文实例以用于处理支持类型的属性。
这对于可以通过注释配置的序列化程序很有用或者应该根据正在序列化的属性类型而具有不同的行为
#createContextual()可以获得字段的类型以及注解该方法只会在第一次序列化字段时调用因为字段的上下文信息在运行期不会改变所以不用担心影响性能。
项目推荐基于SpringBoot2.x、SpringCloud和SpringCloudAlibaba企业级系统架构底层框架封装解决业务开发时常见的非功能性需求防止重复造轮子方便业务快速开发和企业技术栈框架统一管理。
引入组件化的思想实现高内聚低耦合并且高度可配置化做到可插拔。
严格控制包依赖和统一版本管理做到最少化依赖。
注重代码规范和注释非常适合个人学习和企业使用
Github地址https://github.com/plasticene/plasticene-boot-starter-parent
Gitee地址https://gitee.com/plasticene3/plasticene-boot-starter-parent
最后来看看脱敏工具类实现流行的Hutool工具包实现了脱敏先引入依赖
dependencygroupIdcn.hutool/groupIdartifactIdhutool-all/artifactIdversion5.8.16/version
现阶段最新版本的Hutool支持的脱敏数据类型如下基本覆盖了常见的敏感信息用户id、中文姓名、身份证号、座机号、手机号、地址、电子邮件、密码
脱敏是通过*来代替敏感信息的具体实现是在StrUtil.hide方法中如果我们想要自定义隐藏符号则可以把Hutool的源码拷出来重新实现即可。
但是根据上文介绍我们脱敏规则是自定义的所以我们需要得自己实现一个脱敏工具类MaskUtils:
DesensitizedUtil.chineseName(fullName);}/***
DesensitizedUtil.idCardNum(idCard,
前三位后四位其他隐藏例子:138****1234*/public
DesensitizedUtil.mobilePhone(mobile);}/***
DesensitizedUtil.address(address,
DesensitizedUtil.email(email);}/***
前六位后四位其他用星号隐藏每位1个星号例子:6222600**********1234*/public
DesensitizedUtil.bankCard(cardNum);}/***
12316874345;DesensitizationRuleCreate
DesensitizationRuleCreate();rule.setType(1);rule.setScope(3);rule.setCount(4);rule.setStart(1);rule.setEnd(6);String
-3251366134027639518L;ApiModelProperty(value
姓名)MaskField(MaskEnum.NAME)private
身份证号码)MaskField(MaskEnum.ID_CARD)private
电话号码)MaskField(MaskEnum.MOBILE)private
自定义字段)MaskField(MaskEnum.CUSTOM_FIELD)private
首先介绍了数据脱敏需求的背景、概念和重要性紧接着我们逐步探讨实现方案权衡利弊了相关实现选择最终选择Spring
Boot的自带的jackson自定义序列化实现它的实现原来其实就是在json进行序列化渲染给前端时进行脱敏这样可以有效降低性能损耗并且也不会侵入系统业务层逻辑这样可以保证我们的业务逻辑不会因为数据脱敏出现逻辑错误。
与此同时也强调了动态灵活可配置的脱敏信息配置我们通过拦截器实现脱敏信息上下文设置在上面思路我们进行代码实现剖析和实操借助于Hutool工具类提供的脱敏功能完美实现了字段脱敏。
作为专业的SEO优化服务提供商,我们致力于通过科学、系统的搜索引擎优化策略,帮助企业在百度、Google等搜索引擎中获得更高的排名和流量。我们的服务涵盖网站结构优化、内容优化、技术SEO和链接建设等多个维度。
| 服务项目 | 基础套餐 | 标准套餐 | 高级定制 |
|---|---|---|---|
| 关键词优化数量 | 10-20个核心词 | 30-50个核心词+长尾词 | 80-150个全方位覆盖 |
| 内容优化 | 基础页面优化 | 全站内容优化+每月5篇原创 | 个性化内容策略+每月15篇原创 |
| 技术SEO | 基本技术检查 | 全面技术优化+移动适配 | 深度技术重构+性能优化 |
| 外链建设 | 每月5-10条 | 每月20-30条高质量外链 | 每月50+条多渠道外链 |
| 数据报告 | 月度基础报告 | 双周详细报告+分析 | 每周深度报告+策略调整 |
| 效果保障 | 3-6个月见效 | 2-4个月见效 | 1-3个月快速见效 |
我们的SEO优化服务遵循科学严谨的流程,确保每一步都基于数据分析和行业最佳实践:
全面检测网站技术问题、内容质量、竞争对手情况,制定个性化优化方案。
基于用户搜索意图和商业目标,制定全面的关键词矩阵和布局策略。
解决网站技术问题,优化网站结构,提升页面速度和移动端体验。
创作高质量原创内容,优化现有页面,建立内容更新机制。
获取高质量外部链接,建立品牌在线影响力,提升网站权威度。
持续监控排名、流量和转化数据,根据效果调整优化策略。
基于我们服务的客户数据统计,平均优化效果如下:
我们坚信,真正的SEO优化不仅仅是追求排名,而是通过提供优质内容、优化用户体验、建立网站权威,最终实现可持续的业务增长。我们的目标是与客户建立长期合作关系,共同成长。
Demand feedback