sharding-sphere 数据加密(附源码)

sharding-sphere 数据加密

SpringBoot + sharding-sphere 5.2.1 + mybatis-plus 字段加密 源码

1. 处理流程

​ Apache ShardingSphere 通过对用户输入的 SQL 进行解析,并依据用户提供的加密规则对 SQL 进行改写,从而实现对原文数据进行加密,并将原文数据(可选)及密文数据同时存储到底层数据库。 在用户查询数据时,它仅从数据库中取出密文数据,并对其解密,最终将解密后的原始数据返回给用户。 Apache ShardingSphere 自动化 & 透明化了数据加密过程,让用户无需关注数据加密的实现细节,像使用普通数据那样使用加密数据。

2. 整体架构

在这里插入图片描述

​ 加密模块将用户发起的 SQL 进行拦截,并通过 SQL 语法解析器进行解析、理解 SQL 行为,再依据用户传入的加密规则,找出需要加密的字段和所使用的加解密算法对目标字段进行加解密处理后,再与底层数据库进行交互。 Apache ShardingSphere 会将用户请求的明文进行加密后存储到底层数据库;并在用户查询时,将密文从数据库中取出进行解密后返回给终端用户。 通过屏蔽对数据的加密处理,使用户无需感知解析 SQL、数据加密、数据解密的处理过程,就像在使用普通数据一样使用加密数据。

3. 加密规则

在详解整套流程之前,我们需要先了解下加密规则与配置,这是认识整套流程的基础。加密配置主要分为三部分:数据源配置,加密算法配置,加密表配置,其详情如下图所示:
在这里插入图片描述

4. 实战源码

4.1 建表
CREATE TABLE `t_user_info_encryption` (
  `id` bigint NOT NULL COMMENT '主键',
  `name` varchar(255) DEFAULT NULL COMMENT '名称',
  `password` varchar(255) DEFAULT NULL COMMENT '密码',
  `company` varchar(255) DEFAULT NULL COMMENT '公司',
  `account` varchar(255) DEFAULT NULL COMMENT '账号',
  `nike_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '昵称',
  `address` varchar(255) DEFAULT NULL COMMENT '地址',
  `id_card` varchar(255) DEFAULT NULL COMMENT '身份证号',
  `phone_number` varchar(255) DEFAULT NULL COMMENT '电话号码',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
4.2 引入pom
<shardingsphere-jdbc.version>5.2.1</shardingsphere-jdbc.version>
<dependency>
    <groupId>org.apache.shardingsphere</groupId>
    <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
    <version>${shardingsphere-jdbc.version}</version>
</dependency>
<!-- 如果项目启动报错: 
	'void org.apache.shardingsphere.infra.util.yaml.constructor.ShardingSphereYamlConstructor$1.setCodePointLimit(int)'需要引入 org.yaml.snakeyaml -->
<dependency>
    <groupId>org.yaml</groupId>
    <artifactId>snakeyaml</artifactId>
    <version>1.33</version>
</dependency>
4.3 创建实体
@Data
@TableName("t_user_info_encryption")
public class TUserInfoEncryption implements Serializable {
    @TableId(type = IdType.ASSIGN_ID)
    private Long id;
    private String name;
    private String password;
    private String company;
    private String account;
    private String nikeName;
    private String address;
    private String idCard;
    private String phoneNumber;
    @JsonFormat(pattern = DatePattern.NORM_DATETIME_PATTERN)
    private LocalDateTime createTime;
}
4.4 配置加密规则
spring:
  shardingsphere:
    datasource:
      name: master
      # 主数据源
      master:
        name: master
        driver-class-name: com.mysql.cj.jdbc.Driver
        type: com.zaxxer.hikari.HikariDataSource
        jdbc-url: jdbc:mysql://localhost:3306/sharding-sphere?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
        username: root
        password: 123456
    rules:
      # 加密配置
      encrypt:
        encryptors:
          # 加密算法组
          user_encryption_aes:
            # 可逆
            type: AES
            props:
              aes-key-value: 123456abc
          user_encryption_md5:
            # 不可逆
            type: MD5
        tables:
          # 表名
          t_user_info_encryption:
            # 字段组
            columns:
              password:
                cipher-column: password
                # 与上述配置名称对应
                encryptor-name: user_encryption_md5
              name:
                cipher-column: name
                # 与上述配置名称对应
                encryptor-name: user_encryption_aes

    # 展示shardingSphere对SQL的处理
    props:
      sql:
        show: true
  main:
    allow-bean-definition-overriding: true
4.5 写入数据进行验证
4.5.1 写入数据
@Test
public void encryptionTest() {
    TUserInfoEncryption tUserInfoEncryption = new TUserInfoEncryption();
    tUserInfoEncryption.setPassword("123456");
    tUserInfoEncryption.setName("名字");
    tUserInfoEncryption.setCompany("公司名称");
    tUserInfoEncryption.setCreateTime(LocalDateTime.now());
    tUserInfoEncryptionService.save(tUserInfoEncryption);

    List<TUserInfoEncryption> list = tUserInfoEncryptionService.list();
    log.info(JSONObject.toJSONString(list));
}
4.5.2 到数据库中查看,name已经是秘文,password 是被md5摘要的数据。

在这里插入图片描述

4.5.3 查询数据

name字段使用AES加密是可逆的,password使用md5摘要不可逆。
在这里插入图片描述

4.6 自定义加密算法

目前版本已经实现MD5、SM3、SM4、AES、RC4算法,开箱即用。但是遇到某些特定场景,我们需要自己定义加密算法。

4.6.1 AES 算法剖析
/**
 * AES 算法(部分代码)
 *实现了EncryptAlgorithm接口
 */
public final class AESEncryptAlgorithm implements EncryptAlgorithm<Object, String> {
  	# 对应配置中的 aes-key-value 用于生成加密密钥
    private static final String AES_KEY = "aes-key-value";
    @Getter
    private Properties props;
    private byte[] secretKey;
    # 初始化算法
    @Override
    public void init(final Properties props) {
        this.props = props;
        secretKey = createSecretKey(props);
    }
    # 使用 aes-key-value 配置的值生成密钥
    private byte[] createSecretKey(final Properties props) {
        Preconditions.checkArgument(props.containsKey(AES_KEY), "%s can not be null.", AES_KEY);
        return Arrays.copyOf(DigestUtils.sha1(props.getProperty(AES_KEY)), 16);
    }
    
   	# 加密数据
    @SneakyThrows(GeneralSecurityException.class)
    @Override
    public String encrypt(final Object plainValue, final EncryptContext encryptContext) {
        if (null == plainValue) {
            return null;
        }
        byte[] result = getCipher(Cipher.ENCRYPT_MODE).doFinal(String.valueOf(plainValue).getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(result);
    }
    
  	# 解密数据
    @SneakyThrows(GeneralSecurityException.class)
    @Override
    public Object decrypt(final String cipherValue, final EncryptContext encryptContext) {
        if (null == cipherValue) {
            return null;
        }
        byte[] result = getCipher(Cipher.DECRYPT_MODE).doFinal(Base64.getDecoder().decode(cipherValue));
        return new String(result, StandardCharsets.UTF_8);
    }
    
    private Cipher getCipher(final int decryptMode) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException {
        Cipher result = Cipher.getInstance(getType());
        result.init(decryptMode, new SecretKeySpec(secretKey, getType()));
        return result;
    }
  	# 加密类型,在配置中体现
    @Override
    public String getType() {
        return "AES";
    }
}

4.6.2 自定义加密算法

这里我就使用简单的base64转码为例。

  • 编写加密算法
import cn.hutool.core.codec.Base64;
import lombok.Getter;
import org.apache.shardingsphere.encrypt.spi.EncryptAlgorithm;
import org.apache.shardingsphere.encrypt.spi.context.EncryptContext;
import org.springframework.context.annotation.Configuration;

import java.util.Properties;

/**
 * 描述 Base64转码算法
 *
 * @author liyadong
 * @date 2024-05-15 14:04
 */
@Getter
public class Base64EncryptAlgorithm implements EncryptAlgorithm<String, String> {
    private Properties props;
    /**
     * 描述      初始化算法
     * @param	props	配置信息
     * @author liyadong
     * @date 2024/5/15 15:27
     */
    @Override
    public void init(final Properties props) {
        this.props = props;
    }
    @Override
    public String getType() {
        // 返回自定义加密算法的名称
        return "BASE64";
    }
    /**
     * 描述   加密
     * @param	plainValue      明文
     * @param	encryptContext  加密信息(加密字段、表名等)
     * @author liyadong
     * @date 2024/5/15 15:27
     * @return java.lang.String
     */
    @Override
    public String encrypt(final String plainValue, EncryptContext encryptContext) {
        return plainValue == null ? null : Base64.encode(plainValue);
    }
    /**
     * 描述      解密
     * @param	encode	        秘文
     * @param	encryptContext  加密信息(加密字段、表名等)
     * @author liyadong
     * @date 2024/5/15 15:28
     * @return java.lang.String
     */
    @Override
    public String decrypt(String encode, EncryptContext encryptContext) {
        return Base64.decodeStr(encode);
    }
}

  • 增加配置
spring:
  shardingsphere:
    rules:
      # 加密配置
      encrypt:
        encryptors:
          user_encryption_base64:
            type: BASE64
        tables:
          # 表名
          t_user_info_encryption:
            # 字段组
            columns:
              company:
                cipher-column: company
                encryptor-name: user_encryption_base64
  • SPI 配置
    1. resources目录下新建目录 META-INF\services,创建文件 org.apache.shardingsphere.sharding.spi.ShardingAlgorithm;
    2. 配置自定义加密算法 你的包名.Base64EncryptAlgorithm。如下图:
      在这里插入图片描述
  • 插入并查询数据
@Test
public void encryptionTest() {
    TUserInfoEncryption tUserInfoEncryption = new TUserInfoEncryption();
    tUserInfoEncryption.setPassword("123456");
    tUserInfoEncryption.setName("名字");
    tUserInfoEncryption.setCompany("公司名称");
    tUserInfoEncryption.setCreateTime(LocalDateTime.now());
    tUserInfoEncryptionService.save(tUserInfoEncryption);

    List<TUserInfoEncryption> list = tUserInfoEncryptionService.list();
    log.info(JSONObject.toJSONString(list));
}
  • 查看结果

    company字段已经被base64转码。

在这里插入图片描述
查询结果为明文
在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值