一、前言
通过以下系列章节:
Spring Boot集成ShardingSphere实现数据分片(一) | Spring Cloud 40
Spring Boot集成ShardingSphere实现数据分片(二) | Spring Cloud 41
Spring Boot集成ShardingSphere实现数据分片(三) | Spring Cloud 42
Spring Boot集成ShardingSphere实现读写分离 | Spring Cloud 43
Spring Boot集成ShardingSphere实现按月数据分片及创建自定义分片算法 | Spring Cloud 44
Spring Boot集成ShardingSphere分片利器 AutoTable (一)—— 简单体验 | Spring Cloud 45
Spring Boot集成ShardingSphere分片利器 AutoTable (二)—— 自动分片算法示例 | Spring Cloud 46
ShardingSphere 5.3 系列Spring 配置升级指南 | Spring Cloud 47
对ShardingSphere
的数据分片、各分片算法应用、读写分离、最新版本升级等情况有了详细的了解,今天我们继续对其:数据加密和数据脱敏功能进行演示学习。
本章节中会应用以上系列章节的部分成功
二、背景
2.1 数据加密
安全控制一直是治理的重要环节,数据加密属于安全控制的范畴。 无论对互联网公司还是传统行业来说,数据安全一直是极为重视和敏感的话题。 数据加密是指对某些敏感信息通过加密规则进行数据的变形,实现敏感隐私数据的可靠保护。 涉及客户安全数据或者一些商业性敏感数据,如身份证号、手机号、卡号、客户号等个人信息按照相关部门规定,都需要进行数据加密。
对于数据加密的需求,在现实的业务场景中一般分为两种情况:
-
新业务上线,安全部门规定需将涉及用户敏感信息,例如银行、手机号码等进行加密后存储到数据库,在使用的时候再进行解密处理。因为是全新系统,因而没有存量数据清洗问题,所以实现相对简单。
-
已上线业务,之前一直将明文存储在数据库中。相关部门突然需要对已上线业务进行加密整改。这种场景一般需要处理 3 个问题:
- 历史数据需要如何进行加密处理,即洗数。
- 如何能在不改动业务
SQL
和逻辑情况下,将新增数据进行加密处理,并存储到数据库;在使用时,再进行解密取出。 - 如何较为安全、无缝、透明化地实现业务系统在明文与密文数据间的迁移。
2.2 数据脱敏
随着《网络安全法》的颁布施行,对个人隐私数据的保护已经上升到法律层面。传统的应用系统普遍缺少对个人隐私数据的保护措施。数据脱敏,可实现在不需要对生产数据库中的数据进行任何改变的情况下,依据用户定义的脱敏规则,对生产数据库返回的数据进行专门的加密、遮盖和替换,确保生产环境的敏感数据能够得到保护。
三、使用示例
示例采用
springboot
集成shardingsphere-jdbc方
式搭建。
3.1 项目总体结构
3.2 Maven依赖
shading-sphere/shading-encrypt-mask-5.3/pom.xml
:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>shading-sphere</artifactId>
<groupId>com.gm</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>shading-encrypt-mask-5.3</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core</artifactId>
<version>5.3.2</version>
</dependency>
<!-- 解决Mybatis中LocalDateTime和SQL中datetime的交互 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-typehandlers-jsr310</artifactId>
<version>1.0.2</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.33</version>
</dependency>
</dependencies>
</project>
-
shardingsphere-jdbc-core
使用最新的5.3.2
版本 -
JDBC
的ORM
框架选用mybatis-plus
3.3 配置文件
src/main/resources/application.yml:
server:
port: 8844
spring:
application:
name: @artifactId@
datasource:
driver-class-name: org.apache.shardingsphere.driver.ShardingSphereDriver
url: jdbc:shardingsphere:classpath:shading-auto-tables-encrypt-mask.yaml
mybatis:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
src/main/resources/shading-auto-tables-encrypt-mask.yaml
:
dataSources:
ds1:
dataSourceClassName: com.zaxxer.hikari.HikariDataSource
driverClassName: com.mysql.cj.jdbc.Driver
jdbcUrl: jdbc:mysql://192.168.0.35:3306/db1?useUnicode=true&characterEncoding=UTF-8&rewriteBatchedStatements=true&allowMultiQueries=true&serverTimezone=Asia/Shanghai
username: root
password: '1qaz@WSX'
connectionTimeoutMilliseconds: 30000
idleTimeoutMilliseconds: 60000
maxLifetimeMilliseconds: 1800000
maxPoolSize: 50
minPoolSize: 1
ds2:
dataSourceClassName: com.zaxxer.hikari.HikariDataSource
driverClassName: com.mysql.cj.jdbc.Driver
jdbcUrl: jdbc:mysql://192.168.0.46:3306/db2?useUnicode=true&characterEncoding=UTF-8&rewriteBatchedStatements=true&allowMultiQueries=true&serverTimezone=Asia/Shanghai
username: root
password: '1qaz@WSX'
connectionTimeoutMilliseconds: 30000
idleTimeoutMilliseconds: 60000
maxLifetimeMilliseconds: 1800000
maxPoolSize: 50
minPoolSize: 1
rules:
- !SHARDING
autoTables:
# 取模
t_auto_user_info_mod:
actualDataSources: ds$->{1..2}
shardingStrategy:
standard:
shardingColumn: user_id
shardingAlgorithmName: auto_user_info_mod
# 分布式序列策略
keyGenerateStrategy:
# 自增列名称,缺省表示不使用自增主键生成器
column: user_id
# 分布式序列算法名称
keyGeneratorName: snowflake
# 分片算法配置
shardingAlgorithms:
# 取模
auto_user_info_mod:
type: MOD
props:
sharding-count: 2
# 分布式序列算法配置(如果是自动生成的,在插入数据的sql中就不要传id,null也不行,直接插入字段中就不要有主键的字段)
keyGenerators:
# 分布式序列算法名称
snowflake:
# 分布式序列算法类型
type: SNOWFLAKE
- !MASK
tables:
t_auto_user_info_mod:
columns:
password:
maskAlgorithm: md5_mask
email:
maskAlgorithm: mask_before_special_chars_mask
telephone:
maskAlgorithm: keep_first_n_last_m_mask
maskAlgorithms:
md5_mask:
type: MD5
mask_before_special_chars_mask:
type: MASK_BEFORE_SPECIAL_CHARS
props:
special-chars: '@'
replace-char: '*'
keep_first_n_last_m_mask:
type: KEEP_FIRST_N_LAST_M
props:
first-n: 3
last-m: 4
replace-char: '*'
- !ENCRYPT
tables:
t_auto_user_info_mod:
columns:
password: # 加密列名称
cipherColumn: password # 密文列名称
encryptorName: password_encryptor # 密文列加密算法名称
assistedQueryColumn: assisted_query_password # 查询辅助列名称
assistedQueryEncryptorName: assisted_encryptor # 查询辅助列加密算法名称
id_card:
plainColumn: id_card_plain # 原文列名称(ShardingSphere自动创建列)
cipherColumn: id_card # 密文列名称
encryptorName: aes_encryptor # 密文列加密算法名称
assistedQueryColumn: assisted_query_id_card # 查询辅助列名称(ShardingSphere自动创建列,使用时: ..and id_card = '123...' and ..)
assistedQueryEncryptorName: assisted_encryptor # 查询辅助列加密算法名称
likeQueryColumn: like_query_id_card # 模糊查询列名称(ShardingSphere自动创建列,使用时: .. and id_card like '%123%' and ..)
likeQueryEncryptorName: like_encryptor # 模糊查询列加密算法名称
encryptors:
aes_encryptor:
type: AES
props:
aes-key-value: 123456abc
assisted_encryptor:
type: AES
props:
aes-key-value: 123456abc
password_encryptor:
type: MD5
like_encryptor:
type: CHAR_DIGEST_LIKE
props:
sql-show: true
配置简要说明:
逻辑表
t_auto_user_info_mod
按照user_id
分片键使用MOD
自动分片算法进行分片,
其中数据脱敏对:
- 列
password
采用基于MD5
的数据脱敏算法- 列
- 列
telephone
采用保留前n
后m
数据脱敏算法其中数据加密对:.
- 列
id_card
开启原文列、查询辅助列、模糊查询列,以上这些列在基于shardingsphere-jdbc-core
建表时会自动创建,在操作逻辑表的id_card
列时shardingsphere-jdbc-core
自动对其余列进行数据维护。- 列
password
开启查询辅助列,此列在基于shardingsphere-jdbc-core
建表时会自动创建,在操作逻辑表的password
列时shardingsphere-jdbc-core
自动对其余列进行数据维护。
ShardingSphere
更多数据脱敏配置,请见官网:
https://shardingsphere.apache.org/document/current/cn/user-manual/shardingsphere-jdbc/yaml-config/rules/mask/
ShardingSphere
内置提供了多种数据脱敏算法,请见官网:
https://shardingsphere.apache.org/document/current/cn/dev-manual/mask/
ShardingSphere
更多数据加密配置,请见官网:
https://shardingsphere.apache.org/document/5.3.0/cn/user-manual/shardingsphere-jdbc/java-api/rules/encrypt/
ShardingSphere
内置提供了多种数据加密算法,请见官网:
https://shardingsphere.apache.org/document/current/cn/dev-manual/encrypt/
3.4 实体类
com/gm/shading/encrypt/mask/entity/AutoUserInfoMod.java
:
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@Data
@TableName("t_auto_user_info_mod")
public class AutoUserInfoMod {
@TableId(type = IdType.ASSIGN_ID)
private Long userId;
// 身份证,存储加密
private String idCard;
// 密码,存储加密
private String password;
// 电话,数据脱敏
private String telephone;
// 电子邮箱,数据脱敏
private String email;
}
3.5 Mapper
com/gm/shading/encrypt/mask/mapper/AutoUserInfoModMapper.java
:
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.gm.shading.encrypt.mask.entity.AutoUserInfoMod;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface AutoUserInfoModMapper extends BaseMapper<AutoUserInfoMod> {
void save(AutoUserInfoMod autoOrder);
}
src/main/resources/mapper/AutoUserInfoModMapper.xml
:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.gm.shading.encrypt.mask.mapper.AutoUserInfoModMapper">
<insert id="save" parameterType="com.gm.shading.encrypt.mask.entity.AutoUserInfoMod">
insert into t_auto_user_info_mod (id_card,password,telephone,email)
values (#{idCard}, #{password}, #{telephone}, #{email})
</insert>
</mapper>
3.6 启动类
com/gm/shading/encrypt/mask/ShadingEncryptMaskApplication.java
:
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.gm.shading.encrypt.mask.mapper")
public class ShadingEncryptMaskApplication {
public static void main(String[] args) {
SpringApplication.run(ShadingEncryptMaskApplication.class, args);
}
}
3.7 单元测试
src/test/java/com/gm/shading/encrypt/mask/ShadingEncryptMaskApplicationTests.java
:
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.gm.shading.encrypt.mask.entity.AutoUserInfoMod;
import com.gm.shading.encrypt.mask.mapper.AutoUserInfoModMapper;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.jdbc.core.JdbcTemplate;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
@SpringBootTest
@Slf4j
public class ShadingEncryptMaskApplicationTests {
@Autowired
AutoUserInfoModMapper autoUserInfoModMapper;
@Autowired
JdbcTemplate jdbcTemplate;
@Test
public void testCreateAutUserInfoMod() {
jdbcTemplate.execute("CREATE TABLE `t_auto_user_info_mod` (\n" +
" `user_id` bigint(20) NOT NULL COMMENT '用户id',\n" +
" `id_card` varchar(50) NOT NULL COMMENT '身份证',\n" +
" `password` varchar(50) NOT NULL COMMENT '用户密码',\n" +
" `telephone` varchar(50) NOT NULL COMMENT '电话',\n" +
" `email` varchar(50) NOT NULL COMMENT '邮箱',\n" +
" PRIMARY KEY (`user_id`) USING BTREE\n" +
") ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;");
}
@Test
public void testInsertAutoUserInfoMod() {
List<String> idCards = Arrays.asList("34122119920825123", "42122319870601123", "32072319750626123",
"34120219900429123", "15222319970119123", "32038219891112123", "32072319950606123", "32038219900219123", "37132419880902123");
for (int i = 1; i < 6; i++) {
AutoUserInfoMod userInfo = new AutoUserInfoMod();
userInfo.setIdCard(idCards.get(i) + i);
userInfo.setPassword("abcdefg1234567" + 1);
userInfo.setTelephone("18645026410");
userInfo.setEmail("1025304567@qq.com");
autoUserInfoModMapper.save(userInfo);
}
}
@Test
public void testSelectAutoUserInfoModAll() {
List<AutoUserInfoMod> list = autoUserInfoModMapper.selectList(new QueryWrapper<AutoUserInfoMod>());
for (AutoUserInfoMod userInfo : list) {
log.info("{}", userInfo);
}
}
@Test
public void testSelectAutoUserInfoModByIdCard() {
List<Map<String, Object>> list = jdbcTemplate.queryForList("select * from t_auto_user_info_mod where id_card='320723197506261232'");
for (Map<String, Object> map : list) {
log.info("{}", map);
}
list = jdbcTemplate.queryForList("select * from t_auto_user_info_mod where id_card like '%3412%'");
for (Map<String, Object> map : list) {
log.info("{}", map);
}
}
}
- 执行
testCreateAutUserInfoMod
方法根据自动配置的分片规则进行建表
查看逻辑表的真实表结构:
此次会发现配置文件中定义原文列、查询辅助列、模糊查询列会自动完成创建
- 执行
testInsertAutoUserInfoMod
方法完成数据插入,数据加密规则生效
- 执行
testSelectAutoUserInfoModAll
方法完成数据查询,数据脱敏规则生效
- 执行
testSelectAutoUserInfoModByIdCard
方法完成数据查询,数据加密查询辅助列、模糊查询列规则生效