diff --git a/pom.xml b/pom.xml
index 30d5132c8..49f68734a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -36,6 +36,7 @@
2.14.2
2.3.1
1.18.24
+ 1.72
1.33
@@ -252,6 +253,13 @@
${snakeyaml.version}
+
+
+ org.bouncycastle
+ bcprov-jdk15to18
+ ${bouncycastle.version}
+
+
com.ruoyi
diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml
index 77c68e496..5b5a31423 100644
--- a/ruoyi-admin/src/main/resources/application.yml
+++ b/ruoyi-admin/src/main/resources/application.yml
@@ -183,6 +183,19 @@ mybatis-plus:
updateStrategy: NOT_NULL
# 字段验证策略之 select,在 select 的时候的字段验证策略既 wrapper 根据内部 entity 生成的 where 条件
where-strategy: NOT_NULL
+# 数据加密
+mybatis-encryptor:
+ # 是否开启加密
+ enabled: true
+ # 默认加密算法
+ algorithm: aes
+ # 安全秘钥。对称算法的秘钥。如:AES,SM4
+ password: QBcc3SHK2G4ijyfl5XQ7@2g!N2j2jRDR
+# # 公私钥。非对称算法的公私钥,如:SM2,RSA
+# publicKey: MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCDt41GIcWvY6/tpjOLoWY78Oy00uWcNbMRG/8DRoS79/h2D+pV8uxV+0ezaN+fBFCZnK8TdJcPeU4EnNRUh/8HEY33KFvZ700n+Gj5BHUUDKzx3UVFNuF49UI/yoJ8rz6VQQHO79KK89VwmSMO77Tfee1ofe0STY6IwMt/MwaoKwIDAQAB
+# privateKey: MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAIO3jUYhxa9jr+2mM4uhZjvw7LTS5Zw1sxEb/wNGhLv3+HYP6lXy7FX7R7No358EUJmcrxN0lw95TgSc1FSH/wcRjfcoW9nvTSf4aPkEdRQMrPHdRUU24Xj1Qj/KgnyvPpVBAc7v0orz1XCZIw7vtN957Wh97RJNjojAy38zBqgrAgMBAAECgYAkXx4qvI6rFNryw88+Am6JpMioUghHb2ioE9QCYomqohnA+DocS71JLN8qwo3lijp7gJGzzKEeC8Aoc+oKAZfBQQwPP0K/ql+NcLdlqNAr+XyzkZrLJD/BfluQ6mXI23tjonWnPPZ1Et9HC3zXRQPiuh7Ff6UoqatTMI3OIbSyuQJBAPbaJpVYmIsWJdpvaAC6hcGgR/vbG9yX8VphPn8/2wsm2MmQrRkUoKkudE/veX3KQjGikDwo9+bT6aBl+nn2DUUCQQCImSGZFNcGqhMLyRGVogP6fh0YZSEQAhFbZ69nQmplqbie+dREhODH5Vs5F0C2aL5iSR/UaIDkNEA1auX7x56vAkAFWjON927PTTqi4tmBconl6eDFsDmJbe34xLUDM1I/iqcWr8FhEtZs9Knm9c1PkewfgWPZOhYt9hhRtwRYUqJ1AkAPvQ0I9US9KNVe80DKa8tnjiZODEDd9k8HqA+mpxlZM0/pSUGyz1iSz5NOJaa4HaNp8aDwOUY4hOis/u8Wrm5TAkEA00YeUsaXlMyMF/5pjols44tXb54AjAC2mH66pz9JsKg7pKpWVOpEV5rMY58CGZHWau69vGLZnCd1coeMw77YAQ==
+# # 编码方式,base64/hex。默认base64
+ encode: base64
# Swagger配置
swagger:
diff --git a/ruoyi-admin/src/test/java/com/ruoyi/test/EncryptUnitTest.java b/ruoyi-admin/src/test/java/com/ruoyi/test/EncryptUnitTest.java
new file mode 100644
index 000000000..ad69cfbf7
--- /dev/null
+++ b/ruoyi-admin/src/test/java/com/ruoyi/test/EncryptUnitTest.java
@@ -0,0 +1,35 @@
+package com.ruoyi.test;
+
+import com.ruoyi.demo.domain.TestDemo;
+import com.ruoyi.demo.mapper.TestDemoMapper;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import javax.annotation.Resource;
+
+/**
+ * 加密单元测试案例
+ *
+ * @author 老马
+ * @date 2023-01-12 08:53
+ */
+@SpringBootTest
+@DisplayName("加密测试")
+public class EncryptUnitTest {
+
+ @Resource
+ private TestDemoMapper demoMapper;
+
+ @Test
+ public void testCrypt() {
+ TestDemo demo = new TestDemo();
+ demo.setTestKey("测试的key");
+ demo.setValue("测试的value");
+ this.demoMapper.insert(demo);
+ System.out.println(demo);
+ TestDemo testDemo = this.demoMapper.selectById(demo.getId());
+ System.out.println(testDemo);
+ }
+
+}
diff --git a/ruoyi-common/pom.xml b/ruoyi-common/pom.xml
index f88534367..5195cd05a 100644
--- a/ruoyi-common/pom.xml
+++ b/ruoyi-common/pom.xml
@@ -148,6 +148,12 @@
lock4j-redisson-spring-boot-starter
+
+
+ org.bouncycastle
+ bcprov-jdk15to18
+
+
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/EncryptField.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/EncryptField.java
new file mode 100644
index 000000000..492332bfc
--- /dev/null
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/EncryptField.java
@@ -0,0 +1,43 @@
+package com.ruoyi.common.annotation;
+
+import com.ruoyi.common.enums.AlgorithmType;
+import com.ruoyi.common.enums.EncodeType;
+import java.lang.annotation.*;
+
+/**
+ * 字段加密注解
+ *
+ * @author 老马
+ */
+@Documented
+@Inherited
+@Target({ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface EncryptField {
+
+ /**
+ * 加密算法
+ */
+ AlgorithmType algorithm() default AlgorithmType.AES;
+
+ /**
+ * 秘钥。AES、SM4需要
+ */
+ String password() default "";
+
+ /**
+ * 公钥。RSA、SM2需要
+ */
+ String publicKey() default "";
+
+ /**
+ * 公钥。RSA、SM2需要
+ */
+ String privateKey() default "";
+
+ /**
+ * 编码方式。对加密算法为BASE64的不起作用
+ */
+ EncodeType encode() default EncodeType.BASE64;
+
+}
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/encrypt/IEncryptor.java b/ruoyi-common/src/main/java/com/ruoyi/common/encrypt/IEncryptor.java
new file mode 100644
index 000000000..50204cfbd
--- /dev/null
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/encrypt/IEncryptor.java
@@ -0,0 +1,46 @@
+package com.ruoyi.common.encrypt;
+
+import com.ruoyi.common.enums.AlgorithmType;
+import com.ruoyi.common.enums.EncodeType;
+
+/**
+ * 加解者
+ *
+ * @author 老马
+ * @date 2023-01-10 16:08
+ */
+public interface IEncryptor {
+
+ /**
+ * 获得当前算法
+ *
+ * @return com.ruoyi.common.enums.AlgorithmType
+ * @author 老马
+ * @date 2023/1/11 11:18
+ */
+ AlgorithmType algorithm();
+
+ /**
+ * 加密
+ *
+ * @param value 待加密字符串
+ * @param encodeType 加密后的编码格式
+ * @return java.lang.String 加密后的字符串
+ * @throws Exception 抛出异常
+ * @author 老马
+ * @date 2023/1/10 16:38
+ */
+ String encrypt(String value, EncodeType encodeType) throws Exception;
+
+ /**
+ * 解密
+ *
+ * @param value 待加密字符串
+ * @param encodeType 加密后的编码格式
+ * @return java.lang.String 解密后的字符串
+ * @throws Exception 抛出异常
+ * @author 老马
+ * @date 2023/1/10 16:38
+ */
+ String decrypt(String value, EncodeType encodeType) throws Exception;
+}
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/AlgorithmType.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/AlgorithmType.java
new file mode 100644
index 000000000..6b8a5a5db
--- /dev/null
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/AlgorithmType.java
@@ -0,0 +1,36 @@
+package com.ruoyi.common.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 算法名称
+ *
+ * @author 老马
+ */
+@Getter
+@AllArgsConstructor
+public enum AlgorithmType {
+ /**
+ * base64
+ */
+ BASE64("base64"),
+ /**
+ * aes
+ */
+ AES("aes"),
+ /**
+ * rsa
+ */
+ RSA("rsa"),
+ /**
+ * sm2
+ */
+ SM2("sm2"),
+ /**
+ * sm4
+ */
+ SM4("sm4");
+
+ private final String algorithm;
+}
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/EncodeType.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/EncodeType.java
new file mode 100644
index 000000000..da50773b1
--- /dev/null
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/EncodeType.java
@@ -0,0 +1,26 @@
+package com.ruoyi.common.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 编码类型
+ *
+ * @author 老马
+ * @date 2023-01-11 11:39
+ */
+@Getter
+@AllArgsConstructor
+public enum EncodeType {
+ /**
+ * base64编码
+ */
+ BASE64("base64"),
+
+ /**
+ * 16进制编码
+ */
+ HEX("hex");
+
+ private final String encode;
+}
diff --git a/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/TestDemo.java b/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/TestDemo.java
index dc19ce2c7..4a4769f71 100644
--- a/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/TestDemo.java
+++ b/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/TestDemo.java
@@ -1,7 +1,9 @@
package com.ruoyi.demo.domain;
import com.baomidou.mybatisplus.annotation.*;
+import com.ruoyi.common.annotation.EncryptField;
import com.ruoyi.common.core.domain.BaseEntity;
+import com.ruoyi.common.enums.AlgorithmType;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -44,11 +46,13 @@ public class TestDemo extends BaseEntity {
/**
* key键
*/
+ @EncryptField(algorithm=AlgorithmType.SM2, privateKey = "MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgZSlOvw8FBiH+aFJWLYZP/VRjg9wjfRarTkGBZd/T3N+gCgYIKoEcz1UBgi2hRANCAAR5DGuQwJqkxnbCsP+iPSDoHWIF4RwcR5EsSvT8QPxO1wRkR2IhCkzvRb32x2CUgJFdvoqVqfApFDPZzShqzBwX", publicKey = "MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEeQxrkMCapMZ2wrD/oj0g6B1iBeEcHEeRLEr0/ED8TtcEZEdiIQpM70W99sdglICRXb6KlanwKRQz2c0oaswcFw==")
private String testKey;
/**
* 值
*/
+ @EncryptField(algorithm=AlgorithmType.AES, password = "10rfylhtccpuyke5")
private String value;
/**
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/EncryptorConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/EncryptorConfig.java
new file mode 100644
index 000000000..135936ae8
--- /dev/null
+++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/EncryptorConfig.java
@@ -0,0 +1,39 @@
+package com.ruoyi.framework.config;
+
+import com.ruoyi.framework.config.properties.EncryptorProperties;
+import com.ruoyi.framework.encrypt.EncryptorManager;
+import com.ruoyi.framework.encrypt.MybatisDecryptInterceptor;
+import com.ruoyi.framework.encrypt.MybatisEncryptInterceptor;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * 加解密配置
+ *
+ * @author 老马
+ * @date 2023-01-11 10:03
+ */
+@Configuration
+public class EncryptorConfig {
+
+ @Bean
+ @ConditionalOnProperty(value = "mybatis-encryptor.enabled", havingValue = "true")
+ public EncryptorManager mybatisCryptHandler(EncryptorProperties properties) {
+ EncryptorManager encryptorManager = new EncryptorManager();
+ encryptorManager.registAndGetEncryptor(properties);
+ return encryptorManager;
+ }
+
+ @Bean
+ @ConditionalOnProperty(value = "mybatis-encryptor.enabled", havingValue = "true")
+ public MybatisEncryptInterceptor mybatisEncryptInterceptor(EncryptorProperties properties) {
+ return new MybatisEncryptInterceptor();
+ }
+
+ @Bean
+ @ConditionalOnProperty(value = "mybatis-encryptor.enabled", havingValue = "true")
+ public MybatisDecryptInterceptor mybatisDecryptInterceptor(EncryptorProperties properties) {
+ return new MybatisDecryptInterceptor();
+ }
+}
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/EncryptorProperties.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/EncryptorProperties.java
new file mode 100644
index 000000000..d34949d37
--- /dev/null
+++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/EncryptorProperties.java
@@ -0,0 +1,49 @@
+package com.ruoyi.framework.config.properties;
+
+import com.ruoyi.common.enums.AlgorithmType;
+import com.ruoyi.common.enums.EncodeType;
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * 加解密属性配置类
+ *
+ * @author 老马
+ * @date 2023-01-10 16:52
+ */
+@Data
+@Component
+@ConfigurationProperties(prefix = "mybatis-encryptor")
+public class EncryptorProperties {
+
+ /**
+ * 过滤开关
+ */
+ private Boolean enabled;
+
+ /**
+ * 默认算法
+ */
+ private AlgorithmType algorithm;
+
+ /**
+ * 安全秘钥
+ */
+ private String password;
+
+ /**
+ * 公钥
+ */
+ private String publicKey;
+
+ /**
+ * 私钥
+ */
+ private String privateKey;
+
+ /**
+ * 编码方式,base64/hex
+ */
+ private EncodeType encode;
+}
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/encrypt/EncryptedFieldsCacheHelper.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/encrypt/EncryptedFieldsCacheHelper.java
new file mode 100644
index 000000000..53b92496d
--- /dev/null
+++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/encrypt/EncryptedFieldsCacheHelper.java
@@ -0,0 +1,33 @@
+package com.ruoyi.framework.encrypt;
+
+import com.ruoyi.common.annotation.EncryptField;
+
+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 老马
+ * @date 2023-01-12 11:07
+ */
+public class EncryptedFieldsCacheHelper {
+ private static final Map, Set> ENCRYPTED_FIELD_CACHE = new ConcurrentHashMap<>();
+
+ public static Set get(Class> sourceClazz) {
+ return ENCRYPTED_FIELD_CACHE.computeIfAbsent(sourceClazz, clazz -> {
+ Field[] declaredFields = clazz.getDeclaredFields();
+ Set 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;
+ });
+ }
+}
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/encrypt/EncryptorManager.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/encrypt/EncryptorManager.java
new file mode 100644
index 000000000..04b8d30d3
--- /dev/null
+++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/encrypt/EncryptorManager.java
@@ -0,0 +1,125 @@
+package com.ruoyi.framework.encrypt;
+
+import com.ruoyi.common.encrypt.IEncryptor;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.framework.config.properties.EncryptorProperties;
+import com.ruoyi.framework.encrypt.encryptor.*;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * 加密管理类
+ *
+ * @author 老马
+ * @date 2023-01-11 10:07
+ */
+@Slf4j
+public class EncryptorManager {
+
+ /**
+ * 缓存加密器
+ */
+ Map encryptorMap = new ConcurrentHashMap<>();
+
+
+ /**
+ * 注册加密执行者到缓存
+ *
+ * @param properties 加密执行者需要的相关配置参数
+ * @author 老马
+ * @date 2023/1/11 15:09
+ */
+ public IEncryptor registAndGetEncryptor(EncryptorProperties properties) {
+ String encryptorKey = this.getEncryptorKeyFromProperties(properties);
+ if(encryptorMap.containsKey(encryptorKey)){
+ return encryptorMap.get(encryptorKey);
+ }
+ switch (properties.getAlgorithm()) {
+ case BASE64:
+ encryptorMap.put(encryptorKey, new Base64Encryptor());
+ break;
+ case AES:
+ encryptorMap.put(encryptorKey, new AesEncryptor(properties.getPassword()));
+ break;
+ case RSA:
+ encryptorMap.put(encryptorKey, new RsaEncryptor(properties.getPrivateKey(), properties.getPublicKey()));
+ break;
+ case SM2:
+ encryptorMap.put(encryptorKey, new Sm2Encryptor(properties.getPrivateKey(), properties.getPublicKey()));
+ break;
+ case SM4:
+ encryptorMap.put(encryptorKey, new Sm4Encryptor(properties.getPassword()));
+ break;
+ default:
+ AesEncryptor defaultEncryptor = new AesEncryptor(properties.getPassword());
+ encryptorMap.put(encryptorKey, defaultEncryptor);
+ }
+ return encryptorMap.get(encryptorKey);
+ }
+
+ /**
+ * 移除缓存中的加密执行者
+ *
+ * @param properties 加密执行者需要的相关配置参数
+ * @author 老马
+ * @date 2023/1/11 15:55
+ */
+ public void removeEncryptor(EncryptorProperties properties) {
+ this.encryptorMap.remove(getEncryptorKeyFromProperties(properties));
+ }
+
+ /**
+ * 根据配置进行加密。会进行本地缓存对应的算法和对应的秘钥信息。
+ *
+ * @param value 待加密的值
+ * @param properties 加密相关的配置信息
+ * @return java.lang.String 加密后的结果
+ * @author 老马
+ * @date 2023/1/11 16:46
+ */
+ public String encrypt(String value, EncryptorProperties properties) {
+ try {
+ IEncryptor encryptor = this.registAndGetEncryptor(properties);
+ return encryptor.encrypt(value, properties.getEncode());
+ }catch (Exception e) {
+ log.error("字段加密异常,原样返回。异常信息:【{}】", e.getMessage());
+ return value;
+ }
+ }
+
+ /**
+ * 根据配置进行解密
+ *
+ * @param value 待解密的值
+ * @param properties 加密相关的配置信息
+ * @return java.lang.String
+ * @author 老马
+ * @date 2023/1/11 17:43
+ */
+ public String decrypt(String value, EncryptorProperties properties) {
+ try{
+ IEncryptor encryptor = this.registAndGetEncryptor(properties);
+ return encryptor.decrypt(value, properties.getEncode());
+ }catch (Exception e) {
+ log.error("字段解密异常,原样返回。异常信息:【{}】", e.getMessage());
+ return value;
+ }
+ }
+
+
+ /**
+ * 从配置内容中提取缓存的KEY
+ *
+ * @param properties 加密相关的配置信息
+ * @return java.lang.String
+ * @author 老马
+ * @date 2023/1/11 17:39
+ */
+ private String getEncryptorKeyFromProperties(EncryptorProperties properties) {
+ return properties.getAlgorithm().getAlgorithm()+ StringUtils.defaultString(properties.getPassword()) +
+ StringUtils.defaultString(properties.getPublicKey()) + StringUtils.defaultString(properties.getPrivateKey());
+ }
+
+}
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/encrypt/MybatisDecryptInterceptor.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/encrypt/MybatisDecryptInterceptor.java
new file mode 100644
index 000000000..39136f256
--- /dev/null
+++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/encrypt/MybatisDecryptInterceptor.java
@@ -0,0 +1,103 @@
+package com.ruoyi.framework.encrypt;
+
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.exceptions.ExceptionUtil;
+import com.ruoyi.common.annotation.EncryptField;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.common.utils.spring.SpringUtils;
+import com.ruoyi.framework.config.properties.EncryptorProperties;
+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.*;
+
+/**
+ * 出参解密拦截器
+ *
+ * @author 老马
+ * @date 2023-01-12 13:47
+ */
+@Slf4j
+@Intercepts({@Signature(
+ type = ResultSetHandler.class,
+ method = "handleResultSets",
+ args = {Statement.class})
+})
+public class MybatisDecryptInterceptor implements Interceptor {
+
+ private final EncryptorManager encryptorManager = SpringUtils.getBean(EncryptorManager.class);
+ private final EncryptorProperties defaultProperties = SpringUtils.getBean(EncryptorProperties.class);
+
+ @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(EncryptedFieldsCacheHelper.get(firstItem.getClass()))) {
+ return;
+ }
+ ((List>) sourceObject).forEach(this::decryptHandler);
+ return;
+ }
+ Set fields = EncryptedFieldsCacheHelper.get(sourceObject.getClass());
+ try {
+ for (Field field : fields) {
+ field.set(sourceObject, this.decryptField(String.valueOf(field.get(sourceObject)), field));
+ }
+ } catch (Exception e) {
+ log.error("处理加密字段时出错:{}", ExceptionUtil.getSimpleMessage(e));
+ }
+ }
+
+ /**
+ * 字段值进行加密。通过字段的批注注册新的加密算法
+ *
+ * @param value 待加密的值
+ * @param field 待加密字段
+ * @return 加密后结果
+ */
+ private String decryptField(String value, Field field) {
+ EncryptField encryptField = field.getAnnotation(EncryptField.class);
+ EncryptorProperties properties = new EncryptorProperties();
+ properties.setEnabled(true);
+ properties.setAlgorithm(encryptField.algorithm());
+ properties.setEncode(encryptField.encode());
+ properties.setPassword(StringUtils.isEmpty(encryptField.password())?defaultProperties.getPassword():encryptField.password());
+ properties.setPrivateKey(StringUtils.isEmpty(encryptField.privateKey())?defaultProperties.getPrivateKey():encryptField.privateKey());
+ properties.setPublicKey(StringUtils.isEmpty(encryptField.publicKey())?defaultProperties.getPublicKey():encryptField.publicKey());
+ this.encryptorManager.registAndGetEncryptor(properties);
+ return this.encryptorManager.decrypt(value, properties);
+ }
+
+ @Override
+ public Object plugin(Object target) {
+ return Plugin.wrap(target, this);
+ }
+
+ @Override
+ public void setProperties(Properties properties) {
+
+ }
+}
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/encrypt/MybatisEncryptInterceptor.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/encrypt/MybatisEncryptInterceptor.java
new file mode 100644
index 000000000..f5e5f1d87
--- /dev/null
+++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/encrypt/MybatisEncryptInterceptor.java
@@ -0,0 +1,112 @@
+package com.ruoyi.framework.encrypt;
+
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.exceptions.ExceptionUtil;
+import cn.hutool.core.util.ObjectUtil;
+import com.ruoyi.common.annotation.EncryptField;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.common.utils.spring.SpringUtils;
+import com.ruoyi.framework.config.properties.EncryptorProperties;
+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 老马
+ * @date 2023-01-10 16:42
+ */
+@Slf4j
+@Intercepts({@Signature(
+ type = ParameterHandler.class,
+ method = "setParameters",
+ args = {PreparedStatement.class})
+})
+public class MybatisEncryptInterceptor implements Interceptor {
+
+ private final EncryptorManager encryptorManager = SpringUtils.getBean(EncryptorManager.class);
+ private final EncryptorProperties defaultProperties = SpringUtils.getBean(EncryptorProperties.class);
+
+ @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 待加密对象
+ */
+ 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(EncryptedFieldsCacheHelper.get(firstItem.getClass()))) {
+ return;
+ }
+ ((List>) sourceObject).forEach(this::encryptHandler);
+ return;
+ }
+ Set fields = EncryptedFieldsCacheHelper.get(sourceObject.getClass());
+ try {
+ for (Field field : fields) {
+ field.set(sourceObject, this.encryptField(String.valueOf(field.get(sourceObject)), field));
+ }
+ } catch (Exception e) {
+ log.error("处理加密字段时出错:{}", ExceptionUtil.getSimpleMessage(e));
+ }
+ }
+
+ /**
+ * 字段值进行加密。通过字段的批注注册新的加密算法
+ *
+ * @param value 待加密的值
+ * @param field 待加密字段
+ * @return 加密后结果
+ */
+ private String encryptField(String value, Field field) {
+ EncryptField encryptField = field.getAnnotation(EncryptField.class);
+ EncryptorProperties properties = new EncryptorProperties();
+ properties.setEnabled(true);
+ properties.setAlgorithm(encryptField.algorithm());
+ properties.setEncode(encryptField.encode());
+ properties.setPassword(StringUtils.isEmpty(encryptField.password())?defaultProperties.getPassword():encryptField.password());
+ properties.setPrivateKey(StringUtils.isEmpty(encryptField.privateKey())?defaultProperties.getPrivateKey():encryptField.privateKey());
+ properties.setPublicKey(StringUtils.isEmpty(encryptField.publicKey())?defaultProperties.getPublicKey():encryptField.publicKey());
+ this.encryptorManager.registAndGetEncryptor(properties);
+ return this.encryptorManager.encrypt(value, properties);
+ }
+
+
+ @Override
+ public void setProperties(Properties properties) {
+ }
+}
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/encrypt/encryptor/AesEncryptor.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/encrypt/encryptor/AesEncryptor.java
new file mode 100644
index 000000000..bcfcadfb9
--- /dev/null
+++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/encrypt/encryptor/AesEncryptor.java
@@ -0,0 +1,74 @@
+package com.ruoyi.framework.encrypt.encryptor;
+
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.crypto.SecureUtil;
+import cn.hutool.crypto.symmetric.AES;
+import com.ruoyi.common.encrypt.IEncryptor;
+import com.ruoyi.common.enums.AlgorithmType;
+import com.ruoyi.common.enums.EncodeType;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * AES算法实现
+ *
+ * @author 老马
+ * @date 2023-01-06 11:39
+ */
+public class AesEncryptor implements IEncryptor {
+
+ private AES aes = null;
+
+ public AesEncryptor(String password) {
+ aes = SecureUtil.aes(password.getBytes(StandardCharsets.UTF_8));
+ }
+
+ /**
+ * 获得当前算法
+ *
+ * @return com.ruoyi.common.enums.AlgorithmType
+ * @author 老马
+ * @date 2023/1/11 11:18
+ */
+ @Override
+ public AlgorithmType algorithm() {
+ return AlgorithmType.AES;
+ }
+
+ /**
+ * 加密
+ *
+ * @param value 待加密字符串
+ * @param encodeType 加密后的编码格式
+ * @return java.lang.String
+ * @author 老马
+ * @date 2023/1/10 16:38
+ */
+ @Override
+ public String encrypt(String value, EncodeType encodeType) throws Exception {
+ if(ObjectUtil.isNotNull(this.aes)) {
+ if(encodeType == EncodeType.HEX) {
+ return aes.encryptHex(value);
+ }else {
+ return aes.encryptBase64(value);
+ }
+ }
+ return value;
+ }
+
+ /**
+ * 解密
+ *
+ * @param value 待加密字符串
+ * @param encodeType 加密后的编码格式
+ * @return java.lang.String
+ * @author 老马
+ * @date 2023/1/10 16:38
+ */
+ @Override
+ public String decrypt(String value,EncodeType encodeType) throws Exception {
+ if(ObjectUtil.isNotNull(this.aes)) {
+ return this.aes.decryptStr(value);
+ }
+ return value;
+ }
+}
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/encrypt/encryptor/Base64Encryptor.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/encrypt/encryptor/Base64Encryptor.java
new file mode 100644
index 000000000..aee34efaf
--- /dev/null
+++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/encrypt/encryptor/Base64Encryptor.java
@@ -0,0 +1,54 @@
+package com.ruoyi.framework.encrypt.encryptor;
+
+import cn.hutool.core.codec.Base64;
+import com.ruoyi.common.encrypt.IEncryptor;
+import com.ruoyi.common.enums.AlgorithmType;
+import com.ruoyi.common.enums.EncodeType;
+
+/**
+ * Base64算法实现。不建议在生产环境使用
+ *
+ * @author 老马
+ * @date 2023-01-06 10:00
+ */
+public class Base64Encryptor implements IEncryptor {
+ /**
+ * 获得当前算法
+ *
+ * @return com.ruoyi.common.enums.AlgorithmType
+ * @author 老马
+ * @date 2023/1/11 11:18
+ */
+ @Override
+ public AlgorithmType algorithm() {
+ return AlgorithmType.BASE64;
+ }
+
+ /**
+ * 加密
+ *
+ * @param value 待加密字符串
+ * @param encodeType 加密后的编码格式
+ * @return java.lang.String
+ * @author 老马
+ * @date 2023/1/10 16:38
+ */
+ @Override
+ public String encrypt(String value, EncodeType encodeType) throws Exception {
+ return Base64.encode(value);
+ }
+
+ /**
+ * 解密
+ *
+ * @param value 待加密字符串
+ * @param encodeType 加密后的编码格式
+ * @return java.lang.String
+ * @author 老马
+ * @date 2023/1/10 16:38
+ */
+ @Override
+ public String decrypt(String value, EncodeType encodeType) throws Exception {
+ return Base64.decodeStr(value);
+ }
+}
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/encrypt/encryptor/RsaEncryptor.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/encrypt/encryptor/RsaEncryptor.java
new file mode 100644
index 000000000..369ad789b
--- /dev/null
+++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/encrypt/encryptor/RsaEncryptor.java
@@ -0,0 +1,76 @@
+package com.ruoyi.framework.encrypt.encryptor;
+
+import cn.hutool.core.codec.Base64;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.crypto.SecureUtil;
+import cn.hutool.crypto.asymmetric.KeyType;
+import cn.hutool.crypto.asymmetric.RSA;
+import com.ruoyi.common.encrypt.IEncryptor;
+import com.ruoyi.common.enums.AlgorithmType;
+import com.ruoyi.common.enums.EncodeType;
+
+
+/**
+ * RSA算法实现
+ *
+ * @author 老马
+ * @date 2023-01-06 09:37
+ */
+public class RsaEncryptor implements IEncryptor {
+
+ private RSA rsa = null;
+
+ public RsaEncryptor(String privateKey, String publicKey) {
+ this.rsa = SecureUtil.rsa(Base64.decode(privateKey), Base64.decode(publicKey));
+ }
+
+ /**
+ * 获得当前算法
+ *
+ * @return com.ruoyi.common.enums.AlgorithmType
+ * @author 老马
+ * @date 2023/1/11 11:18
+ */
+ @Override
+ public AlgorithmType algorithm() {
+ return AlgorithmType.RSA;
+ }
+
+ /**
+ * 加密
+ *
+ * @param value 待加密字符串
+ * @param encodeType 加密后的编码格式
+ * @return java.lang.String
+ * @author 老马
+ * @date 2023/1/10 16:38
+ */
+ @Override
+ public String encrypt(String value, EncodeType encodeType) throws Exception {
+ if(ObjectUtil.isNotNull(this.rsa)) {
+ if (encodeType == EncodeType.HEX) {
+ return rsa.encryptHex(value, KeyType.PublicKey);
+ } else {
+ return rsa.encryptBase64(value, KeyType.PublicKey);
+ }
+ }
+ return value;
+ }
+
+ /**
+ * 解密
+ *
+ * @param value 待加密字符串
+ * @param encodeType 加密后的编码格式
+ * @return java.lang.String
+ * @author 老马
+ * @date 2023/1/10 16:38
+ */
+ @Override
+ public String decrypt(String value, EncodeType encodeType) throws Exception {
+ if(ObjectUtil.isNotNull(this.rsa)) {
+ return this.rsa.decryptStr(value, KeyType.PrivateKey);
+ }
+ return value;
+ }
+}
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/encrypt/encryptor/Sm2Encryptor.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/encrypt/encryptor/Sm2Encryptor.java
new file mode 100644
index 000000000..89c30246b
--- /dev/null
+++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/encrypt/encryptor/Sm2Encryptor.java
@@ -0,0 +1,78 @@
+package com.ruoyi.framework.encrypt.encryptor;
+
+
+import cn.hutool.core.codec.Base64;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.crypto.SmUtil;
+import cn.hutool.crypto.asymmetric.KeyType;
+import cn.hutool.crypto.asymmetric.SM2;
+import com.ruoyi.common.encrypt.IEncryptor;
+import com.ruoyi.common.enums.AlgorithmType;
+import com.ruoyi.common.enums.EncodeType;
+
+/**
+ * sm2算法实现
+ *
+ * @author 老马
+ * @date 2023-01-06 17:13
+ */
+public class Sm2Encryptor implements IEncryptor {
+
+ private SM2 sm2 = null;
+
+ public Sm2Encryptor(String privateKey, String publicKey) {
+ sm2 = SmUtil.sm2(Base64.decode(privateKey), Base64.decode(publicKey));
+ }
+
+ /**
+ * 获得当前算法
+ *
+ * @return com.ruoyi.common.enums.AlgorithmType
+ * @author 老马
+ * @date 2023/1/11 11:18
+ */
+ @Override
+ public AlgorithmType algorithm() {
+ return AlgorithmType.SM2;
+ }
+
+ /**
+ * 加密
+ *
+ * @param value 待加密字符串
+ * @param encodeType 加密后的编码格式
+ * @return java.lang.String
+ * @throws Exception 抛出异常
+ * @author 老马
+ * @date 2023/1/10 16:38
+ */
+ @Override
+ public String encrypt(String value, EncodeType encodeType) throws Exception {
+ if(ObjectUtil.isNotNull(this.sm2)) {
+ if (encodeType == EncodeType.HEX) {
+ return sm2.encryptHex(value, KeyType.PublicKey);
+ } else {
+ return sm2.encryptBase64(value, KeyType.PublicKey);
+ }
+ }
+ return value;
+ }
+
+ /**
+ * 解密
+ *
+ * @param value 待加密字符串
+ * @param encodeType 加密后的编码格式
+ * @return java.lang.String
+ * @throws Exception 抛出异常
+ * @author 老马
+ * @date 2023/1/10 16:38
+ */
+ @Override
+ public String decrypt(String value, EncodeType encodeType) throws Exception {
+ if(ObjectUtil.isNotNull(this.sm2)) {
+ return this.sm2.decryptStr(value, KeyType.PrivateKey);
+ }
+ return value;
+ }
+}
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/encrypt/encryptor/Sm4Encryptor.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/encrypt/encryptor/Sm4Encryptor.java
new file mode 100644
index 000000000..248e9874d
--- /dev/null
+++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/encrypt/encryptor/Sm4Encryptor.java
@@ -0,0 +1,77 @@
+package com.ruoyi.framework.encrypt.encryptor;
+
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.crypto.SmUtil;
+import cn.hutool.crypto.symmetric.SM4;
+import com.ruoyi.common.encrypt.IEncryptor;
+import com.ruoyi.common.enums.AlgorithmType;
+import com.ruoyi.common.enums.EncodeType;
+
+import java.nio.charset.StandardCharsets;
+
+/**
+ * sm4算法实现
+ *
+ * @author 老马
+ * @date 2023-01-06 17:40
+ */
+public class Sm4Encryptor implements IEncryptor {
+
+ private SM4 sm4 = null;
+
+ public Sm4Encryptor(String password) {
+ this.sm4 = SmUtil.sm4(password.getBytes(StandardCharsets.UTF_8));
+ }
+
+ /**
+ * 获得当前算法
+ *
+ * @return com.ruoyi.common.enums.AlgorithmType
+ * @author 老马
+ * @date 2023/1/11 11:18
+ */
+ @Override
+ public AlgorithmType algorithm() {
+ return AlgorithmType.SM4;
+ }
+
+ /**
+ * 加密
+ *
+ * @param value 待加密字符串
+ * @param encodeType 加密后的编码格式
+ * @return java.lang.String
+ * @throws Exception 抛出异常
+ * @author 老马
+ * @date 2023/1/10 16:38
+ */
+ @Override
+ public String encrypt(String value, EncodeType encodeType) throws Exception {
+ if(ObjectUtil.isNotNull(this.sm4)) {
+ if (encodeType == EncodeType.HEX) {
+ return sm4.encryptHex(value);
+ } else {
+ return sm4.encryptBase64(value);
+ }
+ }
+ return value;
+ }
+
+ /**
+ * 解密
+ *
+ * @param value 待加密字符串
+ * @param encodeType 加密后的编码格式
+ * @return java.lang.String
+ * @throws Exception 抛出异常
+ * @author 老马
+ * @date 2023/1/10 16:38
+ */
+ @Override
+ public String decrypt(String value, EncodeType encodeType) throws Exception {
+ if(ObjectUtil.isNotNull(this.sm4)) {
+ return this.sm4.decryptStr(value);
+ }
+ return value;
+ }
+}