文章目錄
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
順着斷點進去,看到reload好像也沒發現什麼,那就先看一下這個類是幹嘛的,於是首先在註釋中找到了答案
註釋的前兩段對servie-provider進行了詳細的描述,關鍵在於第三部分,註釋中明確指出了接入service-provider的說明。即:在項目的resource目錄下,新增配置文件,配置文件位於resource/META-INF/services下,配置文件的名字就是對外提供實現拓展服務的全路徑名字,官方文檔中加密策略對外提供的service的是Encryptor這個接口。配置文件裏的內容就是實現了官方接口的類的全路徑。於是看到這裏,趕緊去配置一下
後來發現,其實也沒有好好看註釋,在reload方法中註釋表明了,最後會iterator,而最終的路徑,以及路徑裏的內容也是在這裏獲得的
在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上,如需,請自取。地址