对互联网公司、传统行业来说,数据安全一直是极为重视和敏感的话题。数据脱敏是指对某些敏感信息通过脱敏规则进行数据的变形,实现敏感隐私数据的可靠保护。涉及客户安全数据或者一些商业性敏感数据,如身份证号、手机号、卡号、地址、客户号等个人信息按照相关部门规定,都需要进行数据脱敏。
应安全审计要求,需要对数据库中的用户关键信息做加密处理,这样,即使生产数据被拖库,也不会泄露用户的敏感信息。
使用sharding-jdbc实现数据脱敏,比如:用户表中的password字段,日常开发都会进行数据脱敏,对其进行加密**。本文源代码使用Apache2.0开源协议,代码下载地址。
附:shardingsphere官网-数据脱敏介绍、shardingsphere官网-数据脱敏Yaml配置
项目的目录结构:
一、引入相关依赖
核心依赖
<!-- 实现对 Sharding-JDBC 的自动化配置 -->
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>4.0.0-RC2</version>
</dependency>
完整依赖
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.erbadagang.sharding.jdbc</groupId>
<artifactId>sharding-jdbc-data-cipher</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>sharding-jdbc-data-cipher</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.3.0.RELEASE</spring-boot.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 实现对数据库连接池的自动化配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency> <!-- 本示例,我们使用 MySQL -->
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.48</version>
</dependency>
<!-- 实现对 MyBatis 的自动化配置 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
<!-- 实现对 Sharding-JDBC 的自动化配置 -->
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>4.0.0-RC2</version>
</dependency>
<!-- 保证 Spring AOP 相关的依赖包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- 方便等会写单元测试 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
二、 application.yml:
############################数据脱敏#################################
#服务端口
server:
port: 8080
#服务名
spring:
application:
name: sharding-jdbc-data-cipher
main:
allow-bean-definition-overriding: true
shardingsphere:
datasource: #数据源配置
names: ds
ds:
#url: jdbc:mysql://192.168.24.140:3306/test1?useSSL=false&useUnicode=true&serverTimezone=UTC
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.zaxxer.hikari.HikariDataSource # 使用 Hikari 数据库连接池
#driver-class-name: com.mysql.jdbc.Driver
jdbc-url: jdbc:mysql://101.133.227.13:3306/orders_1?useSSL=false&useUnicode=true&characterEncoding=UTF-8
username: guo
password: 205010guo
encrypt:
encryptors:
encryptor_aes:
type: aes #加解密器类型,可自定义或选择内置类型:MD5/AES
props:
aes.key.value: guoxiuzhi-IA8 #属性配置, 注意:使用AES加密器,需要配置AES加密器的KEY属性:aes.key.value
qualifiedColumns: t_user.password
tables:
t_user:
columns:
password:
cipherColumn: password #存储密文的字段
encryptor: encryptor_aes #加解密器名字
props:
sql:
show: true #是否开启SQL显示,默认值: false
query:
with:
cipher:
column: true #是否使用密文列查询
三、建表语句
CREATE TABLE `orders_1`.`t_user` (
`id` bigint(0) NOT NULL AUTO_INCREMENT,
`user_name` varchar(25),
`password` varchar(55),
PRIMARY KEY (`id`)
);
四、UserDao:操作数据库接口
package com.erbadagang.sharding.jdbc.shardingjdbcdatacipher.dao;
import com.erbadagang.sharding.jdbc.shardingjdbcdatacipher.entity.User;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.springframework.stereotype.Repository;
/**
* @description dao数据接口,mybatis数据库操作类。
* @ClassName: UserDao
* @author: 郭秀志 jbcode@126.com
* @date: 2020/7/2 15:58
* @Copyright:
*/
@Repository
public interface UserDao {
@Insert("INSERT INTO t_user (user_name,password) VALUES (#{userName}, #{password})")
void addUser(@Param("userName") String userName, @Param("password") String password);
@Select("SELECT id,user_name as userName,password FROM t_user WHERE user_name=#{userName} and password= #{password}")
User selectUser(@Param("userName") String userName, @Param("password") String password);
}
五、 User实体类 :
package com.erbadagang.sharding.jdbc.shardingjdbcdatacipher.entity;
import lombok.Data;
import java.io.Serializable;
/**
* @description 用户数据entity
* @ClassName: User
* @author: 郭秀志 jbcode@126.com
* @date: 2020/7/2 15:57
* @Copyright:
*/
@Data
public class User implements Serializable {
private Long id;
private String userName;
private String password;
}
六、启动类
package com.erbadagang.sharding.jdbc.shardingjdbcdatacipher;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @description 项目启动类
* @ClassName: ShardingJdbcDataCipherApplication
* @author: 郭秀志 jbcode@126.com
* @date: 2020/7/2 16:12
* @Copyright:
*/
@SpringBootApplication
@MapperScan(value = "com.erbadagang.sharding.jdbc.shardingjdbcdatacipher.dao")
public class ShardingJdbcDataCipherApplication {
public static void main(String[] args) {
SpringApplication.run(ShardingJdbcDataCipherApplication.class, args);
}
}
七、测试类
package com.erbadagang.sharding.jdbc.shardingjdbcdatacipher;
import com.erbadagang.sharding.jdbc.shardingjdbcdatacipher.dao.UserDao;
import com.erbadagang.sharding.jdbc.shardingjdbcdatacipher.entity.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
/**
* @description 数据脱敏测试类,包括insert后数据库密文存储,及加密字段查询功能。
* @ClassName: ShardingJdbcDataCipherApplicationTests
* @author: 郭秀志 jbcode@126.com
* @date: 2020/7/2 16:14
* @Copyright:
*/
@SpringBootTest
class ShardingJdbcDataCipherApplicationTests {
@Test
void contextLoads() {
}
@Autowired
private UserDao userDao;
@Test
void add() {
userDao.addUser("guo", "123456");
}
@Test
public void query() {
User user = userDao.selectUser("guo", "123456");
System.out.println(user);
}
}
八、测试结果
8.1 add()方法运行结果
2020-07-02 16:05:45.587 INFO 16188 --- [main] ShardingSphere-SQL: Rule Type: encrypt
2020-07-02 16:05:45.587 INFO 16188 --- [main] ShardingSphere-SQL: SQL: INSERT INTO t_user (user_name,password) VALUES (?, ?)
8.2 query()方法运行结果
2020-07-02 16:05:45.784 INFO 16188 --- [main] ShardingSphere-SQL: Rule Type: encrypt
2020-07-02 16:05:45.784 INFO 16188 --- [main] ShardingSphere-SQL: SQL: SELECT id,user_name as userName,password FROM t_user WHERE user_name=? and password = ?
User(id=1, userName=guo, password=123456)
九、其他特性
9.1 plainColumn保存明文的字段
application.yml改造:
tables:
t_user:
columns:
password: #logicColumn逻辑列名称
plainColumn: password #存储明文的字段
cipherColumn: pwd_cipher #存储密文的字段
encryptor: encryptor_aes #加解密器名字
新增字段的SQL语句:
ALTER TABLE `orders_1`.`t_user`
ADD COLUMN `pwd_cipher` varchar(55) AFTER `password`;
依据上述加密规则可知,首先需要在数据库表 t_user 里新增一个字段叫做 pwd_cipher
,即cipherColumn
,用于存放密文数据,同时我们把 plainColumn
设置为 password
,用于存放明文数据,而把logicColumn
也设置为password
。 由于之前的代码 SQL 就是使用 password
进行编写,即面向逻辑列进行 SQL 编写,所以业务代码无需改动。
对应的sql依旧是:
@Insert("INSERT INTO t_user (user_name,password) VALUES (#{userName}, #{password})")
void addUser(@Param("userName") String userName, @Param("password") String password);
通过 Apache ShardingSphere,针对新增的数据,会把明文写到 password
列,并同时把明文进行加密存储到pwd_cipher
列。 此时,如果 query.with.cipher.column
设置为false
,对业务应用来说,依旧使用 password
这一明文列进行查询存储,却在底层数据库表pwd_cipher
上额外存储了新增数据的密文数据。
再次运行测试,数据库中同时保存明文和密文数据: