Spring Boot集成ShardingSphere实现数据加密及数据脱敏 | Spring Cloud 48

一、前言

通过以下系列章节:

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版本

  • JDBCORM 框架选用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 的数据脱敏算法
  • email采用特殊字符前遮盖数据脱敏算法
  • telephone采用保留前 nm 数据脱敏算法

其中数据加密对:.

  • 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("[email protected]");
            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方法完成数据查询,数据加密查询辅助列、模糊查询列规则生效

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/ctwy291314/article/details/130622557