ShardingSphere(四)数据脱敏-实现自定义加密策略
ShardingSphere(四)数据脱敏-实现自定义加密策略
背景
目前官方文档脱敏部分给出的加密算法的配置都是基于内置的MD5和AES,有很多同学想要自定义加密算法并配置,但是不知道怎么做。可以参考以下实现过程及思路
注意
以下代码使用的ShardingSphere版本为4.1.1
关于数据脱敏详细的文档可参考官网数据脱敏部分
自定义加密策略一
实现Encryptor接口
这里采用SHA256的加密算法,由于SHA256加密是不可逆的。所以解密方法只返回密文
@Getter
@Setter
public final class Sha256Encryptor implements Encryptor {
private Properties properties = new Properties();
@Override
public void init() {
}
@Override
public String encrypt(final Object plaintext) {
if (null == plaintext) {
return null;
}
return DigestUtils.sha256Hex(String.valueOf(plaintext));
}
@Override
public Object decrypt(final String ciphertext) {
return ciphertext;
}
@Override
public String getType() {
return "SHA256";
}
}
在配置文件中配置(基于SpringBoot YAML)
这里对于要脱敏的字段进行配置,这里使用了两种加密策略,对于info字段采用默认的MD5加密,对于name字段采用自定义的SHA256加密
sharding:
# 数据脱敏规则配置---start
encrypt-rule:
encryptors:
encryptor_sha256:
# 加密、解密器的名字,内置的为MD5,aes.
# 可以自定义,实现
# com.example.mybatis.demomybatis.shardingsphere.encrypt
# 或者
# org.apache.shardingsphere.encrypt.strategy.spi.QueryAssistedEncryptor
# 这两个接口即可
type: SHA256
encryptor_MD5:
type: md5
tables:
# 数据库,对应上面分片的tables
user:
columns:
# 逻辑列,就是写SQL里面的列,因为实体类的名字和数据库的加密列一致,所以这里都是name
name:
# 密文列,用来存储密文数据
cipherColumn: name
# 加密器名字
encryptor: encryptor_sha256
other_info:
cipherColumn: info
encryptor: encryptor_MD5
# 数据脱敏规则配置---end
遇到的问题以及定位过程
配置完成之后,便去启动,此时启动报错,报错提示
Invalid `org.apache.shardingsphere.encrypt.strategy.spi.Encryptor` SPI type `SHA256`.
- 根据错误找到了抛出异常的位置
可以看到,是由于集合为空抛出的异常,那么这个集合是从哪来的?
-
进入loadTypeBasedServices(type)方法中,这个方法里只做了一件事,获取当前classtype的集合,并在集合中查找当前type(就是配置的SHA256)
可以看到,最终返回的集合里确实没有自定义的加密器,上面一行可以看到,result的最终结果都来自SERVICE_MAP中,那么说明自定义的加密策略并没有在这个map中
map里确实是没有 -
看来是在启动的时候没有把自定义的加密策略放到这个map里去造成的,通过debug对这个SERVICE_MAP进行watch发现,这个map是在
在register这里加入的,看到这里的注释就明白了,官方是通过SPI方式将各个功能通过插件的方式加入整个框架之中。这一点官方文档也有说。这里注册加密策略。可以看到,registerServiceClass这个方法往SERVICE_MAP中添加的元素。直接取决于ServiceLoader.load(srevice)这个方法 -
进入ServiceLoader.load
该方法是java内置的spi机制
顺着断点进去,看到reload好像也没发现什么,那就先看一下这个类是干嘛的,于是首先在注释中找到了答案
注释的前两段对servie-provider进行了详细的描述,关键在于第三部分,注释中明确指出了接入service-provider的说明。即:在项目的resource目录下,新增配置文件,配置文件位于resource/META-INF/services下,配置文件的名字就是对外提供实现拓展服务的全路径名字,官方文档中加密策略对外提供的service的是Encryptor这个接口。配置文件里的内容就是实现了官方接口的类的全路径。于是看到这里,赶紧去配置一下
后来发现,其实也没有好好看注释,在reload方法中注释表明了,最后会iterator,而最终的路径,以及路径里的内容也是在这里获得的
ServiceLoader是java.util包下的一个工具类,JDK8官方文档说明和其他博主说明
在resource目录下新增配置
配置文件名字为:org.apache.shardingsphere.encrypt.strategy.spi.Encryptor
接口名字的完全限定名
配置文件里的内容,放入自定义的加密策略的类的全路径,和要使用官方内置的加密策略的类的全路径
com.example.mybatis.demomybatis.shardingsphere.encrypt.Sha256Encryptor
org.apache.shardingsphere.encrypt.strategy.impl.AESEncryptor
org.apache.shardingsphere.encrypt.strategy.impl.MD5Encryptor
com.example.mybatis.demomybatis.shardingsphere.encrypt.Sha256RandomEncryptor
验证自定义的加密策略是否生效
请求接口,通过debug自定义加密策略加解密方法、控制台打印SQL、数据库查看最终生成的数据、单元测试等手段验证了加密策略生效
解析
再回头看看ShardingSphere在配置了加密策略的时候,究竟干了什么
-
ShardingSphere在生成数据源的时候会为数据源配置具体的ShardingRule
方法详情见org.apache.shardingsphere.shardingjdbc.api.ShardingDataSourceFactory#createDataSource
-
在ShardingRule中会根据配置生成对应的加密策略,以及配置加密策略对应的加密算法
方法详情见org.apache.shardingsphere.core.rule.ShardingRule#createEncryptRule
-
具体读取配置文件,以及配置自定义的加密策略是在生成EncryptRule构造方法中的initEncryptors中
方法详情见org.apache.shardingsphere.encrypt.rule.EncryptRule#initEncryptors
这个方法的工作只有两步
- 通过执行NewInstanceServiceLoader.register(Encryptor.class)静态代码块,注册配置的加密策略,通过读取固定位置下配置文件中的内容将具体的加密策略put进SERVICE_MAP中
- 根据SERVICE_MAP中的加密策略,创建加密策略的实例,以便在进行数据库操作时根据配置选择对应的加密策略进行加密
-
在进行数据库操作时,根据配置的加密策略进行加密
自定义加密策略二
实现QueryAssistedEncryptor接口
至于QueryAssistedEncryptor的应用场景,请阅读官方文档数据脱敏部分
@Getter
@Setter
public final class Sha256RandomEncryptor implements QueryAssistedEncryptor {
private Properties properties = new Properties();
@Override
public String queryAssistedEncrypt(final String plaintext) {
if (null == plaintext) {
return null;
}
// 原始字符串
return DigestUtils.sha256Hex(String.valueOf(plaintext));
}
@Override
public void init() {
}
@Override
public String encrypt(final Object plaintext) {
if (null == plaintext) {
return null;
}
// 原始字符串+变动因子
byte[] bytes = LocalDateTime.now().toString().getBytes();
HMac hMac = new HMac(HmacAlgorithm.HmacSHA256, bytes);
return hMac.digestHex(String.valueOf(plaintext));
}
@Override
public Object decrypt(final String ciphertext) {
return ciphertext;
}
@Override
public String getType() {
return "SHA256_RANDOM";
}
}
以当前时间戳作为变动因子,或者随机字符串也可以,对于密文列,采用HMac加密,对于查询辅助列,则使用普通的SHA256加密
数据表新增same_data子段
在配置文件中配置(基于SpringBoot YAML)
sharding:
encrypt-rule:
encryptors:
encryptor_MD5:
type: md5
encryptor_sha256random:
type: SHA256_RANDOM
tables:
# 数据库,对应上面分片的tables
user:
columns:
# 逻辑列,就是写SQL里面的列,因为实体类的名字和数据库的加密列一致,所以这里都是name
name:
# 密文列,用来存储密文数据
cipherColumn: name
# 加密器名字
encryptor: encryptor_sha256random
# 辅助查询列
assistedQueryColumn: same_data
other_info:
cipherColumn: info
encryptor: encryptor_MD5
在配置文件中添加自定义的加密策略
在org.apache.shardingsphere.encrypt.strategy.spi.Encryptor文件中配置自定义加密策略
com.example.mybatis.demomybatis.shardingsphere.encrypt.Sha256Encryptor
org.apache.shardingsphere.encrypt.strategy.impl.AESEncryptor
org.apache.shardingsphere.encrypt.strategy.impl.MD5Encryptor
com.example.mybatis.demomybatis.shardingsphere.encrypt.Sha256RandomEncryptor
验证自定义加密策略是否生效
请求接口,通过debug自定义加密策略加解密方法、控制台打印SQL、数据库查看最终生成的数据、单元测试等手段验证了加密策略生效
-
可以看到数据库里的数据
对于name这个密文列,由于使用了自定义的加密策略,因为变动因子的原因,所以即使名字相同数据库里的存储也是不同的,而两条记录的same_data是一样的。对于没有变动因子的MD5加密策略,则info加密之后的存储信息也是一样的 -
通过name去数据库进行查询,即使密文列不相同,但是由于辅助查询列的存在,ShardingSphere能够准确的查出数据
-
通过打印SQL发现,辅助查询列是新增数据的时候,ShardingSphere根据我们的加密策略以及配置的辅助查询的字段默认插入进去的,改写的SQL如下
ShardingSphere自动帮我们添加了辅助查询列
结束
以上示例代码已经放到GitHub上,如需,请自取。地址
状态:原创(CSDN博主
JackSparrow414)