java 通过注解实现数据动态脱敏

news2024/11/6 3:08:36

一、为什么要数据脱敏?

数据脱敏是指对某些敏感信息通过脱敏规则进行数据的变形,实现敏感隐私数据的可靠保护。在涉及客户安全数据或者一些商业性敏感数据的情况下,在不违反系统规则条件下,对真实数据进行改造并提供测试使用,如身份证号、手机号、卡号、客户号等个人信息都需要进行数据脱敏。
通过数据脱敏产品,可以有效防止企业内部对隐私数据的滥用,防止隐私数据在未经脱敏的情况下从企业流出。满足企业既要保护隐私数据,同时又保持监管合规,满足企业合规性

二、数据脱敏方案

2.1 静态数据脱敏(SDM)

静态数据脱敏(SDM)一般是通过变形、替换、屏蔽、保格式加密(FPE)等算法,将生产数据导出至目标的存储介质;脱敏后的数据,实际已经改变了源数据的内容,使用数据时需要对数据进行逆向还原。

2.2 动态数据脱敏(DDM)

动态数据脱敏(DDM)是获取数据时,对各种不同的需求,通过技术手段使输出的数据去除敏感信息,完成脱敏。例如:访问IP、MAC、数据库用户、客户端工具、操作系统用户、主机名、时间、影响行数等,在匹配成功后通过改写查询SQL或者拦截防护返回脱敏后的数据到应用端,从而实现敏感数据的脱敏。动态数据脱敏实际上未对源数据的内容做任何改变。

三、实现思路

3.1 使用实体类上字段注解完成脱敏

创建一个自定义注解,在实体类中标识需要脱敏的字段,然后使用拦截器将返回的字段进行脱敏。

3.2 使用方法注解完成脱敏

创建两个自定义注解,将注解放在返回值的方法上,并使用注解标记需要脱敏的字段,使用AOP完成脱敏。

四、 实现步骤(本文实现3.2的方法)

4.1 pom引入工具包

		<dependency>
    		<groupId>cn.hutool</groupId>
			<artifactId>hutool-all</artifactId>
			<version>5.7.16</version>
		</dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>${alibaba.fastjson.version}</version>
        </dependency>

4.2 创建自定义注解

import java.lang.annotation.*;

/**
 * @author luoqifeng
 * @date 2023/05/19 14:25
 * @apiNote 用于标记当前方法需要脱敏
 * 使用这个注解进行脱敏 建议在返回值时使用 Result 包装一下,可以确保每一个传入的值都能转换成 JSONObject 类型
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SensitiveWord {
    Sensitive[] value();
}

创建标记需要脱敏的字段的自定义注解

import cn.hutool.core.util.DesensitizedUtil;

import static cn.hutool.core.util.DesensitizedUtil.DesensitizedType.FIXED_PHONE;

/**
 * @author luoqifeng
 * @date 2023/05/19 14:25
 *
 */
public @interface Sensitive {
    /**
     * json path 的标识
     * @return
     */
    String jsonPath();

    /**
     * 脱敏的字段的数据分类,  默认是座机号码类型脱敏
     * @return
     */
    DesensitizedUtil.DesensitizedType desensitizedType() default FIXED_PHONE;
}

4.3 创建数据脱敏切面

直接复制代码会因为没有引用上面的两个自定义类报错,先复制上面两个自定义注解,然后在这里面引入就好

import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.DesensitizedUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONPath;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

import static cn.hutool.core.util.DesensitizedUtil.DesensitizedType.FIXED_PHONE;

/**
 * 数据脱敏切面
 */
@Slf4j
@Aspect
@Component
public class SensitiveAspect {

    @Around("@annotation(sensitiveWord)")
    public Object around(ProceedingJoinPoint pjp, SensitiveWord sensitiveWord) throws Throwable{
        Object object = pjp.proceed();
        try {
            Class<?> aClass = object.getClass();
            // 改变返回值
            Sensitive[] value = sensitiveWord.value();
            JSONObject ob = JSON.parseObject(JSON.toJSONString(object));

            // 循环处理每一个 json 路径下的值
            for (Sensitive s: value) {
                replace(ob, s);
            }
            object = JSON.parseObject(JSON.toJSONString(ob), aClass);
        }catch (Exception e ){
            log.info("数据脱敏失败:"+e.getMessage());
        }
        return object;
    }

    /**
     * 脱敏
    * */
    private Object sensitiveByString(Object value) {
        if (StringUtils.isNotEmpty(value.toString())) {
            String st = Convert.toStr(value);
            st = st.substring(0, st.length() - 3 > 0 ? 3 : st.length()) + "****" + st.substring(Math.max(st.length() - 4, 0));
            return st;
        }
        return value;
    }

    /**
     * 敏感数据替换
     *
     * @param jsonObject
     * @param s
     */
    private void replace(JSONObject jsonObject, Sensitive s) {
        // 只有传入的 JSON 路径在 这个 JSONObject 中才会进行脱敏处理
        if (JSONPath.contains(jsonObject, s.jsonPath())) {

            // 查询是否有数组 列表 类型的数据需要脱敏
            int index = s.jsonPath().lastIndexOf("[*]");
            if (index > -1) {
                String prefix = StrUtil.subPre(s.jsonPath(), index);
                String suffix = StrUtil.subSuf(s.jsonPath(), index + 3);
                // 提取json 路径下的 数组\链表 元素
                Object eval = JSONPath.eval(jsonObject, prefix);

                // 将数组\链表 元素 转为 JSONArray 方便做 统一格式处理
                JSONArray jsonArray = (JSONArray) eval;
                int size = jsonArray.size();
                for (int i = 0; i < size; i++) {
                    // 由于脱敏数组内部的参数传入格式为 :jsonPath = "$.datas.records[*].username"
                    // 所以需要重新组装 jsonPath 将 * 号 替换成具体的值
                    String indexJsonPath = StrUtil.strBuilder().append(prefix).append("[").append(i).append("]").append(suffix).toString();
                    // 使用 cn.hutool.core.convert Convert.toStr 转换为字符串 如果给定的值为null,或者转换失败,返回默认值null,这样可以减少报错,避免程序异常
                    String desensitized = Convert.toStr(JSONPath.eval(jsonObject, indexJsonPath));
                    if (StrUtil.isBlank(desensitized)) {
                        continue;
                    }
                    // 如果是默认指定 则使用默认方式脱敏
                    if (s.desensitizedType() == FIXED_PHONE) {
                        desensitized = sensitiveByString(desensitized).toString();
                    }else {
                        // 否则使用 cn.hutool.core.util 进行数据脱敏
                        desensitized = DesensitizedUtil.desensitized(desensitized, s.desensitizedType());
                    }

                    // 使用JSON 路径操作,将已经脱敏的新数据,放入之前未脱敏的数据地址处,替换未脱敏数据
                    JSONPath.set(jsonObject, indexJsonPath, desensitized);
                }
            } else {
                // 使用 cn.hutool.core.convert Convert.toStr 转换为字符串 如果给定的值为null,或者转换失败,返回默认值null,这样可以减少报错,避免程序异常
                Object eval = JSONPath.eval(jsonObject, Convert.toStr(s.jsonPath()));
                String desensitized = "";
                if (s.desensitizedType() == FIXED_PHONE) {
                    desensitized = sensitiveByString(Convert.toStr(eval)).toString();
                }else {
                    desensitized = DesensitizedUtil.desensitized(Convert.toStr(eval), s.desensitizedType());
                }
                JSONPath.set(jsonObject, s.jsonPath(), desensitized);
            }
        }
    }
}

4.4 测试使用

4.4.1 在测试的controller 中使用如下代码
	@PostMapping("/getUserInfo")
    @SensitiveWord({
            @Sensitive(jsonPath = "$.datas.name",desensitizedType = CHINESE_NAME),
            @Sensitive(jsonPath = "$.datas.phone",desensitizedType = MOBILE_PHONE),
            @Sensitive(jsonPath = "$.datas.bankCard",desensitizedType = BANK_CARD),
    })
    public Result<Object> getUserInfo() {
        Map<String, String> map = new HashMap<>();
        map.put("name","张三");
        map.put("phone","18111111111");
        map.put("bankCard","6227112222211111211");
        return Result.succeed(map);
    }

响应结果为:
在这里插入图片描述

4.4.2 测试实体类数组脱敏
	@GetMapping("getInfo2")
    @SensitiveWord({
            @Sensitive(jsonPath = "$.datas.username",desensitizedType = MOBILE_PHONE),
            @Sensitive(jsonPath = "$.datas.type"),
    })
    public Result<Object> getCaseInfo2() {
        User user = new User()
                .setUsername("18111111111")
                .setType("ceshi");
        return Result.succeed(user);
    }

输出结果
在这里插入图片描述

4.4.3 测试 数组/list 类型脱敏
    @GetMapping("getInfo3")
    @SensitiveWord(@Sensitive(jsonPath = "$.datas[*].username"))
    public Result<List<User>> getCaseInfo(){
        List<User> users = new ArrayList<>();
        users.add( new User()
                .setUsername("18111111111")
                .setType("ceshi")
        );
        users.add( new User()
                .setUsername("18122222222")
                .setType("ceshi2")
        );
        return Result.succeed(users);
    }

输出结果:
在这里插入图片描述
测试完成,数据脱敏成功

五、 说在最后

动态数据脱敏的方式很多,不局限于自定义注解,也不局限于AOP方式,可行方案,可以是在数据SQL时进行脱敏,可以在业务逻辑里面进行脱敏,可以在网关脱敏等等,根据自身业务规则实现即可。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/545006.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

10个高质量的简历制作网站推荐

刚经历完有金三银四&#xff0c;有没有因为简历不行&#xff0c;面试少的可怜的同学。 今天推荐10个高质量的简历制作网站&#xff0c;包括可以在线免费生成设计简历的网站。 1.即时设计资源社区 即时设计是国内首款专业级的 UI 设计工具&#xff0c;像 PC 端的网页&#xf…

ThingsBoard教程(四十六):规则节点解析 延迟节点 Delay Node。生成节点 Generator Node, 日志节点 Log Node

延迟节点 Delay Node Since TB Version 2.1 延迟接收消息的时间段可进行配置。 配置如下: Period in seconds - 指定暂停接收消息的时间段。 Maximum pending messages - 指定最大允许挂起消息的数量(即被暂停的消息队列)。 当特定传入消息的延迟期达到后,该消息将从…

MATLAB第九章_数据图形可视化

目录 数据图形可视化 MATLAB图形窗口 函数绘制 一元函数绘制 二元函数绘图 数据图形绘制简介 离散数据可视化 连续函数可视化 二维绘图函数 基本绘图 快速方程式画图 特殊二维图形 三维绘图函数 绘制三维曲面 生成栅格数据 网格曲线绘制 隐藏线的显示和关闭 数据…

51单片机串口通信

串口通信 1、通信的基本概念2、 51单片机串口介绍2.1、串口通信简介2.2、串口内部结构2.3、串口通信寄存器SCONPCON 2.4、串口工作方式 3、串口使用方法3.1、硬件3.2、通信协议RS2323.3、串口初始化 4、硬件设计5、 软件设计6、拓展 串口的通信&#xff0c;一般是检测通信模块是…

网络安全专业应该从事哪个方向前景比较好

目前网络安全行业包括很多领域&#xff0c;例如网络安全基础、网络安全运维、应用安全、云安全、算法与数据安全、区块链安全、工业控制系统安全和人工智能安全等方向&#xff0c;各个方向的前景都比较好&#xff0c;关键在于选择适合自己并且感兴趣的方向。同时&#xff0c;也…

OSI体系结构7层,5层,4层协议+负载均衡

上图分别是7&#xff0c;4&#xff0c;5层协议 Q&#xff1a;为什么分4层和7层&#xff0c;目的&#xff1f; 7层&#xff1a;OSI参考模型是一种理论模型&#xff0c;旨在描述计算机网络的功能和架构。它提供了一种通用的框架&#xff0c;用于理解和设计网络协议和系统。然而&…

3.6万亿token、3400亿参数,谷歌大模型PaLM 2细节遭曝光

来源 | 机器之心 ID | almosthuman2014 谷歌内部文件又泄露了&#xff0c;这次是谷歌新一代大模型 PaLM 2 的训练细节&#xff1a;训练数据量是前代的近 5 倍、参数量是前代的三分之二左右。 上周四&#xff0c;在 2023 谷歌 I/O 大会上&#xff0c;谷歌 CEO 皮查伊宣布推出对…

埃尔德动力系统指标公式,衡量趋势的惯性和能量

亚历山大埃尔德(Alexander Elder)在其经典著作《以交易为生》&#xff08;原书第2版&#xff09;新增了一个工具——动力系统(Impulse System)&#xff0c;不过书中只介绍了动力系统的指标以及使用方法&#xff0c;并没有介绍系统的参数。其实动力系统在埃尔德2002年出版的《走…

【数据分析之道-基础知识(十一)】面向对象

文章目录 专栏导读1、简介2、类与对象3、属性和方法4、继承5、多态 专栏导读 ✍ 作者简介&#xff1a;i阿极&#xff0c;CSDN Python领域新星创作者&#xff0c;专注于分享python领域知识。 ✍ 本文录入于《数据分析之道》&#xff0c;本专栏针对大学生、初级数据分析工程师精心…

瑞吉外卖开发总结(全功能实现)

技术栈 项目部署 简历上可写的点 集中处理系统异常&#xff0c;自定义统一的错误码, 并封装了全局异常处理器&#xff0c;屏蔽了项目冗余的报错细节、便于接口调用方理解和统一处理。 基于静态ThreadLocal封装了线程隔离的全局上下文对象&#xff0c;便于在请求内部存取用户信…

【STL模版库】模拟实现vector类模版

一、成员变量 template<class T> class Myvector{typedef T *iterator; //[1]typedef const T *const_iterator;private:iterator _start; //指向存存储空间的开头 //[2]iterator _finish; //指向实际存储元素的下一个位置iterator _end_of_storage; //指向存储空间结尾…

【5.19】三、白盒测试方法—逻辑覆盖法

目录 3.1 逻辑覆盖法 3.1.1 语句覆盖 3.1.2 判定覆盖 3.1.3 条件覆盖 3.1.4 判定—条件覆盖 3.1.5 条件组合覆盖 3.1.6 实例&#xff1a;三角形逻辑覆盖问题 白盒测试又称为透明盒测试、结构测试&#xff0c;它基于程序内部结构进行测试&#xff0c;而不是测试应用程序…

Net跨平台UI框架Avalonia入门-资源和样式

Net跨平台UI框架Avalonia入门-资源和样式编写和使用 资源和样式编写和使用样式&#xff08;Styles&#xff09;和资源&#xff08;Resources&#xff09;样式&#xff08;Styles&#xff09;样式定义定义的位置:定义内容&#xff1a; 样式文件的定义和引用 资源&#xff08;Res…

微信小程序xr-frame后处理

前言&#xff1a;什么是后处理&#xff1f;&#xff08;详见&#xff1a;ThreeJS 后处理 - 掘金 (juejin.cn)&#xff09; 后处理就是对WebGLRenderer.render(scene, camera)的渲染2D图片进行处理。可以把多个后处理进行组合&#xff0c;按照顺序执行&#xff0c;每个处理过程…

新-git-gitee代码管理(管理)

git忽略文件失效 git rm -r --cached . //清除缓存 git add . //添加所有文件 git commit -m update .gitignore //提交更新.gitignoregit 提交的一些规范 开发git commit规范&#xff1a; git commit --fix我的问题feat&#xff1a;新功能 fix&#xff1a;BUG…

VMware16安装 CentOS7

目录 VM下载与安装 密钥 CentOS镜像下载 安装过程 问题 win11一点启动就蓝屏重启 系统安装 安装摘要 选择日期 软件选择-> 最小安装 安装位置 网络和主机名 开始安装 用户设置 完成 登录 xshell连接操作 登录成功 VM下载与安装 官网下载地址 下载 VMware Works…

恩智浦正式启动人工智能创新实践平台,为本地生态注入创新动能

中国天津——2023年5月19日——恩智浦半导体&#xff08;NXP Semiconductors N.V.&#xff0c;纳斯达克代码&#xff1a;NXPI&#xff09;今日宣布&#xff0c;设于天津的人工智能应用创新中心二期项目——人工智能创新实践平台&#xff08;以下称“创新实践平台”&#xff09;…

三、IOC容器(3)

一、IOC操作Bean管理&#xff08;外部属性文件&#xff09; 1.直接配置数据库信息 配置德鲁伊连接池引入德鲁伊连接池依赖jar包 <!--配置连接池--> <bean id"dataSource" class"com.alibaba.druid.pool.DruidDataSource"><property name&…

面了一位5年的测试,真的很失望......

最近看了很多简历&#xff0c;很多候选人年限不小&#xff0c;但是做的都是一些非常传统的项目&#xff0c;想着也不能通过简历就直接否定一个人&#xff0c;何况现在大环境越来 越难&#xff0c;大家找工作也不容易&#xff0c;于是就打算见一见。 在沟通中发现&#xff0c;由…

《HTTP权威指南 陈涓 赵振平》读书笔记

目录 第一章 HTTP概述 第二章 URL与资源 第三章 HTTP报文 第四章 连接管理 第一章 HTTP概述 1、POST和PUT的区别 POST&#xff1a;将客户端数据发送到一个服务器网关应用程序PUT&#xff1a;将来自客户端额数据存储到一个命名的的服务器资源中 2、HTTP报文&#xff1a;…