聊聊 Sharding-JDBC 数据脱敏(四)
2. 更新数据
更新和插入数据一样,同样会将SQL改写,调用encrypt(Object plaintext)方法将明文加密成密文后存储进去。
3. 查询数据
查询数据就比较复杂了,这里只讨论默认情况,则是使用加密列查询,默认配置如下:
spring.shardingsphere.props.query.with.cipher.column=true
查询分为两类,如下:
1.where条件中不带脱敏逻辑列
这种情况也就是在查询结果集中涉及到脱敏的逻辑列,但是在查询条件中不涉及,那么在返回结果的时候则会调用加密器的解密方法Object decrypt(String ciphertext)去将结果解密返回
2.where条件中带脱敏逻辑列
这种情况就比较复杂了,where条件中涉及了脱敏逻辑列,那么在改写SQL时会调用加密器的加密方法String encrypt(Object plaintext)将其加密成密文去查询;
同样返回结果集也会调用加密器的解密方法Object decrypt(String ciphertext)去将结果解密返回
加密策略
Sharding-JDBC默认提供了两种内置的加密器,但是实际开发中这两种肯定是不够用的,需要开发人员去自定义加密器应该各种场景。
Sharding-JDBC提供了两类加密策略接口,如下:
1. Encryptor
提供encrypt(), decrypt()两种方法对需要脱敏的数据进行加解密;在用户进行INSERT, DELETE, UPDATE时,ShardingSphere会按照用户配置,对SQL进行解析、改写、路由,并会调用encrypt()将数据加密后存储到数据库, 而在SELECT时,则调用decrypt()方法将从数据库中取出的脱敏数据进行逆向解密,最终将原始数据返回给用户。
接口如下:
public interface Encryptor extends TypeBasedSPI {
void init();
String encrypt(Object plaintext);
Object decrypt(String ciphertext);
}
2. QueryAssistedEncryptor
相比较于第一种脱敏方案,该方案更为安全和复杂。它的理念是:即使是相同的数据,如两个用户的密码相同,它们在数据库里存储的脱敏数据也应当是不一样的。这种理念更有利于保护用户信息,防止撞库成功。
它提供三种函数进行实现,分别是encrypt(), decrypt(), queryAssistedEncrypt()。在encrypt()阶段,用户通过设置某个变动种子,例如时间戳。针对原始数据+变动种子组合的内容进行加密,就能保证即使原始数据相同,也因为有变动种子的存在,致使加密后的脱敏数据是不一样的。在decrypt()可依据之前规定的加密算法,利用种子数据进行解密。
虽然这种方式确实可以增加数据的保密性,但是另一个问题却随之出现:相同的数据在数据库里存储的内容是不一样的,那么当用户按照这个加密列进行等值查询(SELECT FROM table WHERE encryptedColumnn = ?)时会发现无法将所有相同的原始数据查询出来。
为此,我们提出了辅助查询列的概念。该辅助查询列通过queryAssistedEncrypt()生成,与decrypt()不同的是,该方法通过对原始数据进行另一种方式的加密,但是针对原始数据相同的数据,这种加密方式产生的加密数据是一致的。
将queryAssistedEncrypt()后的数据存储到数据中用于辅助查询真实数据。因此,数据库表中多出这一个辅助查询列。
由于queryAssistedEncrypt()和encrypt()产生不同加密数据进行存储,而decrypt()可逆,queryAssistedEncrypt()不可逆。在查询原始数据的时候,会自动对SQL进行解析、改写、路由,利用辅助查询列进行 WHERE条件的查询,却利用 decrypt()对encrypt()加密后的数据进行解密,并将原始数据返回给用户。这一切都是对用户透明化的。
“上文部分内容摘抄自官方文档”
简单概括一下:
- QueryAssistedEncryptor更加安全,加了一个变动因子一起加密,这样即使内容一样加密后的密文也是不同的
- 为了查询方便,提供了一个辅助查询列,这个辅助查询列中的数据不带变动因子,直接明文加密的,可以根据辅助查询列查询
- 这一切的操作都是透明的,开发人员无须关心,只需要按照给定的规则配置
接口如下:
public interface QueryAssistedEncryptor extends Encryptor {
String queryAssistedEncrypt(String plaintext);
}
“QueryAssistedEncryptor这类加密策略并无内置的加密器,需要开发人员自定义实现”
如何自定义加密器
上文介绍到了Sharding-JDBC支持的两种加密策略,肯定都是要实现一下,下面将会针对两种策略去介绍一下如何自定义。
前提:由于Sharding-JDBC中的加密器是使用SPI方式让开发人员扩展的,因此你还要了解一下SPI,有不清楚的可以看我之前的文章:聊聊 Java SPI 机制
1. Encryptor 自定义实现
自定义很简单,直接实现Encryptor 接口即可,重写其中的加密、解密方法。
下面自定义一个SHA256加密算法器,这是一种不可逆的算法,如下:
/**
* @author 不才陈某 公众号:码猿技术专栏
* 自定义的加密解密算法,基于sha256
*/
@Data
public class Sha256HexEncryptor implements Encryptor {
/**
* 别名,配置时需要
*/
public final static String ALGORITHM_NAME="SHA256";
private Properties properties = new Properties();
@Override
public void init() {
}
/**
* 加密
* INSERT, DELETE, UPDATE时会调用该方法进行加密存储到数据库中
* @param plaintext 明文
* @return 加密后的密文
*/
@Override
public String encrypt(final Object plaintext) {
if (null == plaintext) {
return null;
}
return DigestUtils.sha256Hex(String.valueOf(plaintext));
}
/**
* 解密
* 在SELECT 查询会调用该方法进行解密
* @param ciphertext 密文
* @return 由于sha256是一种不可逆的算法,因此直接返回密文
*/
@Override
public String decrypt(final String ciphertext) {
return ciphertext;
}
/**
* 别名,在配置中指定的名称
*/
@Override
public String getType() {
return Sha256HexEncryptor.ALGORITHM_NAME;
}
}
加密解密的过程和MD5Encryptor加密器类似,不再详细介绍了,很容易理解。
由于是使用SPI的方式,因此还需要在resource/META-INF/services目录中新建一个org.apache.shardingsphere.encrypt.strategy.spi.Encryptor文件,内容如下:
com.java.family.shardingjdbc003.encryptor.Sha256HexEncryptor
好了,现在这个SHA256加密器就已经定义好了,可以在配置文件中配置了。
文章转自公众号:码猿技术专栏