前言

前段时间,在群里大佬们讨论了关于数据存储加密的相关需求,后面就有了关于这个功能的 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 进行属性值替换。
在这里插入图片描述

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

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

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

(完)

Logo

快速构建 Web 应用程序

更多推荐