疯狂的狮子li
2023-02-05 c59122a194edbeb58a3a9e5eb4d0142e6202f447
add 新增 迁移 4.X 数据库加解密功能
已添加21个文件
已修改4个文件
1093 ■■■■■ 文件已修改
pom.xml 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/pom.xml 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-bom/pom.xml 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-encrypt/pom.xml 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/annotation/EncryptField.java 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/config/EncryptorAutoConfiguration.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/core/EncryptContext.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/core/EncryptorManager.java 94 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/core/IEncryptor.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/core/encryptor/AbstractEncryptor.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/core/encryptor/AesEncryptor.java 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/core/encryptor/Base64Encryptor.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/core/encryptor/RsaEncryptor.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/core/encryptor/Sm2Encryptor.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/core/encryptor/Sm4Encryptor.java 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/enumd/AlgorithmType.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/enumd/EncodeType.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/interceptor/MybatisDecryptInterceptor.java 108 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/interceptor/MybatisEncryptInterceptor.java 115 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/properties/EncryptorProperties.java 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/ruoyi-common-encrypt/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/pom.xml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestEncryptController.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/TestDemoEncrypt.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/mapper/TestDemoEncryptMapper.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pom.xml
@@ -36,6 +36,7 @@
        <alibaba-ttl.version>2.14.2</alibaba-ttl.version>
        <xxl-job.version>2.3.1</xxl-job.version>
        <lombok.version>1.18.24</lombok.version>
        <bouncycastle.version>1.72</bouncycastle.version>
        <!-- ä¸´æ—¶ä¿®å¤ snakeyaml æ¼æ´ž -->
        <snakeyaml.version>1.33</snakeyaml.version>
@@ -286,6 +287,13 @@
                <version>${snakeyaml.version}</version>
            </dependency>
            <!-- åŠ å¯†åŒ…å¼•å…¥ -->
            <dependency>
                <groupId>org.bouncycastle</groupId>
                <artifactId>bcprov-jdk15to18</artifactId>
                <version>${bouncycastle.version}</version>
            </dependency>
            <dependency>
                <groupId>com.ruoyi</groupId>
                <artifactId>ruoyi-system</artifactId>
ruoyi-common/pom.xml
@@ -30,6 +30,7 @@
        <module>ruoyi-common-translation</module>
        <module>ruoyi-common-sensitive</module>
        <module>ruoyi-common-json</module>
        <module>ruoyi-common-encrypt</module>
    </modules>
    <artifactId>ruoyi-common</artifactId>
ruoyi-common/ruoyi-common-bom/pom.xml
@@ -145,6 +145,13 @@
                <version>${revision}</version>
            </dependency>
            <!-- æ•°æ®åº“加解密模块 -->
            <dependency>
                <groupId>com.ruoyi</groupId>
                <artifactId>ruoyi-common-encrypt</artifactId>
                <version>${revision}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
ruoyi-common/ruoyi-common-encrypt/pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,43 @@
<?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>
        <groupId>com.ruoyi</groupId>
        <artifactId>ruoyi-common</artifactId>
        <version>${revision}</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>ruoyi-common-encrypt</artifactId>
    <description>
        ruoyi-common-encrypt æ•°æ®åŠ è§£å¯†æ¨¡å—
    </description>
    <dependencies>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bcprov-jdk15to18</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-crypto</artifactId>
        </dependency>
    </dependencies>
</project>
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/annotation/EncryptField.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,44 @@
package com.ruoyi.common.encrypt.annotation;
import com.ruoyi.common.encrypt.enumd.AlgorithmType;
import com.ruoyi.common.encrypt.enumd.EncodeType;
import java.lang.annotation.*;
/**
 * å­—段加密注解
 *
 * @author è€é©¬
 */
@Documented
@Inherited
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface EncryptField {
    /**
     * åŠ å¯†ç®—æ³•
     */
    AlgorithmType algorithm() default AlgorithmType.DEFAULT;
    /**
     * ç§˜é’¥ã€‚AES、SM4需要
     */
    String password() default "";
    /**
     * å…¬é’¥ã€‚RSA、SM2需要
     */
    String publicKey() default "";
    /**
     * å…¬é’¥ã€‚RSA、SM2需要
     */
    String privateKey() default "";
    /**
     * ç¼–码方式。对加密算法为BASE64的不起作用
     */
    EncodeType encode() default EncodeType.DEFAULT;
}
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/config/EncryptorAutoConfiguration.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,39 @@
package com.ruoyi.common.encrypt.config;
import com.ruoyi.common.encrypt.core.EncryptorManager;
import com.ruoyi.common.encrypt.interceptor.MybatisDecryptInterceptor;
import com.ruoyi.common.encrypt.interceptor.MybatisEncryptInterceptor;
import com.ruoyi.common.encrypt.properties.EncryptorProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
/**
 * åŠ è§£å¯†é…ç½®
 *
 * @author è€é©¬
 * @version 4.6.0
 */
@AutoConfiguration
@ConditionalOnProperty(value = "mybatis-encryptor.enable", havingValue = "true")
public class EncryptorAutoConfiguration {
    @Autowired
    private EncryptorProperties properties;
    @Bean
    public EncryptorManager encryptorManager() {
        return new EncryptorManager();
    }
    @Bean
    public MybatisEncryptInterceptor mybatisEncryptInterceptor(EncryptorManager encryptorManager) {
        return new MybatisEncryptInterceptor(encryptorManager, properties);
    }
    @Bean
    public MybatisDecryptInterceptor mybatisDecryptInterceptor(EncryptorManager encryptorManager) {
        return new MybatisDecryptInterceptor(encryptorManager, properties);
    }
}
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/core/EncryptContext.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,41 @@
package com.ruoyi.common.encrypt.core;
import com.ruoyi.common.encrypt.enumd.AlgorithmType;
import com.ruoyi.common.encrypt.enumd.EncodeType;
import lombok.Data;
/**
 * åŠ å¯†ä¸Šä¸‹æ–‡ ç”¨äºŽencryptor传递必要的参数。
 *
 * @author è€é©¬
 * @version 4.6.0
 */
@Data
public class EncryptContext {
    /**
     * é»˜è®¤ç®—法
     */
    private AlgorithmType algorithm;
    /**
     * å®‰å…¨ç§˜é’¥
     */
    private String password;
    /**
     * å…¬é’¥
     */
    private String publicKey;
    /**
     * ç§é’¥
     */
    private String privateKey;
    /**
     * ç¼–码方式,base64/hex
     */
    private EncodeType encode;
}
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/core/EncryptorManager.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,94 @@
package com.ruoyi.common.encrypt.core;
import cn.hutool.core.util.ReflectUtil;
import com.ruoyi.common.encrypt.annotation.EncryptField;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
/**
 * åŠ å¯†ç®¡ç†ç±»
 *
 * @author è€é©¬
 * @version 4.6.0
 */
@Slf4j
public class EncryptorManager {
    /**
     * ç¼“存加密器
     */
    Map<EncryptContext, IEncryptor> encryptorMap = new ConcurrentHashMap<>();
    /**
     * ç±»åŠ å¯†å­—æ®µç¼“å­˜
     */
    Map<Class<?>, Set<Field>> fieldCache = new ConcurrentHashMap<>();
    /**
     * èŽ·å–ç±»åŠ å¯†å­—æ®µç¼“å­˜
     */
    public Set<Field> getFieldCache(Class<?> sourceClazz) {
        return fieldCache.computeIfAbsent(sourceClazz, clazz -> {
            Field[] declaredFields = clazz.getDeclaredFields();
            Set<Field> fieldSet = Arrays.stream(declaredFields).filter(field ->
                    field.isAnnotationPresent(EncryptField.class) && field.getType() == String.class)
                .collect(Collectors.toSet());
            for (Field field : fieldSet) {
                field.setAccessible(true);
            }
            return fieldSet;
        });
    }
    /**
     * æ³¨å†ŒåŠ å¯†æ‰§è¡Œè€…åˆ°ç¼“å­˜
     *
     * @param encryptContext åŠ å¯†æ‰§è¡Œè€…éœ€è¦çš„ç›¸å…³é…ç½®å‚æ•°
     */
    public IEncryptor registAndGetEncryptor(EncryptContext encryptContext) {
        if (encryptorMap.containsKey(encryptContext)) {
            return encryptorMap.get(encryptContext);
        }
        IEncryptor encryptor = ReflectUtil.newInstance(encryptContext.getAlgorithm().getClazz(), encryptContext);
        encryptorMap.put(encryptContext, encryptor);
        return encryptor;
    }
    /**
     * ç§»é™¤ç¼“存中的加密执行者
     *
     * @param encryptContext åŠ å¯†æ‰§è¡Œè€…éœ€è¦çš„ç›¸å…³é…ç½®å‚æ•°
     */
    public void removeEncryptor(EncryptContext encryptContext) {
        this.encryptorMap.remove(encryptContext);
    }
    /**
     * æ ¹æ®é…ç½®è¿›è¡ŒåŠ å¯†ã€‚ä¼šè¿›è¡Œæœ¬åœ°ç¼“å­˜å¯¹åº”çš„ç®—æ³•å’Œå¯¹åº”çš„ç§˜é’¥ä¿¡æ¯ã€‚
     *
     * @param value          å¾…加密的值
     * @param encryptContext åŠ å¯†ç›¸å…³çš„é…ç½®ä¿¡æ¯
     */
    public String encrypt(String value, EncryptContext encryptContext) {
        IEncryptor encryptor = this.registAndGetEncryptor(encryptContext);
        return encryptor.encrypt(value, encryptContext.getEncode());
    }
    /**
     * æ ¹æ®é…ç½®è¿›è¡Œè§£å¯†
     *
     * @param value          å¾…解密的值
     * @param encryptContext åŠ å¯†ç›¸å…³çš„é…ç½®ä¿¡æ¯
     */
    public String decrypt(String value, EncryptContext encryptContext) {
        IEncryptor encryptor = this.registAndGetEncryptor(encryptContext);
        return encryptor.decrypt(value);
    }
}
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/core/IEncryptor.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,35 @@
package com.ruoyi.common.encrypt.core;
import com.ruoyi.common.encrypt.enumd.AlgorithmType;
import com.ruoyi.common.encrypt.enumd.EncodeType;
/**
 * åŠ è§£è€…
 *
 * @author è€é©¬
 * @version 4.6.0
 */
public interface IEncryptor {
    /**
     * èŽ·å¾—å½“å‰ç®—æ³•
     */
    AlgorithmType algorithm();
    /**
     * åР坆
     *
     * @param value      å¾…加密字符串
     * @param encodeType åŠ å¯†åŽçš„ç¼–ç æ ¼å¼
     * @return åŠ å¯†åŽçš„å­—ç¬¦ä¸²
     */
    String encrypt(String value, EncodeType encodeType);
    /**
     * è§£å¯†
     *
     * @param value      å¾…加密字符串
     * @return è§£å¯†åŽçš„字符串
     */
    String decrypt(String value);
}
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/core/encryptor/AbstractEncryptor.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,18 @@
package com.ruoyi.common.encrypt.core.encryptor;
import com.ruoyi.common.encrypt.core.EncryptContext;
import com.ruoyi.common.encrypt.core.IEncryptor;
/**
 * æ‰€æœ‰åŠ å¯†æ‰§è¡Œè€…çš„åŸºç±»
 *
 * @author è€é©¬
 * @version 4.6.0
 */
public abstract class AbstractEncryptor implements IEncryptor {
    public AbstractEncryptor(EncryptContext context) {
        // ç”¨æˆ·é…ç½®æ ¡éªŒä¸Žé…ç½®æ³¨å…¥
    }
}
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/core/encryptor/AesEncryptor.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,69 @@
package com.ruoyi.common.encrypt.core.encryptor;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.symmetric.AES;
import com.ruoyi.common.encrypt.core.EncryptContext;
import com.ruoyi.common.encrypt.enumd.AlgorithmType;
import com.ruoyi.common.encrypt.enumd.EncodeType;
import java.nio.charset.StandardCharsets;
/**
 * AES算法实现
 *
 * @author è€é©¬
 * @version 4.6.0
 */
public class AesEncryptor extends AbstractEncryptor {
    private final AES aes;
    public AesEncryptor(EncryptContext context) {
        super(context);
        String password = context.getPassword();
        if (StrUtil.isBlank(password)) {
            throw new IllegalArgumentException("AES没有获得秘钥信息");
        }
        // aes算法的秘钥要求是16位、24位、32位
        int[] array = {16, 24, 32};
        if (!ArrayUtil.contains(array, password.length())) {
            throw new IllegalArgumentException("AES秘钥长度应该为16位、24位、32位,实际为" + password.length() + "位");
        }
        aes = SecureUtil.aes(context.getPassword().getBytes(StandardCharsets.UTF_8));
    }
    /**
     * èŽ·å¾—å½“å‰ç®—æ³•
     */
    @Override
    public AlgorithmType algorithm() {
        return AlgorithmType.AES;
    }
    /**
     * åР坆
     *
     * @param value      å¾…加密字符串
     * @param encodeType åŠ å¯†åŽçš„ç¼–ç æ ¼å¼
     */
    @Override
    public String encrypt(String value, EncodeType encodeType) {
        if (encodeType == EncodeType.HEX) {
            return aes.encryptHex(value);
        } else {
            return aes.encryptBase64(value);
        }
    }
    /**
     * è§£å¯†
     *
     * @param value      å¾…加密字符串
     */
    @Override
    public String decrypt(String value) {
        return this.aes.decryptStr(value);
    }
}
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/core/encryptor/Base64Encryptor.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,48 @@
package com.ruoyi.common.encrypt.core.encryptor;
import cn.hutool.core.codec.Base64;
import com.ruoyi.common.encrypt.core.EncryptContext;
import com.ruoyi.common.encrypt.enumd.AlgorithmType;
import com.ruoyi.common.encrypt.enumd.EncodeType;
/**
 * Base64算法实现
 *
 * @author è€é©¬
 * @version 4.6.0
 */
public class Base64Encryptor extends AbstractEncryptor {
    public Base64Encryptor(EncryptContext context) {
        super(context);
    }
    /**
     * èŽ·å¾—å½“å‰ç®—æ³•
     */
    @Override
    public AlgorithmType algorithm() {
        return AlgorithmType.BASE64;
    }
    /**
     * åР坆
     *
     * @param value      å¾…加密字符串
     * @param encodeType åŠ å¯†åŽçš„ç¼–ç æ ¼å¼
     */
    @Override
    public String encrypt(String value, EncodeType encodeType) {
        return Base64.encode(value);
    }
    /**
     * è§£å¯†
     *
     * @param value      å¾…加密字符串
     */
    @Override
    public String decrypt(String value) {
        return Base64.decodeStr(value);
    }
}
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/core/encryptor/RsaEncryptor.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,65 @@
package com.ruoyi.common.encrypt.core.encryptor;
import cn.hutool.core.codec.Base64;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.encrypt.core.EncryptContext;
import com.ruoyi.common.encrypt.enumd.AlgorithmType;
import com.ruoyi.common.encrypt.enumd.EncodeType;
/**
 * RSA算法实现
 *
 * @author è€é©¬
 * @version 4.6.0
 */
public class RsaEncryptor extends AbstractEncryptor {
    private final RSA rsa;
    public RsaEncryptor(EncryptContext context) {
        super(context);
        String privateKey = context.getPrivateKey();
        String publicKey = context.getPublicKey();
        if (StringUtils.isAnyEmpty(privateKey, publicKey)) {
            throw new IllegalArgumentException("RSA公私钥均需要提供,公钥加密,私钥解密。");
        }
        this.rsa = SecureUtil.rsa(Base64.decode(privateKey), Base64.decode(publicKey));
    }
    /**
     * èŽ·å¾—å½“å‰ç®—æ³•
     */
    @Override
    public AlgorithmType algorithm() {
        return AlgorithmType.RSA;
    }
    /**
     * åР坆
     *
     * @param value      å¾…加密字符串
     * @param encodeType åŠ å¯†åŽçš„ç¼–ç æ ¼å¼
     */
    @Override
    public String encrypt(String value, EncodeType encodeType) {
        if (encodeType == EncodeType.HEX) {
            return rsa.encryptHex(value, KeyType.PublicKey);
        } else {
            return rsa.encryptBase64(value, KeyType.PublicKey);
        }
    }
    /**
     * è§£å¯†
     *
     * @param value      å¾…加密字符串
     */
    @Override
    public String decrypt(String value) {
        return this.rsa.decryptStr(value, KeyType.PrivateKey);
    }
}
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/core/encryptor/Sm2Encryptor.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,64 @@
package com.ruoyi.common.encrypt.core.encryptor;
import cn.hutool.core.codec.Base64;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.SM2;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.encrypt.core.EncryptContext;
import com.ruoyi.common.encrypt.enumd.AlgorithmType;
import com.ruoyi.common.encrypt.enumd.EncodeType;
/**
 * sm2算法实现
 *
 * @author è€é©¬
 * @version 4.6.0
 */
public class Sm2Encryptor extends AbstractEncryptor {
    private final SM2 sm2;
    public Sm2Encryptor(EncryptContext context) {
        super(context);
        String privateKey = context.getPrivateKey();
        String publicKey = context.getPublicKey();
        if (StringUtils.isAnyEmpty(privateKey, publicKey)) {
            throw new IllegalArgumentException("SM2公私钥均需要提供,公钥加密,私钥解密。");
        }
        this.sm2 = SmUtil.sm2(Base64.decode(privateKey), Base64.decode(publicKey));
    }
    /**
     * èŽ·å¾—å½“å‰ç®—æ³•
     */
    @Override
    public AlgorithmType algorithm() {
        return AlgorithmType.SM2;
    }
    /**
     * åР坆
     *
     * @param value      å¾…加密字符串
     * @param encodeType åŠ å¯†åŽçš„ç¼–ç æ ¼å¼
     */
    @Override
    public String encrypt(String value, EncodeType encodeType) {
        if (encodeType == EncodeType.HEX) {
            return sm2.encryptHex(value, KeyType.PublicKey);
        } else {
            return sm2.encryptBase64(value, KeyType.PublicKey);
        }
    }
    /**
     * è§£å¯†
     *
     * @param value      å¾…加密字符串
     */
    @Override
    public String decrypt(String value) {
        return this.sm2.decryptStr(value, KeyType.PrivateKey);
    }
}
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/core/encryptor/Sm4Encryptor.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,67 @@
package com.ruoyi.common.encrypt.core.encryptor;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.symmetric.SM4;
import com.ruoyi.common.encrypt.core.EncryptContext;
import com.ruoyi.common.encrypt.enumd.AlgorithmType;
import com.ruoyi.common.encrypt.enumd.EncodeType;
import java.nio.charset.StandardCharsets;
/**
 * sm4算法实现
 *
 * @author è€é©¬
 * @version 4.6.0
 */
public class Sm4Encryptor extends AbstractEncryptor {
    private final SM4 sm4;
    public Sm4Encryptor(EncryptContext context) {
        super(context);
        String password = context.getPassword();
        if (StrUtil.isBlank(password)) {
            throw new IllegalArgumentException("SM4没有获得秘钥信息");
        }
        // sm4算法的秘钥要求是16位长度
        if (16 != password.length()) {
            throw new IllegalArgumentException("SM4秘钥长度应该为16位,实际为" + password.length() + "位");
        }
        this.sm4 = SmUtil.sm4(password.getBytes(StandardCharsets.UTF_8));
    }
    /**
     * èŽ·å¾—å½“å‰ç®—æ³•
     */
    @Override
    public AlgorithmType algorithm() {
        return AlgorithmType.SM4;
    }
    /**
     * åР坆
     *
     * @param value      å¾…加密字符串
     * @param encodeType åŠ å¯†åŽçš„ç¼–ç æ ¼å¼
     */
    @Override
    public String encrypt(String value, EncodeType encodeType) {
        if (encodeType == EncodeType.HEX) {
            return sm4.encryptHex(value);
        } else {
            return sm4.encryptBase64(value);
        }
    }
    /**
     * è§£å¯†
     *
     * @param value      å¾…加密字符串
     */
    @Override
    public String decrypt(String value) {
        return this.sm4.decryptStr(value);
    }
}
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/enumd/AlgorithmType.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,48 @@
package com.ruoyi.common.encrypt.enumd;
import com.ruoyi.common.encrypt.core.encryptor.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
 * ç®—法名称
 *
 * @author è€é©¬
 * @version 4.6.0
 */
@Getter
@AllArgsConstructor
public enum AlgorithmType {
    /**
     * é»˜è®¤èµ°yml配置
     */
    DEFAULT(null),
    /**
     * base64
     */
    BASE64(Base64Encryptor.class),
    /**
     * aes
     */
    AES(AesEncryptor.class),
    /**
     * rsa
     */
    RSA(RsaEncryptor.class),
    /**
     * sm2
     */
    SM2(Sm2Encryptor.class),
    /**
     * sm4
     */
    SM4(Sm4Encryptor.class);
    private final Class<? extends AbstractEncryptor> clazz;
}
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/enumd/EncodeType.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,26 @@
package com.ruoyi.common.encrypt.enumd;
/**
 * ç¼–码类型
 *
 * @author è€é©¬
 * @version 4.6.0
 */
public enum EncodeType {
    /**
     * é»˜è®¤ä½¿ç”¨yml配置
     */
    DEFAULT,
    /**
     * base64编码
     */
    BASE64,
    /**
     * 16进制编码
     */
    HEX;
}
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/interceptor/MybatisDecryptInterceptor.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,108 @@
package com.ruoyi.common.encrypt.interceptor;
import cn.hutool.core.collection.CollectionUtil;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.encrypt.annotation.EncryptField;
import com.ruoyi.common.encrypt.core.EncryptContext;
import com.ruoyi.common.encrypt.core.EncryptorManager;
import com.ruoyi.common.encrypt.enumd.AlgorithmType;
import com.ruoyi.common.encrypt.enumd.EncodeType;
import com.ruoyi.common.encrypt.properties.EncryptorProperties;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.plugin.*;
import java.lang.reflect.Field;
import java.sql.Statement;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
/**
 * å‡ºå‚解密拦截器
 *
 * @author è€é©¬
 * @version 4.6.0
 */
@Slf4j
@Intercepts({@Signature(
    type = ResultSetHandler.class,
    method = "handleResultSets",
    args = {Statement.class})
})
@AllArgsConstructor
public class MybatisDecryptInterceptor implements Interceptor {
    private final EncryptorManager encryptorManager;
    private final EncryptorProperties defaultProperties;
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // èŽ·å–æ‰§è¡Œmysql执行结果
        Object result = invocation.proceed();
        if (result == null) {
            return null;
        }
        decryptHandler(result);
        return result;
    }
    /**
     * è§£å¯†å¯¹è±¡
     *
     * @param sourceObject å¾…加密对象
     */
    private void decryptHandler(Object sourceObject) {
        if (sourceObject instanceof Map) {
            ((Map<?, Object>) sourceObject).values().forEach(this::decryptHandler);
            return;
        }
        if (sourceObject instanceof List) {
            // åˆ¤æ–­ç¬¬ä¸€ä¸ªå…ƒç´ æ˜¯å¦å«æœ‰æ³¨è§£ã€‚如果没有直接返回,提高效率
            Object firstItem = ((List<?>) sourceObject).get(0);
            if (CollectionUtil.isEmpty(encryptorManager.getFieldCache(firstItem.getClass()))) {
                return;
            }
            ((List<?>) sourceObject).forEach(this::decryptHandler);
            return;
        }
        Set<Field> fields = encryptorManager.getFieldCache(sourceObject.getClass());
        try {
            for (Field field : fields) {
                field.set(sourceObject, this.decryptField(String.valueOf(field.get(sourceObject)), field));
            }
        } catch (Exception e) {
            log.error("处理解密字段时出错", e);
        }
    }
    /**
     * å­—段值进行加密。通过字段的批注注册新的加密算法
     *
     * @param value å¾…加密的值
     * @param field å¾…加密字段
     * @return åŠ å¯†åŽç»“æžœ
     */
    private String decryptField(String value, Field field) {
        EncryptField encryptField = field.getAnnotation(EncryptField.class);
        EncryptContext encryptContext = new EncryptContext();
        encryptContext.setAlgorithm(encryptField.algorithm() == AlgorithmType.DEFAULT ? defaultProperties.getAlgorithm() : encryptField.algorithm());
        encryptContext.setEncode(encryptField.encode() == EncodeType.DEFAULT ? defaultProperties.getEncode() : encryptField.encode());
        encryptContext.setPassword(StringUtils.isBlank(encryptField.password()) ? defaultProperties.getPassword() : encryptField.password());
        encryptContext.setPrivateKey(StringUtils.isBlank(encryptField.privateKey()) ? defaultProperties.getPrivateKey() : encryptField.privateKey());
        encryptContext.setPublicKey(StringUtils.isBlank(encryptField.publicKey()) ? defaultProperties.getPublicKey() : encryptField.publicKey());
        return this.encryptorManager.decrypt(value, encryptContext);
    }
    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }
    @Override
    public void setProperties(Properties properties) {
    }
}
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/interceptor/MybatisEncryptInterceptor.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,115 @@
package com.ruoyi.common.encrypt.interceptor;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.encrypt.annotation.EncryptField;
import com.ruoyi.common.encrypt.core.EncryptContext;
import com.ruoyi.common.encrypt.core.EncryptorManager;
import com.ruoyi.common.encrypt.enumd.AlgorithmType;
import com.ruoyi.common.encrypt.enumd.EncodeType;
import com.ruoyi.common.encrypt.properties.EncryptorProperties;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import java.lang.reflect.Field;
import java.sql.PreparedStatement;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
/**
 * å…¥å‚加密拦截器
 *
 * @author è€é©¬
 * @version 4.6.0
 */
@Slf4j
@Intercepts({@Signature(
    type = ParameterHandler.class,
    method = "setParameters",
    args = {PreparedStatement.class})
})
@AllArgsConstructor
public class MybatisEncryptInterceptor implements Interceptor {
    private final EncryptorManager encryptorManager;
    private final EncryptorProperties defaultProperties;
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        return invocation;
    }
    @Override
    public Object plugin(Object target) {
        if (target instanceof ParameterHandler) {
            // è¿›è¡ŒåŠ å¯†æ“ä½œ
            ParameterHandler parameterHandler = (ParameterHandler) target;
            Object parameterObject = parameterHandler.getParameterObject();
            if (ObjectUtil.isNotNull(parameterObject) && !(parameterObject instanceof String)) {
                this.encryptHandler(parameterObject);
            }
        }
        return target;
    }
    /**
     * åŠ å¯†å¯¹è±¡
     *
     * @param sourceObject å¾…加密对象
     */
    @SuppressWarnings("unchecked cast")
    private void encryptHandler(Object sourceObject) {
        if (sourceObject instanceof Map) {
            ((Map<?, Object>) sourceObject).values().forEach(this::encryptHandler);
            return;
        }
        if (sourceObject instanceof List) {
            // åˆ¤æ–­ç¬¬ä¸€ä¸ªå…ƒç´ æ˜¯å¦å«æœ‰æ³¨è§£ã€‚如果没有直接返回,提高效率
            Object firstItem = ((List<?>) sourceObject).get(0);
            if (CollectionUtil.isEmpty(encryptorManager.getFieldCache(firstItem.getClass()))) {
                return;
            }
            ((List<?>) sourceObject).forEach(this::encryptHandler);
            return;
        }
        Set<Field> fields = encryptorManager.getFieldCache(sourceObject.getClass());
        try {
            for (Field field : fields) {
                field.set(sourceObject, this.encryptField(String.valueOf(field.get(sourceObject)), field));
            }
        } catch (Exception e) {
            log.error("处理加密字段时出错", e);
        }
    }
    /**
     * å­—段值进行加密。通过字段的批注注册新的加密算法
     *
     * @param value å¾…加密的值
     * @param field å¾…加密字段
     * @return åŠ å¯†åŽç»“æžœ
     */
    private String encryptField(String value, Field field) {
        EncryptField encryptField = field.getAnnotation(EncryptField.class);
        EncryptContext encryptContext = new EncryptContext();
        encryptContext.setAlgorithm(encryptField.algorithm() == AlgorithmType.DEFAULT ? defaultProperties.getAlgorithm() : encryptField.algorithm());
        encryptContext.setEncode(encryptField.encode() == EncodeType.DEFAULT ? defaultProperties.getEncode() : encryptField.encode());
        encryptContext.setPassword(StringUtils.isBlank(encryptField.password()) ? defaultProperties.getPassword() : encryptField.password());
        encryptContext.setPrivateKey(StringUtils.isBlank(encryptField.privateKey()) ? defaultProperties.getPrivateKey() : encryptField.privateKey());
        encryptContext.setPublicKey(StringUtils.isBlank(encryptField.publicKey()) ? defaultProperties.getPublicKey() : encryptField.publicKey());
        return this.encryptorManager.encrypt(value, encryptContext);
    }
    @Override
    public void setProperties(Properties properties) {
    }
}
ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/properties/EncryptorProperties.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,50 @@
package com.ruoyi.common.encrypt.properties;
import com.ruoyi.common.encrypt.enumd.AlgorithmType;
import com.ruoyi.common.encrypt.enumd.EncodeType;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
 * åŠ è§£å¯†å±žæ€§é…ç½®ç±»
 *
 * @author è€é©¬
 * @version 4.6.0
 */
@Data
@Component
@ConfigurationProperties(prefix = "mybatis-encryptor")
public class EncryptorProperties {
    /**
     * è¿‡æ»¤å¼€å…³
     */
    private Boolean enable;
    /**
     * é»˜è®¤ç®—法
     */
    private AlgorithmType algorithm;
    /**
     * å®‰å…¨ç§˜é’¥
     */
    private String password;
    /**
     * å…¬é’¥
     */
    private String publicKey;
    /**
     * ç§é’¥
     */
    private String privateKey;
    /**
     * ç¼–码方式,base64/hex
     */
    private EncodeType encode;
}
ruoyi-common/ruoyi-common-encrypt/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1 @@
com.ruoyi.common.encrypt.config.EncryptorAutoConfiguration
ruoyi-modules/ruoyi-demo/pom.xml
@@ -83,6 +83,11 @@
            <artifactId>ruoyi-common-sensitive</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common-encrypt</artifactId>
        </dependency>
        <!-- çŸ­ä¿¡ ç”¨å“ªä¸ªå¯¼å…¥å“ªä¸ªä¾èµ– -->
<!--        <dependency>-->
<!--            <groupId>com.aliyun</groupId>-->
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestEncryptController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,55 @@
package com.ruoyi.demo.controller;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.demo.domain.TestDemoEncrypt;
import com.ruoyi.demo.mapper.TestDemoEncryptMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
/**
 * æµ‹è¯•数据库加解密功能
 *
 * @author Lion Li
 */
@Validated
@RestController
@RequestMapping("/demo/encrypt")
public class TestEncryptController {
    @Autowired
    private TestDemoEncryptMapper mapper;
    @Value("${mybatis-encryptor.enable}")
    private Boolean encryptEnable;
    /**
     * æµ‹è¯•数据库加解密
     *
     * @param key   æµ‹è¯•key
     * @param value æµ‹è¯•value
     */
    @GetMapping()
    public R<Map<String, TestDemoEncrypt>> test(String key, String value) {
        if (!encryptEnable) {
            throw new RuntimeException("加密功能未开启!");
        }
        Map<String, TestDemoEncrypt> map = new HashMap<>(2);
        TestDemoEncrypt demo = new TestDemoEncrypt();
        demo.setTestKey(key);
        demo.setValue(value);
        mapper.insert(demo);
        map.put("加密", demo);
        TestDemoEncrypt testDemo = mapper.selectById(demo.getId());
        map.put("解密", testDemo);
        return R.ok(map);
    }
}
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/TestDemoEncrypt.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,29 @@
package com.ruoyi.demo.domain;
import com.baomidou.mybatisplus.annotation.TableName;
import com.ruoyi.common.encrypt.annotation.EncryptField;
import com.ruoyi.common.encrypt.enumd.AlgorithmType;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("test_demo")
public class TestDemoEncrypt extends TestDemo {
    /**
     * key键
     */
    // @EncryptField(algorithm=AlgorithmType.SM2, privateKey = "MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgZSlOvw8FBiH+aFJWLYZP/VRjg9wjfRarTkGBZd/T3N+gCgYIKoEcz1UBgi2hRANCAAR5DGuQwJqkxnbCsP+iPSDoHWIF4RwcR5EsSvT8QPxO1wRkR2IhCkzvRb32x2CUgJFdvoqVqfApFDPZzShqzBwX", publicKey = "MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEeQxrkMCapMZ2wrD/oj0g6B1iBeEcHEeRLEr0/ED8TtcEZEdiIQpM70W99sdglICRXb6KlanwKRQz2c0oaswcFw==")
    @EncryptField(algorithm = AlgorithmType.RSA, privateKey = "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBANBBEeueWlXlkkj2+WY5l+IWe42d8b5K28g+G/CFKC/yYAEHtqGlCsBOrb+YBkG9mPzmuYA/n9k0NFIc8E8yY5vZQaroyFBrTTWEzG9RY2f7Y3svVyybs6jpXSUs4xff8abo7wL1Y/wUaeatTViamxYnyTvdTmLm3d+JjRij68rxAgMBAAECgYAB0TnhXraSopwIVRfmboea1b0upl+BUdTJcmci412UjrKr5aE695ZLPkXbFXijVu7HJlyyv94NVUdaMACV7Ku/S2RuNB70M7YJm8rAjHFC3/i2ZeIM60h1Ziy4QKv0XM3pRATlDCDNhC1WUrtQCQSgU8kcp6eUUppruOqDzcY04QJBAPm9+sBP9CwDRgy3e5+V8aZtJkwDstb0lVVV/KY890cydVxiCwvX3fqVnxKMlb+x0YtH0sb9v+71xvK2lGobaRECQQDVePU6r/cCEfpc+nkWF6osAH1f8Mux3rYv2DoBGvaPzV2BGfsLed4neRfCwWNCKvGPCdW+L0xMJg8+RwaoBUPhAkAT5kViqXxFPYWJYd1h2+rDXhMdH3ZSlm6HvDBDdrwlWinr0Iwcx3iSjPV93uHXwm118aUj4fg3LDJMCKxOwBxhAkByrQXfvwOMYygBprRBf/j0plazoWFrbd6lGR0f1uI5IfNnFRPdeFw1DEINZ2Hw+6zEUF44SqRMC+4IYJNc02dBAkBCgy7RvfyV/A7N6kKXxTHauY0v6XwSSvpeKtRJkbIcRWOdIYvaHO9L7cklj3vIEdwjSUp9K4VTBYYlmAz1xh03", publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDQQRHrnlpV5ZJI9vlmOZfiFnuNnfG+StvIPhvwhSgv8mABB7ahpQrATq2/mAZBvZj85rmAP5/ZNDRSHPBPMmOb2UGq6MhQa001hMxvUWNn+2N7L1csm7Oo6V0lLOMX3/Gm6O8C9WP8FGnmrU1YmpsWJ8k73U5i5t3fiY0Yo+vK8QIDAQAB")
    private String testKey;
    /**
     * å€¼
     */
    // @EncryptField // ä»€ä¹ˆä¹Ÿä¸å†™èµ°é»˜è®¤yml配置
    // @EncryptField(algorithm = AlgorithmType.SM4, password = "10rfylhtccpuyke5")
    @EncryptField(algorithm = AlgorithmType.AES, password = "10rfylhtccpuyke5")
    private String value;
}
ruoyi-modules/ruoyi-demo/src/main/java/com/ruoyi/demo/mapper/TestDemoEncryptMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,13 @@
package com.ruoyi.demo.mapper;
import com.ruoyi.common.mybatis.core.mapper.BaseMapperPlus;
import com.ruoyi.demo.domain.TestDemoEncrypt;
/**
 * æµ‹è¯•加密功能
 *
 * @author Lion Li
 */
public interface TestDemoEncryptMapper extends BaseMapperPlus<TestDemoEncryptMapper, TestDemoEncrypt, TestDemoEncrypt> {
}