【RuoYi-Vue-Plus】学习笔记 48 - 数据加密功能 Encrypt 源码分析

文章介绍了Mybatis数据加密和解密功能的实现,包括自定义插件如何实现Mybatis拦截器,拦截点的选择,以及加密和解密的具体流程。重点分析了加密过程中的参数拦截、字段加密调用,和解密过程中的结果集处理、字段解密调用。文章还提到了加密解密的配置、实体类和Mapper的使用,以及加密解密管理类的角色。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

前段时间,在群里大佬们讨论了关于数据存储加密的相关需求,后面就有了关于这个功能的 PR,在 框架 5.X 版本 中,这个功能被抽取成了独立的组件,所以本文来分析一下这个功能的实现。

值得一提的是,基于这个功能,并且借助最近大火的 ChatGPT,对于 Mybatis 的自定义插件的实现过程,我有了更进一步的了解,并且最近也在结合书籍进行 Mybatis 源码的阅读,受益匪浅,有空的话会单独再对书中的笔记进行整理分享。

参考目录

功能实现的准备知识

1、目录结构说明

在这里插入图片描述

说明功能
EncryptField字段加密注解标注需要加密的字段,用于实体类字段上
AlgorithmType算法枚举加密注解的加密算法 algorithm()
EncodeType编码类型枚举加密注解的编码类型 encode()
EncryptorAutoConfiguration加解密模块配置类配置初始化,注册加密解密拦截器以及加密管理类
EncryptorProperties加解密属性配置类Yaml 配置
EncryptorManager加密管理类加解密功能的缓存管理以及方法调用
EncryptContext加密上下文对象用于 encryptor 传递必要的参数
IEncryptor加解密接口提供加解密接口用于自定义扩展
*Encryptor加解密现类根据 AlgorithmType 以及 EncodeType 提供不同的加解密实现
MybatisEncryptInterceptor入参加密拦截器加密功能核心实现类
MybatisDecryptInterceptor出参解密拦截器解密功能核心实现类

2、一些准备知识

通过上面的目录结构,其实可以知道的是,要实现加解密功能,需要重点关注的是两个 Mybatis 拦截器。

关于这一部分,我看了源码以及书,也请教了一下 ChatGPT,下面是整理后的一些内容,了解了之后可以对这个功能先有一个大致的了解。

2.1、自定义插件如何实现?

Mybatis的自定义插件是通过实现Mybatis提供的拦截器接口实现的。

这里的拦截器接口就是指 org.apache.ibatis.plugin.Interceptor,框架中 MybatisEncryptInterceptor 以及 MybatisDecryptInterceptor 也分别实现了这个接口。

2.2、Mybatis 拦截器的拦截点?

在这里插入图片描述

加密操作:对 ParameterHandler 进行拦截处理,拦截参数设置。
在这里插入图片描述

解密操作:对 ResultSetHandler 进行拦截处理,拦截结果集处理过程。
在这里插入图片描述

2.3、关于 @Intercepts 注解?

在这里插入图片描述

2.4、关于拦截器中的 Interceptor() 方法和 plugin() 方法?

在这里插入图片描述
在这里插入图片描述

MybatisEncryptInterceptor#plugin
在这里插入图片描述

MybatisDecryptInterceptor#intercept
在这里插入图片描述
下面通过 Debug 结合框架中的代码来分析一下这个功能。

功能调用流程分析

1、说明

1.1、数据加密配置

application.yml
在这里插入图片描述
本文使用默认配置进行说明,其他配置可以参考框架 wiki。

1.2、加密实体类

com.ruoyi.demo.domain.TestDemoEncrypt
在这里插入图片描述

1.3、Mapper(非必须)

com.ruoyi.demo.mapper.TestDemoEncryptMapper
在这里插入图片描述

由于我使用的是开发中的分支,加入了多租户插件,这里为了避免报错所以使用了插件忽略注解。

1.4、测试方法

com.ruoyi.demo.controller.TestEncryptController
在这里插入图片描述

这是框架内置的测试方法,如果想要更好地了解执行过程,也可以对此方法拆分再进行 Debug 分析。如下:

在这里插入图片描述

加密解密操作可以理解成是一个相互的过程,有加密就有解密。理解了其中一个之后,另一个其实也是类似的过程。下面分别会对这两个过程进行分析。

2、加密过程的实现分析

2.1、拦截加密实现方法

MybatisEncryptInterceptor#plugin
在这里插入图片描述

2.2、自定义加密处理器

MybatisEncryptInterceptor#encryptHandler
在这里插入图片描述

2.2.1、获取类加密字段缓存

EncryptorManager#getFieldCache
在这里插入图片描述

由于第一次调用缓存中没有,所以对所有标注 @EncryptField 注解的字段设置属性获取权限,并返回结果集。

2.2.2、属性值替换

在这里插入图片描述

2.2.3、字段加密调用过程

MybatisEncryptInterceptor#encryptField
在这里插入图片描述

根据注解以及配置创建加解密上下文对象,调用加密方法。

EncryptorManager#encrypt在这里插入图片描述

根据加密算法创建对应的加密器,并且存入缓存。
EncryptorManager#registAndGetEncryptor
在这里插入图片描述

调用加密器的加密方法进行加密。
Base64Encryptor#encrypt
在这里插入图片描述

2.3、字段加密完成

加密完成后,返回步骤 #2.2 进行属性值替换。
在这里插入图片描述

循环替换完成后的结果:
在这里插入图片描述

所有字段加密完成后,回到拦截方法并返回目标对象。
在这里插入图片描述

3、解密过程的实现分析

3.1、拦截解密实现方法

MybatisDecryptInterceptor#intercept
在这里插入图片描述

3.2、自定义解密处理器

MybatisDecryptInterceptor#decryptHandler
在这里插入图片描述

在这里插入图片描述

3.2.1、获取类加密字段缓存

这一步和加密类似,由于加密过程中已经将字段存入缓存中,因此可以直接获取到。验证方法:
在这里插入图片描述

3.2.2、属性值替换

上一步获取到类加密字段缓存后,循环进行字段解密。
在这里插入图片描述

3.2.3、字段解密调用过程

MybatisDecryptInterceptor#decryptField
在这里插入图片描述

根据注解以及配置创建加解密上下文对象,调用解密方法。
在这里插入图片描述

如果是第一次调用,同样会根据加密算法创建对应的加密器,并且存入缓存。
在这里插入图片描述

调用加密器的解密方法进行解密。
Base64Encryptor#decrypt
在这里插入图片描述

3.3、字段解密完成

解密完成后,返回步骤 #3.2 进行属性值替换。
在这里插入图片描述

循环替换完成后的结果:
在这里插入图片描述

所有字段解密完成后,回到拦截方法并返回执行结果。
在这里插入图片描述

以上是加解密调用流程的分析。

(完)

@EncryptField是一个自定义的加密字段注解。它可以标记在需要加密的字段上,以指示该字段需要进行加密处理。在上面的代码示例中,@EncryptField被标记在Person类的phoneNumber、IDCard和houseNumber字段上,表示这些字段需要进行加密处理。 使用@EncryptField注解后,可以在相应的业务逻辑中对被标记的字段进行加密操作。具体的加密算法和实现方式可以根据需求进行自定义。 以下是一个示例代码,演示了如何使用@EncryptField注解对字段进行加密处理: ```java @Slf4j @Data public class Person { private Integer id; private String userName; private String loginNo; @EncryptField private String phoneNumber; private String sex; @EncryptField private String IDCard; private String address; @EncryptField private String houseNumber; } // 在业务逻辑中对加密字段进行加密操作 public void encryptPerson(Person person) { // 获取加密字段的值 String phoneNumber = person.getPhoneNumber(); String IDCard = person.getIDCard(); String houseNumber = person.getHouseNumber(); // 进行加密操作,例如使用AES加密算法 String encryptedPhoneNumber = AESUtil.encrypt(phoneNumber); String encryptedIDCard = AESUtil.encrypt(IDCard); String encryptedHouseNumber = AESUtil.encrypt(houseNumber); // 将加密后的值设置回对象 person.setPhoneNumber(encryptedPhoneNumber); person.setIDCard(encryptedIDCard); person.setHouseNumber(encryptedHouseNumber); } ``` 请注意,以上代码只是一个示例,实际的加密操作需要根据具体的需求和加密算法进行实现。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MichelleChung

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值