Browse Source

add 新增 数据加解密模块 ruoyi-common-encrypt

疯狂的狮子li 3 years ago
parent
commit
0711d75c66
22 changed files with 998 additions and 0 deletions
  1. 14 0
      config/nacos/application-common.yml
  2. 8 0
      pom.xml
  3. 1 0
      ruoyi-common/pom.xml
  4. 6 0
      ruoyi-common/ruoyi-common-bom/pom.xml
  5. 37 0
      ruoyi-common/ruoyi-common-encrypt/pom.xml
  6. 44 0
      ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/annotation/EncryptField.java
  7. 39 0
      ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/config/EncryptorAutoConfiguration.java
  8. 41 0
      ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/core/EncryptContext.java
  9. 94 0
      ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/core/EncryptorManager.java
  10. 35 0
      ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/core/IEncryptor.java
  11. 18 0
      ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/core/encryptor/AbstractEncryptor.java
  12. 69 0
      ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/core/encryptor/AesEncryptor.java
  13. 48 0
      ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/core/encryptor/Base64Encryptor.java
  14. 65 0
      ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/core/encryptor/RsaEncryptor.java
  15. 64 0
      ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/core/encryptor/Sm2Encryptor.java
  16. 67 0
      ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/core/encryptor/Sm4Encryptor.java
  17. 48 0
      ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/enumd/AlgorithmType.java
  18. 26 0
      ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/enumd/EncodeType.java
  19. 108 0
      ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/interceptor/MybatisDecryptInterceptor.java
  20. 115 0
      ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/interceptor/MybatisEncryptInterceptor.java
  21. 50 0
      ruoyi-common/ruoyi-common-encrypt/src/main/java/com/ruoyi/common/encrypt/properties/EncryptorProperties.java
  22. 1 0
      ruoyi-common/ruoyi-common-encrypt/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

+ 14 - 0
config/nacos/application-common.yml

@@ -230,6 +230,20 @@ mybatis-plus:
       updateStrategy: NOT_NULL
       where-strategy: NOT_NULL
 
+# 数据加密
+mybatis-encryptor:
+  # 是否开启加密
+  enable: false
+  # 默认加密算法
+  algorithm: BASE64
+  # 编码方式 BASE64/HEX。默认BASE64
+  encode: BASE64
+  # 安全秘钥 对称算法的秘钥 如:AES,SM4
+  password:
+  # 公私钥 非对称算法的公私钥 如:SM2,RSA
+  publicKey:
+  privateKey:
+
 # swagger配置
 swagger:
   info:

+ 8 - 0
pom.xml

@@ -40,6 +40,7 @@
         <easy-es.version>1.1.1</easy-es.version>
         <elasticsearch.version>7.14.0</elasticsearch.version>
         <skywalking-toolkit.version>8.13.0</skywalking-toolkit.version>
+        <bouncycastle.version>1.72</bouncycastle.version>
 
         <!-- 临时修复 snakeyaml 漏洞 -->
         <snakeyaml.version>1.33</snakeyaml.version>
@@ -268,6 +269,13 @@
                 <version>${xxl-job.version}</version>
             </dependency>
 
+            <!-- 加密包引入 -->
+            <dependency>
+                <groupId>org.bouncycastle</groupId>
+                <artifactId>bcprov-jdk15to18</artifactId>
+                <version>${bouncycastle.version}</version>
+            </dependency>
+
             <!-- logstash -->
             <dependency>
                 <groupId>net.logstash.logback</groupId>

+ 1 - 0
ruoyi-common/pom.xml

@@ -35,6 +35,7 @@
         <module>ruoyi-common-skylog</module>
         <module>ruoyi-common-prometheus</module>
         <module>ruoyi-common-translation</module>
+        <module>ruoyi-common-encrypt</module>
     </modules>
 
     <artifactId>ruoyi-common</artifactId>

+ 6 - 0
ruoyi-common/ruoyi-common-bom/pom.xml

@@ -168,6 +168,12 @@
                 <version>${project.version}</version>
             </dependency>
 
+            <dependency>
+                <groupId>com.ruoyi</groupId>
+                <artifactId>ruoyi-common-encrypt</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+
         </dependencies>
     </dependencyManagement>
 </project>

+ 37 - 0
ruoyi-common/ruoyi-common-encrypt/pom.xml

@@ -0,0 +1,37 @@
+<?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>1.5.0</version>
+    </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.bouncycastle</groupId>
+            <artifactId>bcprov-jdk15to18</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-crypto</artifactId>
+        </dependency>
+
+    </dependencies>
+
+</project>

+ 44 - 0
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;
+
+}

+ 39 - 0
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);
+    }
+}

+ 41 - 0
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;
+
+}

+ 94 - 0
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);
+    }
+
+}

+ 35 - 0
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);
+}

+ 18 - 0
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) {
+        // 用户配置校验与配置注入
+    }
+
+}

+ 69 - 0
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);
+    }
+}

+ 48 - 0
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);
+    }
+}

+ 65 - 0
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);
+    }
+}

+ 64 - 0
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);
+    }
+}

+ 67 - 0
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);
+    }
+}

+ 48 - 0
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;
+}

+ 26 - 0
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;
+
+}

+ 108 - 0
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) {
+
+    }
+}

+ 115 - 0
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) {
+    }
+}

+ 50 - 0
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;
+
+}

+ 1 - 0
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