数据脱敏是指对某些敏感信息通过脱敏规则进行数据的变形,实现敏感隐私数据的可靠保护。涉及客户安全数据或者一些商业性敏感数据,如身份证号、手机号、卡号、客户号等个人信息按照规定,都需要进行数据脱敏。

数据脱敏模块属于ShardingSphere分布式治理这一核心功能下的子功能模块。

  • 在更新操作时,它通过对用户输入的SQL进行解析,并依据用户提供的脱敏配置对SQL进行改写,从而实现对原文数据进行加密,并将密文数据存储到底层数据库。
  • 在查询数据时,它又从数据库中取出密文数据,并对其解密,最终将解密后的原始数据返回给用户。

Apache ShardingSphere自动化&透明化了数据脱敏过程,让用户无需关注数据脱敏的实现细节,像使用普通数据那样使用脱敏数据。

脱敏配置主要分为四部分:数据源配置,加密器配置,脱敏表配置以及查询属性配置,其详情如下图所示:

java 身份证脱敏后还原 数据库身份证脱敏_数据

  • 数据源配置:指DataSource的配置信息
  • 加密器配置:指使用什么加密策略进行加解密。目前ShardingSphere内置了两种加解密策略:AES/MD5
  • 脱敏表配置:指定哪个列用于存储密文数据(cipherColumn)、哪个列用于存储明文数据(plainColumn)以及用户想使用哪个列进行SQL编写(logicColumn)
  • 查询属性的配置:当底层数据库表里同时存储了明文数据、密文数据后,该属性开关用于决定是直接查询数据库表里的明文数据进行返回,还是查询密文数据通过Encrypt-JDBC解密后返回。

ShardingSphere提供了两种加密策略用于数据脱敏,该两种策略分别对应ShardingSphere的两种加解密的接口,即Encryptor和QueryAssistedEncryptor。

  • Encryptor

该解决方案通过提供encrypt(), decrypt()两种方法对需要脱敏的数据进行加解密。在用户进行INSERT, DELETE, UPDATE时,ShardingSphere会按照用户配置,对SQL进行解析、改写、路由,并会调用encrypt()将数据加密后存储到数据库, 而在SELECT时,则调用decrypt()方法将从数据库中取出的脱敏数据进行逆向解密,最终将原始数据返回给用户。

当前,ShardingSphere针对这种类型的脱敏解决方案提供了两种具体实现类,分别是MD5(不可逆),AES(可逆),用户只需配置即可使用这两种内置的方案。

  • QueryAssistedEncryptor

相比较于第一种脱敏方案,该方案更为安全和复杂。它的理念是:即使是相同的数据,如两个用户的密码相同,它们在数据库里存储的脱敏数据也应当是不一样的。这种理念更有利于保护用户信息,防止撞库成功。

它提供三种函数进行实现,分别是encrypt(), decrypt(), queryAssistedEncrypt()。在encrypt()阶段,用户通过设置某个变动种子,例如时间戳。针对原始数据+变动种子组合的内容进行加密,就能保证即使原始数据相同,也因为有变动种子的存在,致使加密后的脱敏数据是不一样的。在decrypt()可依据之前规定的加密算法,利用种子数据进行解密。queryAssistedEncrypt()用于生成辅助查询列,用于原始数据的查询过程。

当前,ShardingSphere针对这种类型的脱敏解决方案并没有提供具体实现类,却将该理念抽象成接口,提供给用户自行实现。ShardingSphere将调用用户提供的该方案的具体实现类进行数据脱敏。

数据脱敏实战

首先在ds-user库下创建用户表t_user

CREATE TABLE `t_user` (
  `id` bigint(50) NOT NULL,
  `name` varchar(255) DEFAULT NULL,
  `address` varchar(255) DEFAULT NULL,
  `age` int(3) DEFAULT NULL,
  `pwd_plain` varchar(256) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '密码明文',
  `pwd_cipher` varchar(256) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '密码密文',
  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

创建用户实体

package com.qjc.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.activerecord.Model;

import java.time.LocalDateTime;

import lombok.Data;
import lombok.EqualsAndHashCode;

/**
 * @ClassName: TUser
 * @Description:
 * @Author: qjc
 * @Date 2021-03-25 2:52 下午
 */
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("t_user")
public class TUser extends Model {

    private static final long serialVersionUID = 1L;

    @TableId(value = "id", type = IdType.AUTO)
    private Long id;

    private String name;

    private String address;

    private Integer age;

    private String pwd;

    private LocalDateTime createTime;

    private LocalDateTime updateTime;


}

使用Encrypt-JDBC进行增删改查时,其中的处理流程和转换逻辑,如下图所示。

java 身份证脱敏后还原 数据库身份证脱敏_spring_02

 最后就是配置

#datasource
spring.shardingsphere.datasource.names=ds-2

spring.shardingsphere.datasource.ds-2.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.ds-2.driverClassName=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds-2.url=jdbc:mysql://127.0.0.1:3306/ds-user?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true
spring.shardingsphere.datasource.ds-2.username=root
spring.shardingsphere.datasource.ds-2.password=123456

# 数据脱敏规则
# 使用AES加密算法加密
spring.shardingsphere.encrypt.encryptors.aes_pwd.type=aes
spring.shardingsphere.encrypt.encryptors.aes_pwd.props.aes.key.value=123456
# 使用MD5加密算法加密
spring.shardingsphere.encrypt.encryptors.md5_pwd.type=MD5
spring.shardingsphere.encrypt.encryptors.md5_pwd.props.md5.key.value=123456

# 原文列(注意pwd是逻辑列)
#spring.shardingsphere.encrypt.tables.t_user.columns.pwd.plain-column=pwd_plain
# 密文列
spring.shardingsphere.encrypt.tables.t_user.columns.pwd.cipher-column=pwd_cipher
# 加密器(选择上面定义好的数据脱敏规则aes_pwd/md5_pwd
spring.shardingsphere.encrypt.tables.t_user.columns.pwd.encryptor=md5_pwd

# 是否用密文查询(这时可以不要明文列)
# 如果设置为false,用删除明文列,用明文密码是查询不到数据的;
# 如果设置为true,可以删除明文列,用明文密码可以查询到数据,但是是加密的数据
spring.shardingsphere.props.query.with.cipher.column=true

测试数据加密

package com.qjc;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.qjc.entity.TUser;
import com.qjc.mapper.TUserMapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.Repeat;
import org.springframework.test.context.junit4.SpringRunner;

import javax.annotation.Resource;
import java.util.List;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = ShardingSphereDemoApplication.class)
public class TestEncryptor {

    @Resource
    private TUserMapper userMapper;

    @Test
    @Repeat(1)  // 执行次数
    public void testAdd() {
        TUser user = new TUser();
        user.setName("tiger");
        user.setAddress("北京");
        user.setAge(18);
        user.setPwd("abc");
        userMapper.insert(user);
    }

    @Test
    public void testFind() {
        QueryWrapper<TUser> queryWrapper = new QueryWrapper();
        queryWrapper
                .lambda()
                .eq(TUser::getPwd, "abc")
        ;
        List<TUser> list = userMapper.selectList(queryWrapper);
        list.forEach(user -> {
            System.out.println(user.getId() + " " + user.getName() + " " + user.getPwd());
        });
    }

}

插入数据结果如下:

java 身份证脱敏后还原 数据库身份证脱敏_bc_03

 

 查询数据结果如下:

java 身份证脱敏后还原 数据库身份证脱敏_spring_04