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

springboot实现数据脱敏

springboot实现数据脱敏

  • 怎么说呢,写着写着发觉 ”这写的什么玩意“ 。

    总的来说就是,这篇文章并不能解决数据脱敏问题,但以下链接可以

    SpringBoot中利用自定义注解优雅地实现隐私数据脱敏

    然后回到本文,本来是想基于AOP代理,实现返回数据脱敏的,具体流程是:

    1. 在controller做切面,实现返回通知

    2. 返回通知中获取返回值对象

    3. 利用反射获取返回值字段

    4. 标有脱敏注释的字段做脱敏处理

    说着好像一套一套的,但事实上,忽略了一个重要的问题,复杂对象很难做反射,例如集合List,Set,Map,或者对象的引用也是对象,就算用多重判断深度遍历,但是反射带来的耗时以及空间开销都是值得思考的,总而言之,这是一个很low的方案。

    但是我很少用到反射,并且感觉既然都写到这了,不如记录一下,aop和反射结合,以后可能会用来实现其他有趣的功能也说不定。那就记录一下吧。

1. 引入依赖

        <!-- 引入aop支持 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

2. 实现两个注解

一个标注在controller方法上,Service也可以

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DesensitizeResult {
    
}

一个标注在属性上

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DesensitizeField {

    // 字段名称,给字段起个名字而已,没啥用
    String name() default "字段名";

    // 前面正常显示字符长度(不脱敏长度)
    int prefixShow() default 1;

    // 后面正常显示字符长度(不脱敏)长度
    int suffixShow() default 0;

    // 脱敏引用符号
    String symbol() default "*";
}

3. 编写切面方法类

@Aspect
@Component
public class DesensitizeAspect {

    @Pointcut("@annotation(com.example.testdemo.annotation.DesensitizeResult)")
    public void getDesensitizeAnno(){}

    /**
     * 返回贴面编程,对返回结果result做脱敏操作
     * @param joinPoint 切点
     * @param result 目标方法返回结果
     */
    @AfterReturning(pointcut = "getDesensitizeAnno()",returning = "result")
    public void afterReturn(JoinPoint joinPoint,Object result){
        // 获取返回结果类的所有属性数组
        Class resultClass = result.getClass();
        Field[] declaredFields = resultClass.getDeclaredFields();
        for (Field field : declaredFields){
            // 循环判断属性中是否存在自定义脱敏注解@DesensitizeField
            for (Annotation annotation : field.getAnnotations()) {
                Class<? extends Annotation> aClass = annotation.annotationType();
                if (aClass.equals(DesensitizeField.class)){
                    DesensitizeField desensitizeField = (DesensitizeField) annotation;
                    // 对标有@DesensitizeField的属性进行脱敏处理
                    field.setAccessible(true);      // 先将该属性改为允许值修改
                    try {
                        String originStr = (String)field.get(result);   // 获取原来的值
                        // 字符串脱敏
                        String desensitizeValue = this.desensitizeStr(originStr, desensitizeField.prefixShow(),
                                desensitizeField.suffixShow(), desensitizeField.symbol());
                        field.set(result,desensitizeValue); // 将脱敏后的字符串写入
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }
    }

    /**
     * 
     * @param originStr 原字符串
     * @param prefixShow 前置正常字符串长度
     * @param suffixShow 后置正常字符串长度
     * @param symbol 脱敏字符显示标志
     * @return 脱敏后字符串
     */
    private String desensitizeStr(String originStr, int prefixShow, int suffixShow, String symbol){
        int length = originStr.length();
        // 避免out of index
        prefixShow = Math.min(prefixShow, length);
        suffixShow = Math.min(suffixShow,length);
        // 前后显示数据超过数据是指长度处理,OutOfRange处理
        if (prefixShow+suffixShow>length){
            prefixShow=length;
            suffixShow=0;
        }
        String desensitizeValue = originStr.substring(0,prefixShow)
                +symbol.repeat(length-prefixShow-suffixShow)
                +originStr.substring(length-suffixShow);
        return desensitizeValue;
    }

}

如上代码所示,只是针对单个简单对象做字段脱敏,复杂对象就完了

4. 测试实体类

@Data
@ToString
public class UserInfo {

    private String id;
    private String account;
    private String nickname;
    @DesensitizeField(name = "真实姓名",prefixShow = 1)
    private String realName;
    @DesensitizeField(name = "密码",prefixShow = 0)
    private String password;
    @DesensitizeField(name = "手机号",prefixShow = 3,suffixShow = 1)
    private String mobile;
    @DesensitizeField(name = "身份证号",prefixShow = 3)
    private String identityId;
    private String createTime;

}

5. 测试接口

@RestController
public class TestController {
    @DesensitizeResult  // aop切面脱敏数据
    @GetMapping("/userInfo")
    public UserInfo getUserInfo(){
        UserInfo userInfo = new UserInfo();
        userInfo.setId("123456");
        userInfo.setAccount("abcdef");
        userInfo.setMobile("13579246810");
        userInfo.setNickname("BigBoss");
        userInfo.setRealName("张小凡");
        userInfo.setIdentityId("430121200001011321");
        userInfo.setPassword("Mm123456#");
        return userInfo;
    }
}

6.测试

image-20231127195103232.png

至此,全文完毕,另外盘算着下次把上面链接的方案偷过来再发一篇。


http://www.kler.cn/a/148064.html

相关文章:

  • 【Vue】Vue3.0(十九)Vue 3.0 中一种组件间通信方式-自定义事件
  • Linux kernel 堆溢出利用方法(二)
  • P8680 [蓝桥杯 2019 省 B] 特别数的和
  • 【教程】华南理工大学国际校区宿舍门锁声音设置
  • python数据写入excel文件
  • Java Stream 流常用操作大全
  • 排序算法--快速排序
  • 排序算法:归并排序、快速排序、堆排序
  • QTextEdit 是 Qt 框架中的一个类,用于显示和编辑多行文本内容的可编辑部件
  • 本地开启https,配置nodeJs服务
  • 基于C#实现并查集
  • 华为鸿蒙开发(HarmonyOs开发):超详细的:DevEco Studio 的安装和配置 、华为第三方包依赖:SDK软件包的安装、Nodejs的导入配置
  • 漏洞复现--致远 M3 反序列化 mobile_portal RCE
  • 同旺科技 USB 转 RS-485 适配器 -- 隔离型
  • 钉钉直播不了检查防火墙配置没有拦截应用测试直通都放行的,电脑还可以ping通直播域名,就是开始不了直播
  • Docker Swarm总结+Jenkins安装配置与集成(5/5)
  • Spring代理方式之静态、动态代理(JDK和CGlib动态代理)
  • 解决ansible批量加入新IP涉及known_hosts报错的问题
  • Linux学习笔记6-串口应用
  • OpenJudge NOI 1.8 16:矩阵剪刀石头布 c语言
  • SpringBoot趣探究--1.logo是如何打印出来的
  • 抖音视频如何无水印下载,怎么批量保存主页所有视频没水印?
  • Linux下unzip解压乱码问题的解决
  • Go 中切片(Slice)的长度与容量
  • spring JdbcTemplate 快速入门
  • JavaScript创建枚举