背景
Apache ShardingSphere 通过对用户输入的 SQL 进行解析,并依据用户提供的加密规则对 SQL 进行改写,从而实现对原文数据进行加密,并将原文数据(可选)及密文数据同时存储到底层数据库。在用户查询数据时,它仅从数据库中取出密文数据,并对其解密,最终将解密后的原始数据返回给用户。Apache ShardingSphere 自动化 & 透明化了数据加密过程,让用户无需关注数据加密的实现细节,像使用普通数据那样使用加密数据。此外,无论是已在线业务进行加密改造,还是新上线业务使用加密功能,Apache ShardingSphere 都可以提供一套相对完善的解决方案。

解决方案
参考官方文档分两种场景
- 新上线业务
- 已上线业务
本文首先介绍新上线业务的新操,它相对已上线业务简单许多
新上线业务

版本信息
- SpringBoot 2
- ShardingSphere 5
- MySQL 8
引入依赖
1 <dependency>
2 <groupId>org.apache.shardingsphere</groupId>
3 <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
4 <version>5.0.0-beta</version>
5</dependency>
配置文件:
1spring:
2 profiles:
3 include: common-local
4 shardingsphere:
5 datasource:
6 names: write-ds,read-ds-0
7 write-ds:
8 jdbcUrl: jdbc:mysql://mysql.local.test.myallapp.com:23306/test?allowPublicKeyRetrieval=true&useSSL=false&allowMultiQueries=true&serverTimezone=Asia/Shanghai&useSSL=false&autoReconnect=true&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull
9 type: com.zaxxer.hikari.HikariDataSource
10 driver-class-name: com.mysql.cj.jdbc.Driver
11 username: root
12 password: nicai
13 connectionTimeoutMilliseconds: 3000
14 idleTimeoutMilliseconds: 60000
15 maxLifetimeMilliseconds: 1800000
16 maxPoolSize: 50
17 minPoolSize: 1
18 maintenanceIntervalMilliseconds: 30000
19 read-ds-0:
20 jdbcUrl: jdbc:mysql://mysql.local.test.read1.myallapp.com:23306/test?allowPublicKeyRetrieval=true&useSSL=false&allowMultiQueries=true&serverTimezone=Asia/Shanghai&useSSL=false&autoReconnect=true&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull
21 type: com.zaxxer.hikari.HikariDataSource
22 driver-class-name: com.mysql.cj.jdbc.Driver
23 username: root
24 password: nicai
25 connectionTimeoutMilliseconds: 3000
26 idleTimeoutMilliseconds: 60000
27 maxLifetimeMilliseconds: 1800000
28 maxPoolSize: 50
29 minPoolSize: 1
30 maintenanceIntervalMilliseconds: 30000
31 rules:
32 readwrite-splitting:
33 data-sources:
34 glapp:
35 write-data-source-name: write-ds
36 read-data-source-names:
37 - read-ds-0
38 load-balancer-name: roundRobin # 负载均衡算法名称
39 load-balancers:
40 roundRobin:
41 type: ROUND_ROBIN # 一共两种一种是 RANDOM(随机),一种是 ROUND_ROBIN(轮询)
42 encrypt:
43 encryptors:
44 mobile-encryptor:
45 props:
46 aes-key-value: 123456abc
47 type: AES
48 tables:
49 t_cipher_new:
50 columns:
51 mobile:
52 cipher-column: mobile # 加密列名称
53 encryptor-name: mobile-encryptor # 加密算法名称(名称不能有下划线)
54 # plain-column: mobile # 原文列名称
55 queryWithCipherColumn: true # 是否使用加密列进行查询。在有原文列的情况下,可以使用原文列进行查询
我是将读写分离和加密的配置混合在一块儿了,实际上本文只需要关注 encrypt 节点部分
配置上有几个注意的点:
- key 名称中间不要带下划线,比如
mobile-encryptor这个自定义的名字,之前找了半天原因,忘了规则了。 - 由于这部分演示的是新业务,所以将加密列和原文列用一列表示(mobile), 而逻辑列也是这列。
建表语句:
1
2CREATE TABLE `t_cipher_new` (
3 `id` bigint(20) NOT NULL,
4 `name` varchar(255) DEFAULT NULL,
5 `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
6 `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
7 `pwd` varchar(100) DEFAULT NULL,
8 `mobile` varchar(100) DEFAULT NULL,
9 PRIMARY KEY (`id`)
10) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
测试
先通过业务接口插入一条数据:

可以看到 mobile 字段已经自动加密了,明文是1234567密文是 FGPDcFbE1uWPwPUOeRpKbw==
我们再通过业务接口查询一下这条数据
1{
2 "code": 100000,
3 "msg": "",
4 "data": {
5 "id": 1440855970338058241,
6 "name": "hello",
7 "pwd": "123",
8 "mobile": "1234567",
9 "createTime": "2021-09-23 09:50:09",
10 "updateTime": "2021-09-23 09:50:09"
11 }
12}
可以看到,查询出来的 mobile 字段数据是解密以后的 1234567
