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

Spring Boot AOP实现动态数据脱敏

依赖&配置


<!--  Spring Boot AOP起步依赖  -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>
/*** @Author: 说淑人* @Date: 2025/1/18 23:03* @Description: 切面配置*/
@Configuration
// ---- 该注解用于开启AOP功能。
@EnableAspectJAutoProxy
public class AspectConfig {
}

数据脱敏


 注解&修饰

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** @Author: 说淑人* @Date: 2023-11-24* @Description: 授权业务脱敏AO类*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface OauthBizMask {// ---- 该注解用来修饰在控制器方法上以标注该方法的返回数据需要数据脱敏,其核心作用是// 为AOP提供切入点。// ---- 注意!根据切入方式的不同,该注解并不是必须的,下文在切入代码中提供了无需当前// 注解的切入方式。但我们并不推荐那么做,因为那会导致所有的接口都必须经历数据脱敏过程,// 即使我们并不想执行该操作。}
    /*** 获取宇宙** @param customerId 客户ID* @return 结果BO(客户业务宇宙VO回应)*/@OauthBizMask@ApiOperation("获取宇宙")@GetMapping(value = "get/universe")public ResultBox<CustomerBizUniverseResponse> getUniverse(@ApiParam(value = "客户ID", required = true) @RequestParam(value = "customerId") Long customerId) {return ResultBox.result(customerBizDispatcher.getUniverse(customerId));}/*** 查询宇宙集** @param customerBizQueryRequest 客户业务查询VO请求* @return 结果BO(查询BO(客户业务宇宙VO回应集))*/@OauthBizMask@ApiOperation("查询宇宙集")@GetMapping(value = "query/universes")public ResultBox<QueryBox<CustomerBizUniverseResponse>> queryUniverses(@Valid @ModelAttribute CustomerBizQueryRequest customerBizQueryRequest) {return ResultBox.result(customerBizDispatcher.queryUniverses(customerBizQueryRequest));}
import com.ssr.world.biz.manage.model.eo.oauth.OauthBizMaskRuleEnum;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** @Author: 说淑人* @Date: 2023-11-24* @Description: 授权业务脱敏规则AO类*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface OauthBizMaskRule {// ---- 该注解只对字符串类型的字段有效!// ---- 该注解对嵌套超过5层的对象字段无效!/*** 权限 -- 在拥有指定权限的情况下可以避免数据脱敏,该功能可以视个人情况保留/删除。*/String authority() default "";/*** 规则 -- 具体数据脱敏规则*/OauthBizMaskRuleEnum rule();}
    /*** 账号*/@ApiModelProperty(value = "账号", required = true)// ---- 设置拥有“root/customer/nomask”权限的可以免数据脱敏,数据脱敏规则为账号。@OauthBizMaskRule(authority = "root/customer/nomask", rule = OauthBizMaskRuleEnum.ACCOUNT)private String account;/*** 手机号码*/@ApiModelProperty(value = "手机号码")@OauthBizMaskRule(authority = "root/customer/nomask", rule = OauthBizMaskRuleEnum.PHONE_NUMBER)private String phoneNumber;/*** 名称*/@ApiModelProperty(value = "名称")@OauthBizMaskRule(authority = "root/customer/nomask", rule = OauthBizMaskRuleEnum.NAME)private String name;

 枚举&工具

import com.ssr.world.tool.pedestal.util.string.StringUtil;import java.util.function.Function;/*** @Author: 说淑人* @Date: 2022/1/12 下午8:18* @Description: 授权业务脱敏规则EO类*/
public enum OauthBizMaskRuleEnum {/*** 授权业务脱敏规则枚举集*/ACCOUNT(s -> s.replaceAll("(\\S{5})\\S{10}(\\S*)", "$1**********$2")),PHONE_NUMBER(s -> s.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2")),NAME(s -> s.charAt(0) + StringUtil.repeat('*', s.length() - 1)),;public final Function<String, String> masker;OauthBizMaskRuleEnum(Function<String, String> masker) {this.masker = masker;}}

 切面

    在对数据对象的字段进行反射遍历时,我们还需要考虑父类对象&嵌套对象的字段遍历。由于对象嵌套的层级可能非常深且还可能有相互嵌套的情况,因此在遍历&迭代时必须要限制层级以避免长遍历&死循环,以及还要尽可能避免不必要的遍历,例如原生/框架的类,从而尽可能的提升性能。关于这些问题在下文的代码中都有提及且处理,请仔细查看代码注释。
    下述代码对列表结构也做了处理,基本上可以直接拿来用。

import com.ssr.world.biz.manage.client.oauth.OauthBizStaffClient;
import com.ssr.world.biz.manage.model.ao.oauth.OauthBizMaskRule;
import com.ssr.world.biz.manage.model.eo.oauth.OauthBizUserType;
import com.ssr.world.biz.manage.model.eo.oauth.OauthBizUserTypeEnum;
import com.ssr.world.biz.manage.model.vo.response.oauth.OauthBizStaffAuthorityResponse;
import com.ssr.world.biz.manage.tool.util.oauth.OauthBizUtil;
import com.ssr.world.tool.pedestal.model.bo.result.ResultBox;
import com.ssr.world.tool.pedestal.util.string.StringUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import java.lang.reflect.Field;
import java.util.Collection;
import java.util.Date;
import java.util.Map;
import java.util.Objects;/*** @Author: 说淑人* @Date: 2023-11-24* @Description: 授权业务脱敏AO类*/
@Aspect
@Component
public class OauthBizMaskAspect {@Autowiredprivate OauthBizStaffClient oauthBizStaffClient;//    /**
//     * 切入点
//     */
//    @Pointcut("execution(* com.ssr.world..controller..*(..))")
//    public void pointcut() {
//        // ---- 以工程路径下所有控制器方法为切入点。这种方式比较简便,因为无需额外注解进
//        // 行修饰。但对性能的损耗很大,因为所有的控制器方法都会被切入。
//    }/*** 切入点*/@Pointcut("@annotation(com.ssr.world.biz.manage.model.ao.oauth.OauthBizMask)")public void pointcut() {// ---- 以修饰了@OauthBizMask注解的方法为切入点。}/*** 环绕** @param proceedingJoinPoint 行动参与点* @return 值* @throws Throwable 可抛出*/@Around("pointcut()")public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {// ---- 获取方法的执行结果。Object object = proceedingJoinPoint.proceed();// ---- 判断是否需要对当前请求的返回数据进行脱敏操作,如果未携带令牌/用户为客户/用// 户为超级管理员,则直接返回而不执行的数据脱敏操作(该逻辑视个人情况保留/删除)。if (StringUtil.isBlank(OauthBizUtil.getAuthorization()) ||OauthBizUserTypeEnum.CUSTOMER.equals(OauthBizUtil.getUserType())|| OauthBizUserType.SHUO_SHU_REN.equals(OauthBizUtil.getAccount())) {return object;}// ---- 将控制器方法的返回值强制转化为ResultBox对象以获取内部的封装数据。(该逻辑// 视个人情况保留/删除)。ResultBox<?> resultBox = (ResultBox<?>) object;Object data = resultBox.getData();// ---- 迭代数据对象包括父类在内的所有字段,判断其是否标注了@OauthBizMask注解,是// 则对内部数据进行脱敏。if (Objects.nonNull(data)) {recursiveField(1, data.getClass(), data, // ---- 获取员工权限作为数据脱敏的执行依据。oauthBizStaffClient.getStaffAuthorityMapCache(OauthBizUtil.getAccount()));}return object;}/*** 迭代字段** @param tier         层级* @param clazz        类对象* @param data         数据* @param authorityMap 权限映射* @throws IllegalAccessException 非法访问异常*/private void recursiveField(int tier, Class<?> clazz, Object data, Map<String, OauthBizStaffAuthorityResponse> authorityMap) throws IllegalAccessException {// ---- 如果嵌套层级超过5级则直接返回。层级限制是为了避免深度嵌套导致的性能问题,// 以及相互嵌套导致的死循环问题。if (tier > 5) {return;}// ---- 判断数据对象是否是集(及子类)类型 ,是则迭代内部所有对象的所有字段。注意!// 迭代集中的对象不需要增加层级。if (data instanceof Collection) {for (Object collectionData : (Collection<?>) data) {if (Objects.nonNull(collectionData)) {recursiveField(tier, collectionData.getClass(), collectionData, authorityMap);}}return;}// ---- 如果数据对象不是集(及子类)类型,判断其是否是自开发的类型,否则直接返回。// 该判断可以帮助我们免去对原生/框架类的字段迭代,因为我们只能对自开发的类字段修// 饰@OauthBizMaskRule注解,从而有效提升性能。// ---- 当然,在极少数情况下,我们可能使用除"集类"以外的某些原生/框架类对象来承载自// 开发类对象。这种情况下当前逻辑会导致数据无法脱敏,因此后续可能需要和"集类"一样// 对这些类进行特殊处理。Package pack = clazz.getPackage();if (Objects.isNull(pack) || !pack.getName().startsWith("个人工程路径前缀,例如com.xxx.xxx")) {return;}// ---- 迭代当前class对象的所有直属字段,即非父类字段。Field[] fields = clazz.getDeclaredFields();for (Field field : fields) {// ---- 判断当前字段值是否为null,是则直接略过。field.setAccessible(true);Object fieldData = field.get(data);if (Objects.isNull(fieldData)) {continue;}// ---- 判断当前字段是否是字符串类型,否则对该嵌套对象进行字段迭代,随后返回。if (!(fieldData instanceof String)) {recursiveField(tier + 1, fieldData.getClass(), fieldData, authorityMap);continue;}// ---- 判断字符串字段是否直接修饰了@OauthBizMaskRule注解,否则直接略过。OauthBizMaskRule oauthBizMaskRule = field.getDeclaredAnnotation(OauthBizMaskRule.class);if (Objects.isNull(oauthBizMaskRule)) {continue;}// ---- 如果字符串字段修饰了@OauthBizMaskRule注解,判断当前员工是否拥有指定权// 限且未曾过期,否则直接略过(该逻辑视个人情况保留/删除)。String authorityCode = oauthBizMaskRule.authority();OauthBizStaffAuthorityResponse authority;if (StringUtil.isNotBlank(authorityCode) &&Objects.nonNull(authority = authorityMap.get(authorityCode)) &&new Date().before(authority.getExpireDatetime())) {continue;}// ---- 进行数据脱敏操作。System.out.println("字段名:" + field.getName());System.out.println("字段值:" + fieldData);String value = (String) fieldData;field.set(data, oauthBizMaskRule.rule().masker.apply(value));}// ---- 获取父类,如果父类存在,继续迭代。注意!父类不属于嵌套。Class<?> parentClass = clazz.getSuperclass();if (Objects.nonNull(parentClass)) {recursiveField(tier, parentClass, data, authorityMap);}}}

 效果

相关文章:

Spring Boot AOP实现动态数据脱敏

依赖&配置 <!-- Spring Boot AOP起步依赖 --> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId> </dependency>/*** Author: 说淑人* Date: 2025/1/18 23:03* Desc…...

Leetcode刷题-二分查找

灵神的二分视频&#xff1a;二分查找 红蓝染色法_哔哩哔哩_bilibili 34 class Solution:def searchRange(self, nums: List[int], target: int) -> List[int]:right len(nums) - 1left 0res [-1,-1]mid int((right left)/2)while right > left:if nums[mid] < …...

凭证Account Assignment的校验(FAGL_VALIDATE)

本文主要介绍在S4 HANA OP中凭证Account Assignment的校验配置。具体请参照如下内容&#xff1a; 目录 1. 定义Account Assignment校验策略(FAGL_VALIDATE) 1.1 Derivation Rule 1.2 Assignment 1.3 Initialize 1.4 Enhancement 2. 分配Account Assignment校验策略给公司…...

【20】Word:小许-质量管理-论文❗

目录 题目​ NO1.2.3.4.5 NO6.7 NO8 NO9 NO10.11 题目 NO1.2.3.4.5 另存为“Word.docx”文件在考生文件夹下&#xff0c;F12Fn是另存为的作用布局→页面设置对话框→纸张&#xff1a;大小A4→页边距&#xff1a;上下左右不连续ctrl选择除表格外的所有内容→开始→字体对…...

二十八、Qos服务质量

Qos服务质量 一、产生原因 Resources也不是万能的,使用一段时间后,资源总量可能会超过接节点配置。 根据这个情况,我们可以设置,清除资源。给pod配置,按顺序删除 二、服务质量QoS分类 Guaranteed:最高服务质量(保证),当宿主机内存不够时,会先kill掉QoS为BestEffort…...

Flutter 改完安卓 applicationId 后App 闪退问题。

一、问题 当我们项目创建完&#xff0c;想 build.gradle 改 applicationId 的时候&#xff0c;再次执行的时候可能会出现 app 闪退问题&#xff0c; 控制台不显示任何错误提示 也不出现 Exit 停止运行的情况。&#xff08;像下方这样&#xff0c; 而 app 只是在模拟器中一闪而…...

es 3期 第25节-运用Rollup减少数据存储

#### 1.Elasticsearch是数据库&#xff0c;不是普通的Java应用程序&#xff0c;传统数据库需要的硬件资源同样需要&#xff0c;提升性能最有效的就是升级硬件。 #### 2.Elasticsearch是文档型数据库&#xff0c;不是关系型数据库&#xff0c;不具备严格的ACID事务特性&#xff…...

小菜鸟系统学习Python第三天

1.优先级问题: 结论: 幂运算>正负号>加减乘除和整除>比较运算符>逻辑运算符 2.三元运算符 3.assert断言:抛出AssertionError异常 4.for循环 4. 5.break和continue...

七.网络模型

最小(支撑)树问题 最小部分树求解&#xff1a; 破圈法&#xff1a;任取一圈&#xff0c;去掉圈中最长边&#xff0c;直到无圈&#xff1b; 加边法&#xff1a;取图G的n个孤立点&#xff5b;v1&#xff0c;v2&#xff0c;…&#xff0c; vn }作为一个支撑图&#xff0c;从最短…...

1170 Safari Park (25)

A safari park&#xff08;野生动物园&#xff09;has K species of animals, and is divided into N regions. The managers hope to spread the animals to all the regions, but not the same animals in the two neighboring regions. Of course, they also realize that t…...

数字图像处理:实验五

uu们&#xff01;大家好&#xff0c;欢迎来到数字图像处理第五章节内容的学习&#xff0c;在本章中有关空间滤波的理论学习是十分重要的&#xff0c;所以建议大家要去用心的学习本章&#xff0c;在之后的传感器的相关图像采集时&#xff0c;不可避免的会有噪声等的影响&#xf…...

2024我在csdn走过的路

自我介绍 ✏️博客名✏️&#xff1a; zy_destiny &#x1f338;粉丝数&#x1f338;&#xff1a; 1万 &#x1f33f;擅长领域&#x1f33f;&#xff1a; 人工智能 &#x1f440;欢迎访问&#x1f440;&#xff1a; 我的主页 我的2024 回顾下2024年&#xff0c;起点要从去年写…...

网络安全等级保护基本要求——等保二级

《信息安全技术网络安全等级保护基本要求》GB/T22239-2019 7.1 安全通用要求 7.1.1 安全物理环境 7.1.1.1 物理位置选择 本项要求包括&#xff1a; a) 机房场地应选择在具有防震、防风和防雨等能力的建筑内;b) 机房场地应避免设在建筑物的顶层或地下室&#xff0c;否则应加…...

了解 .mgJSON 文件

.mgJSON &#xff08;Motion Graphics JSON&#xff09;是一个基于标准 JSON 格式的文件扩展名&#xff0c;专门用于存储和交换与动态图形、动画和多媒体应用相关的数据。该格式支持静态和动态数据流&#xff0c;能够精确描述动画、物体变换、图形效果等。 .mgJSON 文件通过层级…...

django使用踩坑经历

DRF 使用drf获取序列化后的id visitor_serializer VisitorSaveSerializer(data{…}) if visitor_serializer.is_valid():visitor visitor_serializer.save() visitor_id visitor.pkpostgrepsql踩坑 django使用postgrepsql&#xff0c;使用聚合函数如:sum 等&#xff0c;被…...

【数据分享】1929-2024年全球站点的逐年最低气温数据(Shp\Excel\免费获取)

气象数据是在各项研究中都经常使用的数据&#xff0c;气象指标包括气温、风速、降水、湿度等指标&#xff01;说到气象数据&#xff0c;最详细的气象数据是具体到气象监测站点的数据&#xff01; 有关气象指标的监测站点数据&#xff0c;之前我们分享过1929-2024年全球气象站点…...

Leetcode:2239

1&#xff0c;题目 2&#xff0c;思路 循环遍历满足条件就记录&#xff0c;最后返回结果值 3&#xff0c;代码 public class Leetcode2239 {public static void main(String[] args) {System.out.println(new Solution2239().findClosestNumber(new int[]{-4, -2, 1, 4, 8})…...

【FPGA】MIPS 12条整数指令【1】

目录 修改后的仿真结果 修改后的完整代码 实现bgtz、bltz、jalr 仿真结果&#xff08;有问题&#xff09; bltz------并未跳转&#xff0c;jCe&#xff1f; 原因是该条跳转语句判断的寄存器r7&#xff0c;在该时刻并未被赋值 代码&#xff08;InstMem修改前&#xff09; i…...

Halcon 3D基础知识及常用函数

一、基本概念 1、点云&#xff08;Point Cloud&#xff09; 点云是一组3D数据点&#xff0c;每个点由笛卡尔坐标系或其他坐标系中的一个三维坐标表示&#xff0c;它被认为是一组非结构化的三维点&#xff0c;象征着三维物体的几何形状。点云是一种简单、完整的数据结构&#…...

贵金属铟,钌,铱,钯铂铑回收工艺详解

Tulsimer CH-95S 是一款为了从工业废水中去除回收汞和贵金属而专门开发的螯合树脂。 Tulsimer CH-95S 是一款拥有聚乙烯异硫脲官能基的大孔树脂&#xff0c;这种树脂对汞有极高的选择性。它也选 择其他的贵金属&#xff0c;如黄金&#xff0c;铂金和其他铂金族金属。…...

文档处理组件Aspose.Words 25.5全新发布 :六大新功能与性能深度优化

在数字化办公日益普及的今天&#xff0c;文档处理的效率与质量直接影响到企业的运营效率。Aspose.Words 作为业界领先的文档处理控件&#xff0c;其最新发布的 25.5 版本带来了六大新功能和多项性能优化&#xff0c;旨在为开发者和企业用户提供更强大、高效的文档处理能力。 六…...

基于PSO粒子群优化的VMD-GRU时间序列预测算法matlab仿真

目录 1.前言 2.算法运行效果图预览 3.算法运行软件版本 4.部分核心程序 5.算法仿真参数 6.算法理论概述 6.1变分模态分解&#xff08;VMD&#xff09; 6.2 门控循环单元&#xff08;GRU&#xff09; 6.3 粒子群优化&#xff08;PSO&#xff09; 7.参考文献 8.算法完…...

基于深度强化学习的Scrapy-Redis分布式爬虫动态调度策略研究

在大数据时代&#xff0c;网络数据的采集与分析变得至关重要&#xff0c;分布式爬虫作为高效获取海量数据的工具&#xff0c;被广泛应用于各类场景。然而&#xff0c;传统的爬虫调度策略在面对复杂多变的网络环境和动态的抓取需求时&#xff0c;往往存在效率低下、资源浪费等问…...

前端项目eslint配置选项详细解析

文章目录 1. 前言2、错误级别3、常用规则4、目前项目使用的.eslintrc.js 1. 前言 ‌ESLint‌ 是一个可配置的 JavaScript 代码检查工具&#xff0c;旨在帮助开发者发现并修复代码中的潜在问题&#xff0c;包括语法错误、逻辑错误以及风格不一致等问题。以下是其核心功能和特点…...

光电耦合器:数字时代的隐形守护者

在数字化、自动化高速发展的今天&#xff0c;光电耦合器正以一种低调却不可或缺的方式&#xff0c;悄然改变着我们的生活。它不仅是电子电路中的“安全卫士”&#xff0c;更是连接信号世界的“桥梁”&#xff0c;凭借出色的电气隔离能力&#xff0c;为各类设备提供稳定可靠的信…...

快速用 uv 模拟发布一个 Python 依赖包到 TestPyPI 上,以及常用命令

目录 1. uv 介绍2. uv 安装&#xff08;Windows版&#xff09;3. 快速模拟一个要发布到TestPyPI上的依赖包&#xff0c;scoful-test-lib3.1 初始化 uv init3.2 进入scoful-test-lib3.3 修改pyproject.toml3.4 使用命令 uv sync3.5. 使用命令 uv lock3.6 使用命令 uv build3.7 获…...

React从基础入门到高级实战:React 实战项目 - 项目一:在线待办事项应用

React 实战项目&#xff1a;在线待办事项应用 欢迎来到本 React 开发教程专栏的第 26 篇&#xff01;在之前的 25 篇文章中&#xff0c;我们从 React 的基础概念逐步深入到高级技巧&#xff0c;涵盖了组件、状态、路由和性能优化等核心知识。这一次&#xff0c;我们将通过一个…...

Unity VR/MR开发-VR设备与适用场景分析

视频讲解链接&#xff1a;【XR马斯维】VR/MR设备与适用场景分析&#xff1f;【UnityVR/MR开发教程--入门】_游戏热门视频...

如何安全高效的文件管理?文件管理方法

文件的管理早已不只是办公场景中的需求。日常生活、在线学习以及个人收藏中&#xff0c;文件管理正逐渐成为我们数字生活中的基础。但与此同时&#xff0c;文件管理的混乱、低效以及安全性问题也频繁困扰着许多人。 文件管理的挑战与解决思路 挑战一&#xff1a;文件存储无序…...

Next.js 中间件鉴权绕过漏洞 CVE-2025-29927

前言:CVE-2025-29927 是一个影响 Next.js 的严重漏洞&#xff0c;源于开发者信任了客户端请求中携带的 X-Middleware-Rewrite 头部字段。攻击者可以手动构造该头部&#xff0c;实现绕过中间件逻辑&#xff0c;访问本应受保护的资源或 API。 影响版本&#xff1a;Next.js < …...