当前位置: 首页 > 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;铂金和其他铂金族金属。…...

React Native 导航系统实战(React Navigation)

导航系统实战&#xff08;React Navigation&#xff09; React Navigation 是 React Native 应用中最常用的导航库之一&#xff0c;它提供了多种导航模式&#xff0c;如堆栈导航&#xff08;Stack Navigator&#xff09;、标签导航&#xff08;Tab Navigator&#xff09;和抽屉…...

ardupilot 开发环境eclipse 中import 缺少C++

目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...

工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配

AI3D视觉的工业赋能者 迁移科技成立于2017年&#xff0c;作为行业领先的3D工业相机及视觉系统供应商&#xff0c;累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成&#xff0c;通过稳定、易用、高回报的AI3D视觉系统&#xff0c;为汽车、新能源、金属制造等行…...

多种风格导航菜单 HTML 实现(附源码)

下面我将为您展示 6 种不同风格的导航菜单实现&#xff0c;每种都包含完整 HTML、CSS 和 JavaScript 代码。 1. 简约水平导航栏 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&qu…...

网络编程(UDP编程)

思维导图 UDP基础编程&#xff08;单播&#xff09; 1.流程图 服务器&#xff1a;短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...

图表类系列各种样式PPT模版分享

图标图表系列PPT模版&#xff0c;柱状图PPT模版&#xff0c;线状图PPT模版&#xff0c;折线图PPT模版&#xff0c;饼状图PPT模版&#xff0c;雷达图PPT模版&#xff0c;树状图PPT模版 图表类系列各种样式PPT模版分享&#xff1a;图表系列PPT模板https://pan.quark.cn/s/20d40aa…...

使用Matplotlib创建炫酷的3D散点图:数据可视化的新维度

文章目录 基础实现代码代码解析进阶技巧1. 自定义点的大小和颜色2. 添加图例和样式美化3. 真实数据应用示例实用技巧与注意事项完整示例(带样式)应用场景在数据科学和可视化领域,三维图形能为我们提供更丰富的数据洞察。本文将手把手教你如何使用Python的Matplotlib库创建引…...

【7色560页】职场可视化逻辑图高级数据分析PPT模版

7种色调职场工作汇报PPT&#xff0c;橙蓝、黑红、红蓝、蓝橙灰、浅蓝、浅绿、深蓝七种色调模版 【7色560页】职场可视化逻辑图高级数据分析PPT模版&#xff1a;职场可视化逻辑图分析PPT模版https://pan.quark.cn/s/78aeabbd92d1...

【Linux手册】探秘系统世界:从用户交互到硬件底层的全链路工作之旅

目录 前言 操作系统与驱动程序 是什么&#xff0c;为什么 怎么做 system call 用户操作接口 总结 前言 日常生活中&#xff0c;我们在使用电子设备时&#xff0c;我们所输入执行的每一条指令最终大多都会作用到硬件上&#xff0c;比如下载一款软件最终会下载到硬盘上&am…...

HybridVLA——让单一LLM同时具备扩散和自回归动作预测能力:训练时既扩散也回归,但推理时则扩散

前言 如上一篇文章《dexcap升级版之DexWild》中的前言部分所说&#xff0c;在叠衣服的过程中&#xff0c;我会带着团队对比各种模型、方法、策略&#xff0c;毕竟针对各个场景始终寻找更优的解决方案&#xff0c;是我个人和我司「七月在线」的职责之一 且个人认为&#xff0c…...