【数据脱敏】⭐️SpringBoot 整合 Jackson 实现隐私数据加密

news2024/12/28 20:30:06

目录

🍸前言

🍻一、Jackson 序列化库

🍺二、方案实践

        2.1 环境准备

        2.2 依赖引入

        2.3 代码编写

 💞️三、接口测试

🍹四、章末


🍸前言

        小伙伴们大家好,最近也是很忙啊,上次的文章分享了 Caffeine 本地缓存,文章链接如下:

【Caffeine】⭐️SpringBoot 项目整合 Caffeine 实现本地缓存_springboot caffeine-CSDN博客

        最近在别的技术分享上看到了数据脱敏的介绍,感觉平时接触的挺多的,比如个人私密信息在页面上显示的时候需要对部分信息加密,就像手机号中间几个位置用 “*” 代替(如下图),具体的实现也有很多方式,比如单独某一个接口需要加密,那么可以在接口返回值时处理下数据即可,但是如果过多的接口有此需求,挨个处理显然费时费力并且增加代码冗余,这个时候就可以选用全局处理的解决方案,实现一次编写,到处实现,具体的方案就是可以在序列化时处理数据 ,序列化工具常用的 Jackson 日常接触也很多

🍻一、Jackson 序列化库

        Jackson 是一个流行的 Java 序列化/反序列化库,专门用于处理 JSON 数据。它提供了一组功能强大且灵活的 API,可以帮助开发者在 Java 对象和 JSON 数据之间进行转换。有以下几个特性,灵活、高效、应用广泛(整合各种框架),比如我们经常接触的 web 系统,就是将后端的数据 Json 序列化后传递给前端实现的

🍺二、方案实践

        2.1 环境准备

        测试项目是基于 SpringBoot 框架的项目实现,另外接口测试使用 Apipost 实现模拟请求

        2.2 依赖引入

        因为使用的 SpringBoot 框架,所以在创建项目的时候就已经勾选了 web 模块,而这些模块已经集成了相关的 Jackson 包,依赖如下

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        如果项目中没有的话,也可以手动引入,依赖如下: 

        注:手动引入的时候,需要注意下版本问题,项目启动的时候可能会遇到如下情况,这种情况我在博客上搜索了相关的文章,有讲解的不是很多,但是可行的一种方案就是自己不要指定版本,使用父依赖的即可解决

Error starting Tomcat context. Exception: org.springframework.beans.factory.BeanCreationException. Message: Error creating bean with name 'formContentFilter' defined in class path resource

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
<!--            <version>2.9.8</version>-->
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
<!--            <version>2.9.8</version>-->
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
<!--            <version>2.9.8</version>-->
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.datatype</groupId>
            <artifactId>jackson-datatype-jsr310</artifactId>
<!--            <version>2.13.0</version> &lt;!&ndash; 替换为与你的Spring Boot兼容的版本 &ndash;&gt;-->
        </dependency>
        2.3 代码编写

                2.3.1 脱敏数据类型枚举

                指定哪些类型的数据需要脱敏,命名为枚举类,方便后续调用

/**
 * @author HuangBen 
 */
public enum SensitiveEnum {

    /**
     * 中文名
     */
    CHINESE_NAME,

    /**
     * 身份证号
     */
    ID_CARD,

    /**
     * 手机号
     */
    MOBILE_PHONE,

    /**
     * 地址
     */
    ADDRESS,

    /**
     * 电子邮件
     */
    EMAIL,

    /**
     * 银行卡
     */
    BANK_CARD,
}

                2.3.2  处理隐私数据工具类

                这里将已有的处理工具封装为一个工具类,方便设置参数以及调用,这里导入的工具类如果报错,说明并没有引入依赖,依赖如下:

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.12.0</version>
        </dependency>
import org.apache.commons.lang3.StringUtils;

/**
 * @author HuangBen 
 */
public class SensitiveInfoUtils {

    /**
     * [中文姓名] 只显示第一个汉字,其他隐藏为2个星号<例子:李**>
     */
    public static String chineseName(final String fullName) {
        if (StringUtils.isBlank(fullName)) {
            return "";
        }
        final String name = StringUtils.left(fullName, 1);
        return StringUtils.rightPad(name, StringUtils.length(fullName), "*");
    }

    /**
     * [中文姓名] 只显示第一个汉字,其他隐藏为2个星号<例子:李**>
     */
    public static String chineseName(final String familyName, final String givenName) {
        if (StringUtils.isBlank(familyName) || StringUtils.isBlank(givenName)) {
            return "";
        }
        return chineseName(familyName + givenName);
    }

    /**
     * [身份证号] 显示最后四位,其他隐藏。共计18位或者15位。<例子:420**********5762>
     */
    public static String idCardNum(final String id) {
        if (StringUtils.isBlank(id)) {
            return "";
        }

        return StringUtils.left(id, 3).concat(StringUtils
                .removeStart(StringUtils.leftPad(StringUtils.right(id, 4), StringUtils.length(id), "*"),
                        "***"));
    }
    
    /**
     * [手机号码] 前三位,后四位,其他隐藏<例子:138******1234>
     */
    public static String mobilePhone(final String num) {
        if (StringUtils.isBlank(num)) {
            return "";
        }
        return StringUtils.left(num, 3).concat(StringUtils
                .removeStart(StringUtils.leftPad(StringUtils.right(num, 4), StringUtils.length(num), "*"),
                        "***"));

    }

    /**
     * [地址] 只显示到地区,不显示详细地址;我们要对个人信息增强保护<例子:北京市海淀区****>
     *
     * @param sensitiveSize 敏感信息长度
     */
    public static String address(final String address, final int sensitiveSize) {
        if (StringUtils.isBlank(address)) {
            return "";
        }
        final int length = StringUtils.length(address);
        return StringUtils.rightPad(StringUtils.left(address, length - sensitiveSize), length, "*");
    }

    /**
     * [电子邮箱] 邮箱前缀仅显示第一个字母,前缀其他隐藏,用星号代替,@及后面的地址显示<例子:g**@163.com>
     */
    public static String email(final String email) {
        if (StringUtils.isBlank(email)) {
            return "";
        }
        final int index = StringUtils.indexOf(email, "@");
        if (index <= 1) {
            return email;
        } else {
            return StringUtils.rightPad(StringUtils.left(email, 1), index, "*")
                    .concat(StringUtils.mid(email, index, StringUtils.length(email)));
        }
    }

    /**
     * [银行卡号] 前六位,后四位,其他用星号隐藏每位1个星号<例子:6222600**********1234>
     */
    public static String bankCard(final String cardNum) {
        if (StringUtils.isBlank(cardNum)) {
            return "";
        }
        return StringUtils.left(cardNum, 6).concat(StringUtils.removeStart(
                StringUtils.leftPad(StringUtils.right(cardNum, 4), StringUtils.length(cardNum), "*"),
                "******"));
    }
    

}

                2.3.3 编写脱敏序列化实现类

                这里就是先用刚才工具类处理数据再进行序列化

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import java.io.IOException;
import java.util.Objects;

/**
 * @author HuangBen
 */
public class SensitiveSerialize extends JsonSerializer<String> implements ContextualSerializer {

    /**
     * 脱敏类型
     */
    private SensitiveEnum type;

    
    @Override
    public void serialize(String s, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        switch (this.type) {
            case CHINESE_NAME: {
                jsonGenerator.writeString(SensitiveInfoUtils.chineseName(s));
                break;
            }
            case ID_CARD: {
                jsonGenerator.writeString(SensitiveInfoUtils.idCardNum(s));
                break;
            }
            case MOBILE_PHONE: {
                jsonGenerator.writeString(SensitiveInfoUtils.mobilePhone(s));
                break;
            }
            case ADDRESS: {
                jsonGenerator.writeString(SensitiveInfoUtils.address(s, 4));
                break;
            }
            case EMAIL: {
                jsonGenerator.writeString(SensitiveInfoUtils.email(s));
                break;
            }
            case BANK_CARD: {
                jsonGenerator.writeString(SensitiveInfoUtils.bankCard(s));
                break;
            }
        }
    }

    @Override
    public JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) throws JsonMappingException {
        // 为空直接跳过
        if (beanProperty != null) {
            // 非 String 类直接跳过
            if (Objects.equals(beanProperty.getType().getRawClass(), String.class)) {
                SensitiveWrapped sensitiveWrapped = beanProperty.getAnnotation(SensitiveWrapped.class);
                if (sensitiveWrapped == null) {
                    sensitiveWrapped = beanProperty.getContextAnnotation(SensitiveWrapped.class);
                }
                if (sensitiveWrapped != null) {
                    // 如果能得到注解,就将注解的 value 传入 SensitiveSerialize
                    return new SensitiveSerialize(sensitiveWrapped.value());
                }
            }
            return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty);
        }
        return serializerProvider.findNullValueSerializer(beanProperty);
    }

    public SensitiveSerialize() {}

    public SensitiveSerialize(final SensitiveEnum type) {
        this.type = type;
    }
}

                2.3.4  编写脱敏注解类

                通过此注解可以灵活标注哪些数据需要脱敏处理,并且标注对应的枚举类型;注意这里指定了序列化的实现类为我们自己刚定义的,具体实现的功能就是遇到此注解标注的字段,序列化的时候使用我们指定的序列化器,我们自定义的序列化器中对每种类型的字段都有相应的处理逻辑

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
 * @author HuangBen 
 */
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonSerialize(using = SensitiveSerialize.class)
public @interface SensitiveWrapped {

    /**
     * 脱敏类型
     * @return
     */
    SensitiveEnum value();
}

                 2.3.5 编写测试实体类

                这里指定了 mobile , idcard 两个字段需要脱敏,并且指明了按照哪一种数据类型脱敏处理

import lombok.Data;

/**
 * @author HuangBen 
 */
@Data
public class UserEntity {

    /**
     * 用户ID
     */
    private Long userId;

    /**
     * 用户姓名
     */
    private String name;

    /**
     * 手机号
     */
    @SensitiveWrapped(SensitiveEnum.MOBILE_PHONE)
    private String mobile;

    /**
     * 身份证号码
     */
    @SensitiveWrapped(SensitiveEnum.ID_CARD)
    private String idCard;

    /**
     * 年龄
     */
    private String sex;

    /**
     * 性别
     */
    private int age;

}

 💞️三、接口测试

        简单用一个 restful 接口测试下处理后的数据是否脱敏,接口如下:

        利用接口请求工具,确实按照我们指定的字段以及类型进行脱敏处理了,测试成功,结果如下:

 

🍹四、章末

        通过这种方式实现的数据脱敏处理,后续在使用的时候也很方便,有哪些需要处理的字段只需要在实体类的字段上方添加注解并且指定脱敏类型即可,符合开闭原则,并且遵循很少改动原有代码的规则;

        文章到这里就结束了~

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

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

相关文章

【人工智能】使用Python的dlib库实现人脸识别技术

&#x1f525; 个人主页&#xff1a;空白诗 文章目录 一、引言二、传统人脸识别技术1. 基于几何特征的方法2. 基于模板匹配的方法3. 基于统计学习的方法 三、深度学习在脸识别中的应用1. 卷积神经网络&#xff08;CNN&#xff09;2. FaceNet和ArcFace 四、使用Python和dlib库实…

SpringBoot:SpringBoot通过注解监测Controller接口

一、前言 在Spring Boot中&#xff0c;度量指标&#xff08;Metrics&#xff09;是监控和诊断应用性能与行为的重要工具。Spring Boot通过集成Micrometer和Spring Boot Actuator&#xff0c;提供了强大的度量指标收集与暴露功能。 二、度量指标 1. Micrometer Micrometer是一…

C#基于SkiaSharp实现印章管理(4)

前几篇文章实现了绘制不同外形印章的功能&#xff0c;印章内部一般包含圆形、线条等形状&#xff0c;有些印章内部还有五角星&#xff0c;然后就是各种样式的文字。本文实现在印章内部绘制圆形、线条、矩形、椭圆等四种形状。   定义FigureType枚举记录印章内部形状&#xff…

Lua 语法学习笔记

Lua 语法学习笔记 安装(windows) 官网&#xff1a;https://www.lua.org/ 下载SDK 解压&修改名称&#xff08;去除版本号&#xff09; 将lua后面的版本号去掉&#xff0c;如lua54.exe->lua.ext 配置环境变量 数据类型 数据类型描述nil这个最简单&#xff0c;只有值n…

HTML 相册2.0 通过css 获取图片资源 2024/7/22 日志

简单方法通过css 绕过同源策略 以获取资源 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>水面倒影…

软件更新的双刃剑:从”微软蓝屏”事件看网络安全的挑战与对策

引言 原文链接 近日&#xff0c;一场由微软视窗系统软件更新引发的全球性"微软蓝屏"事件震惊了整个科技界。这次事件源于美国电脑安全技术公司"众击"提供的一个带有"缺陷"的软件更新&#xff0c;如同一颗隐形炸弹在全球范围内引爆&#xff0c;…

《数据结构:顺序实现二叉树》

文章目录 一、树1、树的结构与概念2、树相关术语 二、二叉树1、概念与结构2、满二叉树3、完全二叉树 三、顺序二叉树存储结构四、实现顺序结构二叉树1、堆的概念与结构2、堆的实现3、堆的排序 一、树 1、树的结构与概念 树是一种非线性的数据结构&#xff0c;它是由n&#xff…

选购指南:如何挑选最适合的快手矩阵系统

在短视频风潮席卷的今天&#xff0c;快手作为其中的佼佼者&#xff0c;吸引了无数创作者和商家的目光。然而&#xff0c;想要在快手上脱颖而出&#xff0c;仅凭内容和创意是远远不够的。一个强大且适合的快手矩阵系统&#xff0c;将是你通往成功的重要钥匙。那么&#xff0c;如…

『 Linux 』信号概念与信号的产生

文章目录 信号概念前台进程与后台进程信号的本质硬件理解信号的产生 信号概念 "信号"一词指用来传达信息或只是的各种形式的提示或标志; 在生活中常见的信号例如红绿灯,交通标志,短信通知等 在操作系统中,"信号"是一种用于异步通知进程发生特定事件的机制;…

【iOS】——SideTable

SideTable Side Table主要用于存储和管理对象的额外信息&#xff0c;特别是与弱引用相关的数据。Side Table的设计和使用是Objective-C运行时实现弱引用的基础&#xff0c;使得ARC&#xff08;Automatic Reference Counting&#xff09;能够正确地处理弱引用的生命周期。 新版…

【深度学习】大模型GLM-4-9B Chat ,微调与部署

下载好东西&#xff1a; 启动容器环境: docker run -it --gpus all --net host --shm-size8g -v /ssd/xiedong/glm-4-9b-xd:/ssd/xiedong/glm-4-9b-xd kevinchina/deeplearning:pytorch2.3.0-cuda12.1-cudnn8-devel-yolov8train bashpip install typer tiktoken numpy1.2…

2024最新版虚拟便携空调小程序源码 支持流量主切换空调型号

产品截图 部分源代码展示 urls.js Object.defineProperty(exports, "__esModule", {value: !0 }), exports.default ["9c5f1fa582bee88300ffb7e28dce8b68_3188_128_128.png", "E-116154b04e91de689fb1c4ae99266dff_960.svg", "573eee719…

web每日一练

每日一题 每天一题罢了。。 ctfshow内部赛签到 扫到备份文件 login.php <?php function check($arr){ if(preg_match("/load|and|or|\||\&|select|union|\|| |\\\|,|sleep|ascii/i",$arr)){echo "<script>alert(bad hacker!)</script>&q…

windows网页视频下载器+Video DownloadHelper+IDM+唧唧down

1:Video DownloadHelper 第一步:下载 链接&#xff1a;https://pan.baidu.com/s/1tWlXcJsq0kY_qrn9pzfCXw?pwdcsy2 提取码&#xff1a;csy2 --来自百度网盘超级会员V4的分享 第二步:浏览器扩展 以edge为例:点击管理扩展: 点击"加载解压缩的扩展": 选中我们的文…

中科亿海微信号采集核心板在振动采集场景中的应用

在工业现场控制领域&#xff0c;对于旋转物体的速度我们通用的做法是测量旋转所产生的振动量来倒推设备的转速值。振动采集系统是一种广泛用于检测和记录系统振动的设备&#xff0c;整体包括传感器和数据采集两部分。传感器类型包括加速度传感器、速度传感器和位移传感器&#…

微信小程序:多图片显示及图片点击放大,多视频显示

微信小程序&#xff1a;多图片显示及图片点击放大&#xff0c;多视频显示 01 多图片显示及图片点击放大02 多视频03 全部代码 01 多图片显示及图片点击放大 <view><view class"title">图片&#xff1a;</view><block wx:if"{{photoUrlList…

什么是离线语音识别芯片?与在线语音识别的区别

离线语音识别芯片是一种不需要联网和其他外部设备支持&#xff0c;‌上电即可使用的语音识别系统。‌它的应用场合相对单一&#xff0c;‌主要适用于智能家电、‌语音遥控器、‌智能玩具等&#xff0c;‌以及车载声控和一部分智能家居。‌离线语音识别芯片的特点包括小词汇量、…

【JavaEE】AQS原理

本文将介绍AQS的简单原理。 首先有个整体认识&#xff0c;全称是 AbstractQueuedSynchronizer&#xff0c;是阻塞式锁和相关的同步器工具的框架。常用的ReentrantLock、Semaphore、CountDownLatch等都有实现它。 本文参考&#xff1a; 深入理解AbstractQueuedSynchronizer只需…

2.5.LeNet

1.LeNet ​ LeNet-5由两个部分组成: 卷积编码器&#xff1a;由两个卷积层组成全连接层密集块&#xff1a;由三个全连接层组成 ​ 先试用卷积层来学习图片空间信息&#xff0c;然后使用全连接层来转换到类别空间 ​ 第一层卷积层要padding一下&#xff0c;收集边框的信息&…

数据清洗系统设计

设计一个高效的数据清洗系统旨在确保数据的质量&#xff0c;以便后续分析和决策过程可以基于准确、一致和完整的信息。以下是设计实时数据清洗系统时需要考虑的关键要素&#xff0c;结合之前提到的设计目标和原则&#xff1a; 1. 高效的数据处理 技术选型&#xff1a;采用并行…