本资源由 itjc8.com 收集整理

今天,我们讨论 ShardingSphere 中的数据脱敏模块。通过在 “10 | 数据脱敏:如何确保敏感数据的安全访问?” 课时中的介绍,我们知道 ShardingSphere 提供了一套自动的数据加解密机制来实现透明化的数据脱敏。

数据脱敏模块整体架构

与普通的编程模式一样,对于数据脱敏而言,我们同样先获取一个 DataSource 作为整个流程的入口,当然这里获取的不是一个普通的 DataSource,而是一个专门针对数据脱敏的 EncryptDataSource。对于数据脱敏模块,我们的思路还是从上到下,从 EncryptDataSource 开始进入到 ShardingSphere 数据脱敏的世界中。

同时,我们这次讲解数据脱敏模块不是零基础,因为在前面介绍 ShardingDataSource、ShardingConnection、ShardingStatement 等内容时,已经对整个 SQL 执行流程的抽象过程做了全面介绍,所涉及的很多内容对于数据脱敏模块而言也都是适用的。

让我们结合下图来做一些回顾:

上图中,可以看到与数据脱敏模块相关的类实际上都继承了一个抽象类,而这些抽象类在前面的内容都已经做了介绍。因此,我们对数据脱敏模块将重点关注于几个核心类的讲解,对于已经介绍过的内容我们会做一些回顾,但不会面面俱到。

基于上图,我们从 EncryptDataSource 开始入手,EncryptDataSource 的创建依赖于工厂类 EncryptDataSourceFactory,其实现如下所示:

public final class EncryptDataSourceFactory {

public static DataSource createDataSource(final DataSource dataSource, final EncryptRuleConfiguration encryptRuleConfiguration, final Properties props) throws SQLException {
        return new EncryptDataSource(dataSource, new EncryptRule(encryptRuleConfiguration), props);
    }
}

这里直接创建了一个 EncryptDataSource,依赖于 EncryptRule 规则对象,我们先来梳理一下 EncryptRule 中具体包含了哪些内容。

EncryptRule

EncryptRule 是数据脱敏模块的一个核心对象,值得我们专门进行展开。在 EncryptRule 中,定义了如下所示的三个核心变量:

//加解密器
private final Map<String, ShardingEncryptor> encryptors = new LinkedHashMap<>();
//脱敏数据表
private final Map<String, EncryptTable> tables = new LinkedHashMap<>();
//脱敏规则配置
private EncryptRuleConfiguration ruleConfiguration;

我们可以把这三个变量分成两部分,其中 ShardingEncryptor 用于完成加解密,而 EncryptTable 和 EncryptRuleConfiguration 则更多的与数据脱敏的配置体系相关。

接下来我将对这两部分分别展开讲解。

1.ShardingEncryptor

在 EncryptRule 中,ShardingEncryptor 是一个接口,代表具体的加密器类,该接口定义如下:

public interface ShardingEncryptor extends TypeBasedSPI {//初始化void init();//加密String encrypt(Object plaintext);//解密Object decrypt(String ciphertext);
}

ShardingEncryptor 接口中存在一对用于加密和解密的方法,同时该接口也继承了 TypeBasedSPI 接口,意味着会通过 SPI 的方式进行动态类加载。

ShardingEncryptorServiceLoader 完成了这个工作,同时在 sharding-core-common 工程中,我们也找到了 SPI 的配置文件,如下所示:

ShardingEncryptor 的 SPI 配置文件

可以看到这里有两个实现类,分别是 MD5ShardingEncryptor 和 AESShardingEncryptor。对于 MD5 算法而言,我们知道它是单向散列的,无法根据密文反推出明文,MD5ShardingEncryptor 的实现类如下所示:

public final class MD5ShardingEncryptor implements ShardingEncryptor {

private Properties properties = new Properties();

@Override
    public String getType() {
        return “MD5”;
    }

@Override
    public void init() {
    }

@Override
    public String encrypt(final Object plaintext) {
        return DigestUtils.md5Hex(String.valueOf(plaintext));
    }

@Override
    public Object decrypt(final String ciphertext) {
        return ciphertext;
    }
}

而 AES 是一个对称加密算法,所以可以根据密文反推出明文,对应的 AESShardingEncryptor 如下所示:

public final class AESShardingEncryptor implements ShardingEncryptor {

private static final String AES_KEY = “aes.key.value”;

private Properties properties = new Properties();

@Override
    public String getType() {
        return “AES”;
    }

@Override
    public void init() {
    }

@Override
    @SneakyThrows
    public String encrypt(final Object plaintext) {
        byte[] result = getCipher(Cipher.ENCRYPT_MODE).doFinal(StringUtils.getBytesUtf8(String.valueOf(plaintext)));
       //使用 Base64 进行加密
        return Base64.encodeBase64String(result);
    }

@Override
    @SneakyThrows
    public Object decrypt(final String ciphertext) {
        if (null == ciphertext) {
            return null;
        }
       //使用 Base64 进行解密
        byte[] result = getCipher(Cipher.DECRYPT_MODE).doFinal(Base64.decodeBase64(String.valueOf(ciphertext)));
        return new String(result, StandardCharsets.UTF_8);
    }

private Cipher getCipher(final int decryptMode) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException {
        Preconditions.checkArgument(properties.containsKey(AES_KEY), “No available secret key for %s.”, AESShardingEncryptor.class.getName());
        Cipher result = Cipher.getInstance(getType());
        result.init(decryptMode, new SecretKeySpec(createSecretKey(), getType()));
        return result;
    }

private byte[] createSecretKey() {
        Preconditions.checkArgument(null != properties.get(AES_KEY), String.format(“%s can not be null.”, AES_KEY));
       //创建秘钥
        return Arrays.copyOf(DigestUtils.sha1(properties.get(AES_KEY).toString()), 16);
    }
}

这里就是对一些常用加密库的直接使用,不做展开讨论。

2.EncryptRuleConfiguration

我们接下来关注于 EncryptRule 中的第二组变量,即 EncryptTable,以及与之相关的配置类 EncryptRuleConfiguration 之间的关系。

我们先来看 EncryptRuleConfiguration,内部包含了两部分内容:

private final Map<String, EncryptorRuleConfiguration> encryptors;
private final Map<String, EncryptTableRuleConfiguration> tables;

而在 EncryptTableRuleConfiguration 内部,同样保存着一个 EncryptColumnRuleConfiguration 列表,如下所示:

private final Map<String, EncryptColumnRuleConfiguration> columns = new LinkedHashMap<>();

我们再来看 EncryptColumnRuleConfiguration 的数据结构,如下所示:

public final class EncryptColumnRuleConfiguration {//存储明文的字段private final String plainColumn;//存储密文的字段private final String cipherColumn;//辅助查询字段private final String assistedQueryColumn;//加密器名字private final String encryptor;
}

终于,我们在这里看到了指定存放明文的 plainColumn、存放密文的 cipherColumn,以及加密器 encryptor 等信息。

我们可以回顾案例中的相关配置项来加深理解:

spring.shardingsphere.encrypt.tables.encrypt_user.columns.user_name.plainColumn=user_name_plain
spring.shardingsphere.encrypt.tables.encrypt_user.columns.user_name.cipherColumn=user_name
spring.shardingsphere.encrypt.tables.encrypt_user.columns.user_name.encryptor=name_encryptor

我们回到最上层的 EncryptRule,发现它的构造函数如下所示:

public EncryptRule(final EncryptRuleConfiguration encryptRuleConfig) {this.ruleConfiguration = encryptRuleConfig;Preconditions.checkArgument(isValidRuleConfiguration(), "Invalid encrypt column configurations in EncryptTableRuleConfigurations.");initEncryptors(encryptRuleConfig.getEncryptors());initTables(encryptRuleConfig.getTables());
}

上述 initEncryptors 方法就是初始化加解密器 Encryptor,而 initTables 方法会根据 EncryptRuleConfiguration 中的 EncryptTableRuleConfiguration 来初始化 EncryptTable。这里的 EncryptTable 更多是一种中间领域模型,用于简化对各种配置信息的处理,其内部保存着一个 EncryptColumn 列表,如下所示:

private final Map<String, EncryptColumn> columns;

而这个 EncryptColumn 中的变量则跟前面介绍的 EncryptColumnRuleConfiguration 一样,包含了存放明文的 plainColumn、存放密文的 cipherColumn,以及加密器 encryptor 等信息。

在了解了 EncryptRule 中所持有的数据模型之后,我们就可以来看一下 EncryptDataSource,在 EncryptDataSource 的构造函数中使用到了 EncryptRule,如下所示:

private final EncryptRuntimeContext runtimeContext;

public EncryptDataSource(final DataSource dataSource, final EncryptRule encryptRule, final Properties props) throws SQLException {
        super(dataSource);
        runtimeContext = new EncryptRuntimeContext(dataSource, encryptRule, props, getDatabaseType());
}

可以看到所传入的 EncryptRule 和 Properties 是用来构建一个 EncryptRuntimeContext,该类继承自 AbstractRuntimeContext 类,而 EncryptRuntimeContext 内部主要保存了用于描述表元数据的 TableMetas 数据结构。

基于改写引擎的数据脱敏实现方案

我们知道 EncryptDataSource 继承了适配器类 AbstractDataSourceAdapter,而它的作用就是生成 EncryptConnection。而对于 EncryptConnection,我们同样也明确它的职责是创建各种 EncryptStatement 和 EncryptPreparedStatement,如下所示:

@Override
public Statement createStatement() throws SQLException {return new EncryptStatement(this);
}@Override
public PreparedStatement prepareStatement(final String sql) throws SQLException {return new EncryptPreparedStatement(this, sql);
}

然后,我们再快速来到 EncryptStatement,来看它的 executeQuery 方法,如下所示:

@Override
public ResultSet executeQuery(final String sql) throws SQLException {if (Strings.isNullOrEmpty(sql)) {throw new SQLException(SQLExceptionConstant.SQL_STRING_NULL_OR_EMPTY);}//获取改写后的 SQL 并执行ResultSet resultSet = statement.executeQuery(getRewriteSQL(sql));this.resultSet = new EncryptResultSet(connection.getRuntimeContext(), sqlStatementContext, this, resultSet);return this.resultSet;
}

显然这里需要重点关注的是 getRewriteSQL 方法,该方法用于获取改写后的 SQL,如下所示:

private String getRewriteSQL(final String sql) { //通过 ParseEngine 对 SQL 进行解析SQLStatement sqlStatement = connection.getRuntimeContext().getParseEngine().parse(sql, false);//获取关系元数据 RelationMetasRelationMetas relationMetas = getRelationMetas(connection.getRuntimeContext().getTableMetas());//构建 SQLStatementContextsqlStatementContext = SQLStatementContextFactory.newInstance(relationMetas, sql, Collections.emptyList(), sqlStatement);//构建 SQLRewriteContextSQLRewriteContext sqlRewriteContext = new SQLRewriteContext(relationMetas, sqlStatementContext, sql, Collections.emptyList());//判断是否根据数据脱敏列进行查询boolean isQueryWithCipherColumn = connection.getRuntimeContext().getProps().<Boolean>getValue(ShardingPropertiesConstant.QUERY_WITH_CIPHER_COLUMN);//构建 EncryptSQLRewriteContextDecorator 对 SQLRewriteContext 进行装饰new EncryptSQLRewriteContextDecorator(connection.getRuntimeContext().getRule(), isQueryWithCipherColumn).decorate(sqlRewriteContext);//生成 SQLTokenssqlRewriteContext.generateSQLTokens();//使用 DefaultSQLRewriteEngine 进行改写String result = new DefaultSQLRewriteEngine().rewrite(sqlRewriteContext).getSql();//打印结果showSQL(result);//返回结果return result;
}

这个方法的部分代码有一种让人似曾相识的感觉,我们回想一下 “20 | 改写引擎:如何理解装饰器模式下的 SQL 改写实现机制?” 中介绍的 BaseShardingEngine的rewriteAndConvert 方法,也看到过 isQueryWithCipherColumn 判断,以及 EncryptSQLRewriteContextDecorator,当时我们没有具体展开,今天就来一起看一下。

1.EncryptSQLRewriteContextDecorator

EncryptSQLRewriteContextDecorator 实现如下所示:

public final class EncryptSQLRewriteContextDecorator implements SQLRewriteContextDecorator {

private final EncryptRule encryptRule;

private final boolean isQueryWithCipherColumn;

@Override
    public void decorate(final SQLRewriteContext sqlRewriteContext) {
        //参数改写
        for (ParameterRewriter each : new EncryptParameterRewriterBuilder(encryptRule, isQueryWithCipherColumn).getParameterRewriters(sqlRewriteContext.getRelationMetas())) {
            if (!sqlRewriteContext.getParameters().isEmpty() && each.isNeedRewrite(sqlRewriteContext.getSqlStatementContext())) {
                each.rewrite(sqlRewriteContext.getParameterBuilder(), sqlRewriteContext.getSqlStatementContext(), sqlRewriteContext.getParameters());
            }
        }

//SQLTokenGenerator 初始化
        sqlRewriteContext.addSQLTokenGenerators(new EncryptTokenGenerateBuilder(encryptRule, isQueryWithCipherColumn).getSQLTokenGenerators());
    }
}

我们还是来对比 ShardingSQLRewriteContextDecorator 类,会发现它与 EncryptSQLRewriteContextDecorator 类的结构完全一致。区别在于这里创建的 ParameterRewriterBuilder 和 SQLTokenGeneratorBuilder 分别是 EncryptParameterRewriterBuilder 和 EncryptTokenGenerateBuilder,而不是ShardingParameterRewriterBuilder 和 ShardingTokenGenerateBuilder。但这两组类的内部结构同样是完全一致的。

在 EncryptParameterRewriterBuilder 内部,同样使用如下方法获取一组 ParameterRewriter:

private Collection<ParameterRewriter> getParameterRewriters() {Collection<ParameterRewriter> result = new LinkedList<>();result.add(new EncryptAssignmentParameterRewriter());result.add(new EncryptPredicateParameterRewriter());result.add(new EncryptInsertValueParameterRewriter());return result;
}

接下来,我们先以 EncryptAssignmentParameterRewriter 为例来看用于数据脱敏的具体 ParameterRewriter 的实现机制。

2.EncryptAssignmentParameterRewriter

EncryptAssignmentParameterRewriter 类完成在数据脱敏场景下对参数赋值过程的改写。我们首先注意到 EncryptAssignmentParameterRewriter 中存在一个 isNeedRewriteForEncrypt 方法用于判断是否需要改写。

@Override
protected boolean isNeedRewriteForEncrypt(final SQLStatementContext sqlStatementContext) {return sqlStatementContext.getSqlStatement() instanceof UpdateStatement|| sqlStatementContext instanceof InsertSQLStatementContext && sqlStatementContext.getSqlStatement().findSQLSegment(SetAssignmentsSegment.class).isPresent();
}

这里的判断条件有两个,一个是 UpdateStatement,一个是 InsertSQLStatementContext(且其中的 SQLStatement 中包含 SetAssignmentsSegment)。我们知道在 SQL 语法中,INSERT 和 UPDATE 语句中都具有如下所示的 SET 赋值部分:

SET userId = 1, task_name = 'taskName'

EncryptAssignmentParameterRewriter 类针对的就是这种场景。我们来看它的 Rewrite 核心方法,如下所示:

@Override
public void rewrite(final ParameterBuilder parameterBuilder, final SQLStatementContext sqlStatementContext, final List<Object> parameters) {String tableName = sqlStatementContext.getTablesContext().getSingleTableName();//获取 SetAssignmentsSegment 并进行遍历for (AssignmentSegment each : getSetAssignmentsSegment(sqlStatementContext.getSqlStatement()).getAssignments()) {//判断是否存在 ShardingEncryptorif (each.getValue() instanceof ParameterMarkerExpressionSegment && getEncryptRule().findShardingEncryptor(tableName, each.getColumn().getName()).isPresent()) {StandardParameterBuilder standardParameterBuilder = parameterBuilder instanceof StandardParameterBuilder? (StandardParameterBuilder) parameterBuilder : ((GroupedParameterBuilder) parameterBuilder).getParameterBuilders().get(0);//对参数进行加密encryptParameters(standardParameterBuilder, tableName, each, parameters);}}
}

这里通过 getSetAssignmentsSegment 方法获取 SetAssignmentsSegment,实现过程就是根据 SQLStatement 类型分别获取 InsertStatement 和 UpdateStatement 中的 SetAssignment。

然后,我们循环遍历每一个 SetAssignmentsSegment,针对表中的每一个 Column 判断是否存在 ShardingEncryptor,如果有的话就返回对应的加解密器。

这部分判断工作就是在前面介绍的 EncryptRule 中完成,如下所示:

public Optional<ShardingEncryptor> findShardingEncryptor(final String logicTable, final String logicColumn) {if (!tables.containsKey(logicTable)) {return Optional.absent();}Optional<String> encryptor = tables.get(logicTable).findShardingEncryptor(logicColumn);return encryptor.isPresent() ? Optional.of(encryptors.get(encryptor.get())) : Optional.<ShardingEncryptor>absent();
}

然后我们获取 StandardParameterBuilder,并调用 encryptParameters 方法完成参数的数据脱敏操作,如下所示:

private void encryptParameters(final StandardParameterBuilder parameterBuilder, final String tableName, final AssignmentSegment assignmentSegment, final List<Object> parameters) {String columnName = assignmentSegment.getColumn().getName();int parameterMarkerIndex = ((ParameterMarkerExpressionSegment) assignmentSegment.getValue()).getParameterMarkerIndex();Object originalValue = parameters.get(parameterMarkerIndex);

//通过 ShardingEncryptor 进行加密,并替换原来存储密文的 cipherColumn
        Object cipherValue = getEncryptRule().getEncryptValues(tableName, columnName, Collections.singletonList(originalValue)).iterator().next();
        parameterBuilder.addReplacedParameters(parameterMarkerIndex, cipherValue);
        Collection<Object> addedParameters = new LinkedList<>();

//如果存在 assistedQueryColumn,则添加辅助查询字段
        if (getEncryptRule().findAssistedQueryColumn(tableName, columnName).isPresent()) {
            Object assistedQueryValue = getEncryptRule().getEncryptAssistedQueryValues(tableName, columnName, Collections.singletonList(originalValue)).iterator().next();
            addedParameters.add(assistedQueryValue);
        }

//如果存在 plainColumn,则添加明文字段
        if (getEncryptRule().findPlainColumn(tableName, columnName).isPresent()) {
            addedParameters.add(originalValue);
        }
        if (!addedParameters.isEmpty()) {
            parameterBuilder.addAddedParameters(parameterMarkerIndex + 1, addedParameters);
        }
}

这里的核心逻辑就是继续通过 EncryptRule 的 getEncryptValues 方法获取密文,然后通过获取具体的 ShardingEncryptor 并调用其方法完成这一操作,如下所示:

public List<Object> getEncryptValues(final String logicTable, final String logicColumn, final List<Object> originalValues) {final Optional<ShardingEncryptor> shardingEncryptor = findShardingEncryptor(logicTable, logicColumn);Preconditions.checkArgument(shardingEncryptor.isPresent(), String.format("Can not find ShardingQueryAssistedEncryptor by %s.%s.", logicTable, logicColumn));return Lists.transform(originalValues, new Function<Object, Object>() {

@Override
            public Object apply(final Object input) {
                return null == input ? null : String.valueOf(shardingEncryptor.get().encrypt(input.toString()));
            }
        });
}

关于 EncryptAssignmentParameterRewriter 的实现,这里面涉及的类也比较多,我们可以先来画张图作为后续讨论的基础,如下所示:

3.EncryptAssignmentTokenGenerator

讨论完 EncryptParameterRewriterBuilder 之后,我们再来讨论 EncryptTokenGenerateBuilder。这里,我们也是以 EncryptAssignmentTokenGenerator 为例来进行展开,在这个类中,核心方法是 generateSQLTokens,如下所示:

@Override
public Collection<EncryptAssignmentToken> generateSQLTokens(final SQLStatementContext sqlStatementContext) {Collection<EncryptAssignmentToken> result = new LinkedList<>();String tableName = sqlStatementContext.getTablesContext().getSingleTableName();//获取 SetAssignmentsSegment 并进行遍历for (AssignmentSegment each : getSetAssignmentsSegment(sqlStatementContext.getSqlStatement()).getAssignments()) {//判断是否存在 ShardingEncryptorif (getEncryptRule().findShardingEncryptor(tableName, each.getColumn().getName()).isPresent()) {//生成 SQLTokenOptional<EncryptAssignmentToken> sqlToken = generateSQLToken(tableName, each);if (sqlToken.isPresent()) {result.add(sqlToken.get());}}}return result;
}

这里同样根据是否找到 ShardingEncryptor 来执行后续的 generateSQLToken 方法,该方法最终会调用类似如下所示的 generateLiteralSQLToken 方法:

private EncryptAssignmentToken generateLiteralSQLToken(final String tableName, final AssignmentSegment assignmentSegment) {EncryptLiteralAssignmentToken result = new EncryptLiteralAssignmentToken(assignmentSegment.getColumn().getStartIndex(), assignmentSegment.getStopIndex());addCipherAssignment(tableName, assignmentSegment, result);addAssistedQueryAssignment(tableName, assignmentSegment, result);addPlainAssignment(tableName, assignmentSegment, result);return result;
}

以上面的 addCipherAssignment 方法为例,我们不难想象该方法通过调用 ShardingEncryptor 来完成了 CipherColumn 的设置。

private void addCipherAssignment(final String tableName, final AssignmentSegment assignmentSegment, final EncryptLiteralAssignmentToken token) {Object originalValue = ((LiteralExpressionSegment) assignmentSegment.getValue()).getLiterals();Object cipherValue = getEncryptRule().getEncryptValues(tableName, assignmentSegment.getColumn().getName(), Collections.singletonList(originalValue)).iterator().next();token.addAssignment(getEncryptRule().getCipherColumn(tableName, assignmentSegment.getColumn().getName()), cipherValue);
}

至此,我们对 EncryptSQLRewriteContextDecorator 的介绍就告一段落,这部分内容可以结合 “20 | 改写引擎:如何理解装饰器模式下的 SQL 改写实现机制?” 一起来看,以便加深理解。

数据脱敏和结果归并

介绍完了 EncryptSQLRewriteContextDecorator 之后,接下来我们回到 EncryptStatement 类,继续探讨 getRewriteSQL 方法的后续流程。

我们回到 EncryptStatement 的 executeQuery 方法,回顾如下语句:

ResultSet resultSet = statement.executeQuery(getRewriteSQL(sql));

我们通过执行 executeQuery 方法获取了 ResultSet,但并不是直接返回这个 resultSet 对象,而是需要对其进行封装,构建一个 EncryptResultSet 对象,如下所示:

this.resultSet = new EncryptResultSet(connection.getRuntimeContext(), sqlStatementContext, this, resultSet);

EncryptResultSet 继承了 AbstractUnsupportedOperationResultSet 类,而 AbstractUnsupportedOperationResultSet 又继承了 AbstractUnsupportedUpdateOperationResultSet,这个 AbstractUnsupportedUpdateOperationResultSet 又继承了 WrapperAdapter 类并实现了 ResultSet 接口。所以 EncryptResultSet 也是一种适配器,这点和 EncryptDataSource、EncryptConnection 在本质上是一样的。

对于 EncryptResultSet 而言,存在一大批 get 方法,我们都不需要专门进行介绍,关键点在于构造函数中的如下方法:

mergedResult = createMergedResult(queryWithCipherColumn, resultSet);

我们知道 ShardingSphere 中,执行引擎之后就是归并引擎,而在 EncryptResultSet 中我们就用到了归并引擎并生成了 MergedResult。

EncryptResultSet 会先判断传入的 SQLStatement 是否是一种 DALStatement,如果是,则会调用 DALEncryptMergeEngine 完成结果归并;如果不是,则会使用 DQLEncryptMergeEngine,我们同样重点关注 DQLEncryptMergeEngine。

public final class DQLEncryptMergeEngine implements MergeEngine {private final EncryptorMetaData metaData;private final MergedResult mergedResult;private final boolean queryWithCipherColumn;

@Override
    public MergedResult merge() {
        return new EncryptMergedResult(metaData, mergedResult, queryWithCipherColumn);
    }
}

DQLEncryptMergeEngine 非常简单,其 merge 方法只是构建了一个 EncryptMergedResult 对象并进行返回。EncryptMergedResult 中核心方法 getValue 如下所示:

@Override
public Object getValue(final int columnIndex, final Class<?> type) throws SQLException {Object value = mergedResult.getValue(columnIndex, type);if (null == value || !queryWithCipherColumn) {return value;}Optional<ShardingEncryptor> encryptor = metaData.findEncryptor(columnIndex);return encryptor.isPresent() ? encryptor.get().decrypt(value.toString()) : value;
}

显然,从上述流程中不难看出,数据脱敏模块中的归并实现实际上就是调用 ShardingEncryptor 的 decrypt 方法将加密列的密文解密成明文即可。

这样整个 EncryptStatement 的 executeQuery 方法的整体流程就介绍完毕了,理解了这个方法的实现过程之后,对于 EncryptStatement 和 EncryptPreparedStatement 的其他方法而言,理解起来就比较容易了。

从源码解析到日常开发

对于今天讨论的主题而言,能够直接应用到日常开发过程中的内容就是 ShardingEncryptor 的抽象过程,以及内部的加解密实现机制。ShardingSphere 使用了 DigestUtils 工具类来完成 MD5 算法的应用,以及 Base64 工具类来完成AES算法的实现。

这两个工具类都可以完全照搬到我们自己的系统中,从而添加成熟的加解密算法实现方案。

小结与预告

今天,我们讨论了 ShardingSphere 中实现数据脱敏机制的底层原理。我们发现数据脱敏模块同时依赖于分片引擎中的改写引擎和归并引擎这两大块内容,尤其是改写引擎在数据脱敏过程中起到了核心作用,通过补列的方式完成明文数据与密文数据之间的自动加解密,以及透明的 SQL 转换过程。

这里留一道思考题:ShardingSphere 中,数据脱敏模块与改写引擎和归并引擎之间是怎么样的协作关系?欢迎你在留言区与大家讨论,我将逐一点评解答。

在介绍完今天的数据脱敏机制之后,明天将介绍一个同样非常有用的编排和治理功能,我们将基于配置中心解析实现配置信息动态化管理的底层原理。

课程评价入口,挑选 5 名小伙伴赠送小礼品~


精选评论

ShardingSphere分库分表核心原理精讲第十二节 数据安全和脱敏详解相关推荐

  1. ShardingSphere分库分表核心原理精讲第十一节 分布式事务详解

    27 分布式事务:如何理解 ShardingSphere 中对分布式事务的抽象过程? 从今天开始,我们将进入一个全新模块,即ShardingSphere 分布式事务.这是一个非常重要的主题,我们将通过 ...

  2. mysql主从shardingsphere分库分表

    问题: 1. 公司的mysql主从复制方式怎么查看--这个命令在哪敲 2.公司扩容一个从的时候怎么做的?-- 3.公司主从架构模式是什么样的?几主几从 4.公司的业务场景有木有要求写后立马查出数据的 ...

  3. 基于ShardingSphere分库分表

    背景: 无论是数据库还是应用从最原始的单机架构开始,随着用户的增多,考虑到系统的高可用和越来越多的用户请求,我们开始使用主从架构,用户量和业务量进一步上升之后,微服务集群和数据库集群也就出现了,就应用 ...

  4. ShardingSphere分库分表

    ShardingSphere是一款起源于当当网内部的应用框架.2015年在当当网内部诞生,最初就叫ShardingJDBC.2016年的时候,由其中一个主要的开发人员张亮,带入到京东数科,组件团队继续 ...

  5. 百亿数据分库分表核心流程详解

    前言 俗话说:面试造火箭,入职拧螺丝.尽管99.99%的业务都不需要用到分库分表,但是分库分表还是频繁出现在大厂的面试中. 分库分表涉及到的内容非常多,有很多细节,如果在面试中被问到了,既是挑战,也是 ...

  6. 快速入门分库分表概念原理

    什么是分库分表 其实 分库 和 分表 是两个概念,只不过通常分库与分表的操作会同时进行,以至于我们习惯性的将它们合在一起叫做分库分表. 分库分表是为了解决由于库.表数据量过大,而导致数据库性能持续下降 ...

  7. Springboot2.x +JPA 集成 Apache ShardingSphere 分库分表

    分库分表背景: 数据库性能瓶颈:主要分为按照业务来划分或者按照数据量来划分. 拆分方式: 水平拆分(每个表的结构都一样):订单表数据量大,我们可以水平拆分 ,分成order表1.order表2.ord ...

  8. SpringBoot 2.x ShardingSphere分库分表实战

    本文转载自微信公众号:李浩东的博客 一. 项目需求 在之前我做项目的时候,数据量比较大,单表千万级别的,需要分库分表,于是在网上搜索这方面的开源框架,最常见的就是mycat,sharding-sphe ...

  9. 【分库分表】ShardingSphere分库分表实战

    一.参考资料 概览 :: ShardingSphere ShardingSphere之分库&分表_JustryDeng-CSDN博客_shardingsphere 分表 利用ShardingS ...

最新文章

  1. 使用Volley传输网络数据
  2. 杰思安全获数千万元A+轮投资,绿盟科技领投,德联资本跟投
  3. php ci session获取值,CI3.1 Session类取不到值的问题
  4. PHP判断手机横向,H5横竖屏检测的方法
  5. (转) Hadoop1.2.1安装
  6. web player php,unity web player是什么软件
  7. GB35114---基于openssl加密库进行开发(二)
  8. UMLChina公众号文章精选(20220807更新精选)
  9. PS教程:一分钟搞定 超简单PS皮肤美白方法
  10. Android Adapter详解
  11. Java选出偶数,Java 定义集合存入20个随机数字,通过自定义方法筛选偶数元素,放入新集合,打印输出...
  12. ubuntu下运行.exe程序
  13. 10019---mybatis的缓存机制(一级缓存二级缓存和刷新缓存)
  14. python取出数组大于某值_计算矩阵中大于某个值的所有值
  15. 二进制编译安装启动Redis时报权限不够(redis.service: Failed at step EXEC spawning xxx: Permission denied)
  16. 你与chatGPT有什么区别?
  17. 网页聊天窗口的自动输入内容及自动发送
  18. 《操作系统》2022年期末A卷
  19. 学习笔记(01):大数据金融信贷项目实战(Spark2.3.x Streaming)-Spark SQL架构原理
  20. Multisim10在51单片机仿真中的运用

热门文章

  1. windows installer正准备安装
  2. 维护高 Star Github 项目,会遇到什么有趣的问题 2022 版
  3. 职场小白新建SSM项目
  4. Laravel 用户认证中 attempt方法
  5. win7笔记本外接显示器html,笔记本接显示屏如何设置_笔记本外接显示屏的图文教程-win7之家...
  6. linux bios设置界面,BIOS怎么设置 史上最详细的bios设置图解教程
  7. Matlab最小面积包围四边形
  8. gm 1 n 模型matlab,灰色预测模型GM1,n模型的matlab源...
  9. 中国科学研成都计算机,中国科学院成都分院
  10. JVM学习笔记(二):垃圾回收、垃圾回收算法、垃圾回收器(Serial、Parallel、CMC、G1)、内存分配原则实战