征文|「ShardingSphere(四)数据脱敏-实现自定义加密策略」

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`.
  1. 根据错误找到了抛出异常的位置

可以看到,是由于集合为空抛出的异常,那么这个集合是从哪来的?

  1. 进入loadTypeBasedServices(type)方法中,这个方法里只做了一件事,获取当前classtype的集合,并在集合中查找当前type(就是配置的SHA256)


    可以看到,最终返回的集合里确实没有自定义的加密器,上面一行可以看到,result的最终结果都来自SERVICE_MAP中,那么说明自定义的加密策略并没有在这个map中

    map里确实是没有

  2. 看来是在启动的时候没有把自定义的加密策略放到这个map里去造成的,通过debug对这个SERVICE_MAP进行watch发现,这个map是在


    在register这里加入的,看到这里的注释就明白了,官方是通过SPI方式将各个功能通过插件的方式加入整个框架之中。这一点官方文档也有说。这里注册加密策略。可以看到,registerServiceClass这个方法往SERVICE_MAP中添加的元素。直接取决于ServiceLoader.load(srevice)这个方法

  3. 进入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在配置了加密策略的时候,究竟干了什么

  1. ShardingSphere在生成数据源的时候会为数据源配置具体的ShardingRule

    方法详情见org.apache.shardingsphere.shardingjdbc.api.ShardingDataSourceFactory#createDataSource

  2. 在ShardingRule中会根据配置生成对应的加密策略,以及配置加密策略对应的加密算法

    方法详情见org.apache.shardingsphere.core.rule.ShardingRule#createEncryptRule

  3. 具体读取配置文件,以及配置自定义的加密策略是在生成EncryptRule构造方法中的initEncryptors中

    方法详情见org.apache.shardingsphere.encrypt.rule.EncryptRule#initEncryptors

    这个方法的工作只有两步

    • 通过执行NewInstanceServiceLoader.register(Encryptor.class)静态代码块,注册配置的加密策略,通过读取固定位置下配置文件中的内容将具体的加密策略put进SERVICE_MAP中
    • 根据SERVICE_MAP中的加密策略,创建加密策略的实例,以便在进行数据库操作时根据配置选择对应的加密策略进行加密
  4. 在进行数据库操作时,根据配置的加密策略进行加密

自定义加密策略二

实现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、数据库查看最终生成的数据、单元测试等手段验证了加密策略生效

  1. 可以看到数据库里的数据


    对于name这个密文列,由于使用了自定义的加密策略,因为变动因子的原因,所以即使名字相同数据库里的存储也是不同的,而两条记录的same_data是一样的。对于没有变动因子的MD5加密策略,则info加密之后的存储信息也是一样的

  2. 通过name去数据库进行查询,即使密文列不相同,但是由于辅助查询列的存在,ShardingSphere能够准确的查出数据

  3. 通过打印SQL发现,辅助查询列是新增数据的时候,ShardingSphere根据我们的加密策略以及配置的辅助查询的字段默认插入进去的,改写的SQL如下


    ShardingSphere自动帮我们添加了辅助查询列

结束

以上示例代码已经放到GitHub上,如需,请自取。地址

状态:原创(CSDN博主
JackSparrow414

2 个赞
京ICP备2021015875号