ruoyi-vue-plus-数据加密
# 数据加密
工具类应用 点击跳转
# 数据库加密
ruoyi-vue-plus 数据库加密是通过 Mybatis拦截器实现 , 在处理字段时 有EncryptField
注解的字段会进行加密处理 . 当数据添加时加密 , 获取时解密
支持加密算法 : BASE64
AES
RSA
SM2
SM4
参考文档
- ruoyi-vue-plus数据库加密应用文档 : https://plus-doc.dromara.org (opens new window)
- Liu_York博主Mybatis拦截器笔记 : https://blog.csdn.net/Liu_York (opens new window)
# 应用
- 配置 秘钥 & 类型
- 对象属性写上
EncryptField
注解 , 参数标识 加密类型 - 执行SQL , 新增、修改、查询 , 即可实现存储加密 , 获取解密
提示
查询解密仅限于当前实体类 , 不支持继承实体类方式解密
# 实现原理
理解过程的前提可以看下 Mybatis拦截器 大概运作
# 数据加密
数据加密拦截处理的时期是 参数传递设置时进行处理 , 注解配置项 (根据Signature控制拦截时期)
@Intercepts({@Signature(
type = ParameterHandler.class,
method = "setParameters",
args = {PreparedStatement.class})
})
数据加密过程 :
执行传参SQL时 , 进入 *plugin()*代理方法 , 判断
ParameterHandler
处理类型获取当前参数对象(SQL传递的参数) , 该对象不能为空并且也不能是字符串 , 必须是对象才进行加密处理
对象加密处理 *encryptHandler()*方法 , 判断 以下三种类型 以及拆箱过程
null类型不处理 / Map类型取值递归拆箱加密 / List类型循环取值递归拆箱加密
- Null值 : 跳出加密处理
- Map类型 : 取出所有value值递归执行
- List类型 : 判空 , 非空则获取List第一个对象 , 并判断当中的字段是否包含
EncryptField
加密注解 , 只要第一个含有EncryptField
加密注解 , 那么循环List递归执行
注册式反射获取对象字段 , 采用 EncryptorManager#getFieldCache() 获取对象所有字段
EncryptorManager#getFieldCache() 注册式获取对象反射的字段 (首次反射获取 , 下次缓存获取)
循环对象的所有字段 , 加密处理
- 判空处理 , 空则 跳过循环
- 获取对象注解 (加密类型以及信息) , 并根据注解构建
EncryptContext
上下文对象 - 注册式获取加解者 , 根据
EncryptContext
上下文对象 和 加密配置信息 反射获取 (相同的上下文配置 , 获取相同的加解者) - 使用加解者加密处理 , 加密其他配置信息可直接在
EncryptContext
上下文对象获取 - 对该字段反射设置加密的值
注册式获取
注册式获取 是减少频繁实例和反射次数为目的一种优化方案 , 首次访问会进行缓存起来 , 下次获取时直接取缓存中的已注册对象 . 存储形式一般是Map , key一般是 class类型、配置对象 等..
# 数据解密
数据解密拦截处理的时期是 SQL执行出结果集 , 注解配置项 (根据Signature控制拦截时期)
@Intercepts({@Signature(
type = ResultSetHandler.class,
method = "handleResultSets",
args = {Statement.class})
})
数据解密过程 : (步骤3-5 与加密的相同)
SQL执行出结果值时 , 进入 *plugin()*代理方法 , 采用 Plugin.wrap(target, this)绑定关联代理拦截器方法intercept()
拦截后置处理(SQL执行出结果) , 在*intercept()*方法中 , 执行 *invocation.proceed()*方法得到结果
解密结果集处理 decryptHandler() , 判断 以下三种类型 以及拆箱过程
null类型不处理 / Map类型取值递归拆箱加密 / List类型循环取值递归拆箱加密
- Null值 : 跳出加密处理
- Map类型 : 取出所有value值递归执行
- List类型 : 判空 , 非空则获取List第一个对象 , 并判断当中的字段是否包含
EncryptField
加密注解 , 只要第一个含有EncryptField
加密注解 , 那么循环List递归执行
注册式反射获取对象字段 , 采用 EncryptorManager#getFieldCache() 获取对象所有字段
EncryptorManager#getFieldCache() 注册式获取对象反射的字段 (首次反射获取 , 下次缓存获取)
循环对象的所有字段 , 解密处理
- 判空处理 , 空则 跳过循环
- 获取对象注解 (解密类型以及信息) , 并根据注解构建
EncryptContext
上下文对象 - 注册式获取加解者 , 根据
EncryptContext
上下文对象 和 解密配置信息 反射获取 (相同的上下文配置 , 获取相同的加解者) - 使用加解者加密处理 , 加密其他配置信息可直接在
EncryptContext
上下文对象获取 - 对该字段反射设置加密的值
返回结果集 , *intercept()*方法 解密的结果集直接返回即可 (该对象地址映射的值已被解密处理)
# Mybatis拦截
概念
Intercepts
注解 , 标识拦截类 , 参数Signature注解控制拦截时期Signature
注解 , 标识具体的拦截时期type(指定拦截类)、method(指定拦截类中的时期)、ages(拦截方法的参数)
Interceptor
接口 , Mybatis拦截器实现的接口 , 含以下3个实现方法*intercept(Invocation invocation)*方法 , 拦截前置和后置处理(前后分界线为 *invocation.proceed()*继续执行方法) , Invocation 含有
Signature
注解的标识信息 target (理解为type)、method、ages . 可以得知哪个时期触发拦截 , 日志场景plugin()
方法 , 代理拦截时期的处理过程 , 不处理则返回本身PS : 如果拦截了多个时期(Signature) , 那么代理处理前建议判断 参数对象类型 , 以免交叉处理现象
setProperties()
方法 , 加载核心配置文件时设置变量参数
提示
*plugin()*方法 是 返回代理对象的拦截器 或 不处理 , 返回的值有以下两种形式
- 返回对象本身
target
: 不处理拦截器代理的方法 intercept() - 返回 Plugin.wrap(target, this) : 创建并绑定代理拦截器方法 intercept() , 但拦截器方法一定调用 invocation.proceed() 方法执行
Signature
注解 标识信息
type 拦截类 | method 拦截方法 | 说明 |
---|---|---|
Executor | update | 库前后操作拦截 |
ParameterHandler | getParameterObject、setParameters | 参数拦截 |
ResultSetHandler | handleResultSets、handleCursorResultSets、handleOutputParameters | 结果集拦截 |
StatementHandler | prepare、parameterize、batch、update、query、queryCursor、getBoundSql、getParameterHandler | SQL语句拦截 |