添加“ruoyi-common-encrypt”加密模块
This commit is contained in:
parent
c14f8d098f
commit
617cf85209
9
pom.xml
9
pom.xml
@ -53,6 +53,8 @@
|
|||||||
<ip2region.version>2.7.0</ip2region.version>
|
<ip2region.version>2.7.0</ip2region.version>
|
||||||
<!-- OSS 配置 -->
|
<!-- OSS 配置 -->
|
||||||
<aws-java-sdk-s3.version>1.12.600</aws-java-sdk-s3.version>
|
<aws-java-sdk-s3.version>1.12.600</aws-java-sdk-s3.version>
|
||||||
|
<!-- 加解密依赖库 -->
|
||||||
|
<bcprov-jdk.version>1.77</bcprov-jdk.version>
|
||||||
|
|
||||||
<!-- 插件版本 -->
|
<!-- 插件版本 -->
|
||||||
<maven-jar-plugin.version>3.2.2</maven-jar-plugin.version>
|
<maven-jar-plugin.version>3.2.2</maven-jar-plugin.version>
|
||||||
@ -341,6 +343,13 @@
|
|||||||
<version>${aws-java-sdk-s3.version}</version>
|
<version>${aws-java-sdk-s3.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 加解密依赖库 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.bouncycastle</groupId>
|
||||||
|
<artifactId>bcprov-jdk18on</artifactId>
|
||||||
|
<version>${bcprov-jdk.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!--Annotation Processor-->
|
<!--Annotation Processor-->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
@ -150,6 +150,33 @@ mybatis-flex:
|
|||||||
# 默认的乐观锁字段
|
# 默认的乐观锁字段
|
||||||
version-column: version
|
version-column: version
|
||||||
|
|
||||||
|
# 数据加密
|
||||||
|
mybatis-encryptor:
|
||||||
|
# 是否开启加密
|
||||||
|
enable: false
|
||||||
|
# 默认加密算法
|
||||||
|
algorithm: BASE64
|
||||||
|
# 编码方式 BASE64/HEX。默认BASE64
|
||||||
|
encode: BASE64
|
||||||
|
# 安全秘钥 对称算法的秘钥 如:AES,SM4
|
||||||
|
password:
|
||||||
|
# 公私钥 非对称算法的公私钥 如:SM2,RSA
|
||||||
|
publicKey:
|
||||||
|
privateKey:
|
||||||
|
|
||||||
|
# api接口加密
|
||||||
|
api-decrypt:
|
||||||
|
# 是否开启全局接口加密
|
||||||
|
enabled: true
|
||||||
|
# AES 加密头标识
|
||||||
|
headerFlag: encrypt-key
|
||||||
|
# 响应加密公钥 非对称算法的公私钥 如:SM2,RSA 使用者请自行更换
|
||||||
|
# 对应前端解密私钥 MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAmc3CuPiGL/LcIIm7zryCEIbl1SPzBkr75E2VMtxegyZ1lYRD+7TZGAPkvIsBcaMs6Nsy0L78n2qh+lIZMpLH8wIDAQABAkEAk82Mhz0tlv6IVCyIcw/s3f0E+WLmtPFyR9/WtV3Y5aaejUkU60JpX4m5xNR2VaqOLTZAYjW8Wy0aXr3zYIhhQQIhAMfqR9oFdYw1J9SsNc+CrhugAvKTi0+BF6VoL6psWhvbAiEAxPPNTmrkmrXwdm/pQQu3UOQmc2vCZ5tiKpW10CgJi8kCIFGkL6utxw93Ncj4exE/gPLvKcT+1Emnoox+O9kRXss5AiAMtYLJDaLEzPrAWcZeeSgSIzbL+ecokmFKSDDcRske6QIgSMkHedwND1olF8vlKsJUGK3BcdtM8w4Xq7BpSBwsloE=
|
||||||
|
publicKey: MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJnNwrj4hi/y3CCJu868ghCG5dUj8wZK++RNlTLcXoMmdZWEQ/u02RgD5LyLAXGjLOjbMtC+/J9qofpSGTKSx/MCAwEAAQ==
|
||||||
|
# 请求解密私钥 非对称算法的公私钥 如:SM2,RSA 使用者请自行更换
|
||||||
|
# 对应前端加密公钥 MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ==
|
||||||
|
privateKey: MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqhHyZfSsYourNxaY7Nt+PrgrxkiA50efORdI5U5lsW79MmFnusUA355oaSXcLhu5xxB38SMSyP2KvuKNPuH3owIDAQABAkAfoiLyL+Z4lf4Myxk6xUDgLaWGximj20CUf+5BKKnlrK+Ed8gAkM0HqoTt2UZwA5E2MzS4EI2gjfQhz5X28uqxAiEA3wNFxfrCZlSZHb0gn2zDpWowcSxQAgiCstxGUoOqlW8CIQDDOerGKH5OmCJ4Z21v+F25WaHYPxCFMvwxpcw99EcvDQIgIdhDTIqD2jfYjPTY8Jj3EDGPbH2HHuffvflECt3Ek60CIQCFRlCkHpi7hthhYhovyloRYsM+IS9h/0BzlEAuO0ktMQIgSPT3aFAgJYwKpqRYKlLDVcflZFCKY7u3UP8iWi1Qw0Y=
|
||||||
|
|
||||||
# SpringDoc配置
|
# SpringDoc配置
|
||||||
springdoc:
|
springdoc:
|
||||||
#需要扫描的包,可以配置多个,使用逗号分割
|
#需要扫描的包,可以配置多个,使用逗号分割
|
||||||
@ -222,15 +249,6 @@ lock4j:
|
|||||||
# 分布式锁的超时时间,默认为 30 秒
|
# 分布式锁的超时时间,默认为 30 秒
|
||||||
expire: 30000
|
expire: 30000
|
||||||
|
|
||||||
## token配置
|
|
||||||
#token:
|
|
||||||
# # 令牌自定义标识
|
|
||||||
# header: Authorization
|
|
||||||
# # 令牌密钥
|
|
||||||
# secret: abcdefghijklmnopqrstuvwxyz
|
|
||||||
# # 令牌有效期(默认30分钟)
|
|
||||||
# expireTime: 30
|
|
||||||
|
|
||||||
# Sa-Token配置
|
# Sa-Token配置
|
||||||
sa-token:
|
sa-token:
|
||||||
# token名称 (同时也是cookie名称)
|
# token名称 (同时也是cookie名称)
|
||||||
@ -281,16 +299,6 @@ security:
|
|||||||
tenant:
|
tenant:
|
||||||
# 是否开启
|
# 是否开启
|
||||||
enable: false
|
enable: false
|
||||||
# 排除表
|
|
||||||
excludes:
|
|
||||||
- sys_menu
|
|
||||||
- sys_tenant
|
|
||||||
- sys_tenant_package
|
|
||||||
- sys_role_dept
|
|
||||||
- sys_role_menu
|
|
||||||
- sys_user_post
|
|
||||||
- sys_user_role
|
|
||||||
- sys_client
|
|
||||||
|
|
||||||
--- # Actuator 监控端点的配置项
|
--- # Actuator 监控端点的配置项
|
||||||
management:
|
management:
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
<modules>
|
<modules>
|
||||||
<module>ruoyi-common-bom</module>
|
<module>ruoyi-common-bom</module>
|
||||||
<module>ruoyi-common-core</module>
|
<module>ruoyi-common-core</module>
|
||||||
|
<module>ruoyi-common-encrypt</module>
|
||||||
<module>ruoyi-common-excel</module>
|
<module>ruoyi-common-excel</module>
|
||||||
<module>ruoyi-common-job</module>
|
<module>ruoyi-common-job</module>
|
||||||
<module>ruoyi-common-json</module>
|
<module>ruoyi-common-json</module>
|
||||||
|
@ -26,6 +26,13 @@
|
|||||||
<version>${revision}</version>
|
<version>${revision}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 加解密模块 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.ruoyi</groupId>
|
||||||
|
<artifactId>ruoyi-common-encrypt</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- excel模块 -->
|
<!-- excel模块 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.ruoyi</groupId>
|
<groupId>com.ruoyi</groupId>
|
||||||
|
46
ruoyi-common/ruoyi-common-encrypt/pom.xml
Normal file
46
ruoyi-common/ruoyi-common-encrypt/pom.xml
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<?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>
|
||||||
|
</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-jdk18on</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.hutool</groupId>
|
||||||
|
<artifactId>hutool-crypto</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.ruoyi</groupId>
|
||||||
|
<artifactId>ruoyi-common-orm</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-webmvc</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
@ -0,0 +1,20 @@
|
|||||||
|
package com.ruoyi.common.encrypt.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 强制加密注解
|
||||||
|
*
|
||||||
|
* @author Michelle.Chung
|
||||||
|
*/
|
||||||
|
@Documented
|
||||||
|
@Target({ElementType.METHOD})
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface ApiEncrypt {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 响应加密忽略,默认不加密,为 true 时加密
|
||||||
|
*/
|
||||||
|
boolean response() default false;
|
||||||
|
|
||||||
|
}
|
@ -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;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
package com.ruoyi.common.encrypt.config;
|
||||||
|
|
||||||
|
import com.ruoyi.common.encrypt.filter.CryptoFilter;
|
||||||
|
import jakarta.servlet.DispatcherType;
|
||||||
|
import com.ruoyi.common.encrypt.properties.ApiDecryptProperties;
|
||||||
|
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
|
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* api 解密自动配置
|
||||||
|
*
|
||||||
|
* @author wdhcr
|
||||||
|
*/
|
||||||
|
@AutoConfiguration
|
||||||
|
@EnableConfigurationProperties(ApiDecryptProperties.class)
|
||||||
|
@ConditionalOnProperty(value = "api-decrypt.enabled", havingValue = "true")
|
||||||
|
public class ApiDecryptAutoConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public FilterRegistrationBean<CryptoFilter> cryptoFilterRegistration(ApiDecryptProperties properties) {
|
||||||
|
FilterRegistrationBean<CryptoFilter> registration = new FilterRegistrationBean<>();
|
||||||
|
registration.setDispatcherTypes(DispatcherType.REQUEST);
|
||||||
|
registration.setFilter(new CryptoFilter(properties));
|
||||||
|
registration.addUrlPatterns("/*");
|
||||||
|
registration.setName("cryptoFilter");
|
||||||
|
registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE);
|
||||||
|
return registration;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
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.boot.context.properties.EnableConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加解密配置
|
||||||
|
*
|
||||||
|
* @author 老马
|
||||||
|
* @version 4.6.0
|
||||||
|
*/
|
||||||
|
@AutoConfiguration
|
||||||
|
@EnableConfigurationProperties(EncryptorProperties.class)
|
||||||
|
@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);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
package com.ruoyi.common.encrypt.core;
|
||||||
|
|
||||||
|
import com.ruoyi.common.encrypt.enumd.EncodeType;
|
||||||
|
import lombok.Data;
|
||||||
|
import com.ruoyi.common.encrypt.enumd.AlgorithmType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加密上下文 用于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;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,100 @@
|
|||||||
|
package com.ruoyi.common.encrypt.core;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.ReflectUtil;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import com.ruoyi.common.encrypt.annotation.EncryptField;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
|
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 -> {
|
||||||
|
Set<Field> fieldSet = new HashSet<>();
|
||||||
|
while (clazz != null) {
|
||||||
|
Field[] fields = clazz.getDeclaredFields();
|
||||||
|
fieldSet.addAll(Arrays.asList(fields));
|
||||||
|
clazz = clazz.getSuperclass();
|
||||||
|
}
|
||||||
|
fieldSet = fieldSet.stream().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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
package com.ruoyi.common.encrypt.core.encryptor;
|
||||||
|
|
||||||
|
import com.ruoyi.common.encrypt.core.IEncryptor;
|
||||||
|
import com.ruoyi.common.encrypt.core.EncryptContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 所有加密执行者的基类
|
||||||
|
*
|
||||||
|
* @author 老马
|
||||||
|
* @version 4.6.0
|
||||||
|
*/
|
||||||
|
public abstract class AbstractEncryptor implements IEncryptor {
|
||||||
|
|
||||||
|
public AbstractEncryptor(EncryptContext context) {
|
||||||
|
// 用户配置校验与配置注入
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,55 @@
|
|||||||
|
package com.ruoyi.common.encrypt.core.encryptor;
|
||||||
|
|
||||||
|
import com.ruoyi.common.encrypt.core.EncryptContext;
|
||||||
|
import com.ruoyi.common.encrypt.enumd.AlgorithmType;
|
||||||
|
import com.ruoyi.common.encrypt.enumd.EncodeType;
|
||||||
|
import com.ruoyi.common.encrypt.utils.EncryptUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AES算法实现
|
||||||
|
*
|
||||||
|
* @author 老马
|
||||||
|
* @version 4.6.0
|
||||||
|
*/
|
||||||
|
public class AesEncryptor extends AbstractEncryptor {
|
||||||
|
|
||||||
|
private final EncryptContext context;
|
||||||
|
|
||||||
|
public AesEncryptor(EncryptContext context) {
|
||||||
|
super(context);
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得当前算法
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public AlgorithmType algorithm() {
|
||||||
|
return AlgorithmType.AES;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加密
|
||||||
|
*
|
||||||
|
* @param value 待加密字符串
|
||||||
|
* @param encodeType 加密后的编码格式
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String encrypt(String value, EncodeType encodeType) {
|
||||||
|
if (encodeType == EncodeType.HEX) {
|
||||||
|
return EncryptUtils.encryptByAesHex(value, context.getPassword());
|
||||||
|
} else {
|
||||||
|
return EncryptUtils.encryptByAes(value, context.getPassword());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解密
|
||||||
|
*
|
||||||
|
* @param value 待加密字符串
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String decrypt(String value) {
|
||||||
|
return EncryptUtils.decryptByAes(value, context.getPassword());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
package com.ruoyi.common.encrypt.core.encryptor;
|
||||||
|
|
||||||
|
import com.ruoyi.common.encrypt.core.EncryptContext;
|
||||||
|
import com.ruoyi.common.encrypt.enumd.AlgorithmType;
|
||||||
|
import com.ruoyi.common.encrypt.enumd.EncodeType;
|
||||||
|
import com.ruoyi.common.encrypt.utils.EncryptUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 EncryptUtils.encryptByBase64(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解密
|
||||||
|
*
|
||||||
|
* @param value 待加密字符串
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String decrypt(String value) {
|
||||||
|
return EncryptUtils.decryptByBase64(value);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
package com.ruoyi.common.encrypt.core.encryptor;
|
||||||
|
|
||||||
|
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;
|
||||||
|
import com.ruoyi.common.encrypt.utils.EncryptUtils;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RSA算法实现
|
||||||
|
*
|
||||||
|
* @author 老马
|
||||||
|
* @version 4.6.0
|
||||||
|
*/
|
||||||
|
public class RsaEncryptor extends AbstractEncryptor {
|
||||||
|
|
||||||
|
private final EncryptContext context;
|
||||||
|
|
||||||
|
public RsaEncryptor(EncryptContext context) {
|
||||||
|
super(context);
|
||||||
|
String privateKey = context.getPrivateKey();
|
||||||
|
String publicKey = context.getPublicKey();
|
||||||
|
if (StringUtils.isAnyEmpty(privateKey, publicKey)) {
|
||||||
|
throw new IllegalArgumentException("RSA公私钥均需要提供,公钥加密,私钥解密。");
|
||||||
|
}
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得当前算法
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public AlgorithmType algorithm() {
|
||||||
|
return AlgorithmType.RSA;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加密
|
||||||
|
*
|
||||||
|
* @param value 待加密字符串
|
||||||
|
* @param encodeType 加密后的编码格式
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String encrypt(String value, EncodeType encodeType) {
|
||||||
|
if (encodeType == EncodeType.HEX) {
|
||||||
|
return EncryptUtils.encryptByRsaHex(value, context.getPublicKey());
|
||||||
|
} else {
|
||||||
|
return EncryptUtils.encryptByRsa(value, context.getPublicKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解密
|
||||||
|
*
|
||||||
|
* @param value 待加密字符串
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String decrypt(String value) {
|
||||||
|
return EncryptUtils.decryptByRsa(value, context.getPrivateKey());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,61 @@
|
|||||||
|
package com.ruoyi.common.encrypt.core.encryptor;
|
||||||
|
|
||||||
|
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;
|
||||||
|
import com.ruoyi.common.encrypt.utils.EncryptUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sm2算法实现
|
||||||
|
*
|
||||||
|
* @author 老马
|
||||||
|
* @version 4.6.0
|
||||||
|
*/
|
||||||
|
public class Sm2Encryptor extends AbstractEncryptor {
|
||||||
|
|
||||||
|
private final EncryptContext context;
|
||||||
|
|
||||||
|
public Sm2Encryptor(EncryptContext context) {
|
||||||
|
super(context);
|
||||||
|
String privateKey = context.getPrivateKey();
|
||||||
|
String publicKey = context.getPublicKey();
|
||||||
|
if (StringUtils.isAnyEmpty(privateKey, publicKey)) {
|
||||||
|
throw new IllegalArgumentException("SM2公私钥均需要提供,公钥加密,私钥解密。");
|
||||||
|
}
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得当前算法
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public AlgorithmType algorithm() {
|
||||||
|
return AlgorithmType.SM2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加密
|
||||||
|
*
|
||||||
|
* @param value 待加密字符串
|
||||||
|
* @param encodeType 加密后的编码格式
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String encrypt(String value, EncodeType encodeType) {
|
||||||
|
if (encodeType == EncodeType.HEX) {
|
||||||
|
return EncryptUtils.encryptBySm2Hex(value, context.getPublicKey());
|
||||||
|
} else {
|
||||||
|
return EncryptUtils.encryptBySm2(value, context.getPublicKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解密
|
||||||
|
*
|
||||||
|
* @param value 待加密字符串
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String decrypt(String value) {
|
||||||
|
return EncryptUtils.decryptBySm2(value, context.getPrivateKey());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,55 @@
|
|||||||
|
package com.ruoyi.common.encrypt.core.encryptor;
|
||||||
|
|
||||||
|
import com.ruoyi.common.encrypt.core.EncryptContext;
|
||||||
|
import com.ruoyi.common.encrypt.enumd.AlgorithmType;
|
||||||
|
import com.ruoyi.common.encrypt.enumd.EncodeType;
|
||||||
|
import com.ruoyi.common.encrypt.utils.EncryptUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sm4算法实现
|
||||||
|
*
|
||||||
|
* @author 老马
|
||||||
|
* @version 4.6.0
|
||||||
|
*/
|
||||||
|
public class Sm4Encryptor extends AbstractEncryptor {
|
||||||
|
|
||||||
|
private final EncryptContext context;
|
||||||
|
|
||||||
|
public Sm4Encryptor(EncryptContext context) {
|
||||||
|
super(context);
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得当前算法
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public AlgorithmType algorithm() {
|
||||||
|
return AlgorithmType.SM4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加密
|
||||||
|
*
|
||||||
|
* @param value 待加密字符串
|
||||||
|
* @param encodeType 加密后的编码格式
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String encrypt(String value, EncodeType encodeType) {
|
||||||
|
if (encodeType == EncodeType.HEX) {
|
||||||
|
return EncryptUtils.encryptBySm4Hex(value, context.getPassword());
|
||||||
|
} else {
|
||||||
|
return EncryptUtils.encryptBySm4(value, context.getPassword());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解密
|
||||||
|
*
|
||||||
|
* @param value 待加密字符串
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String decrypt(String value) {
|
||||||
|
return EncryptUtils.decryptBySm4(value, context.getPassword());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
package com.ruoyi.common.encrypt.enumd;
|
||||||
|
|
||||||
|
import com.ruoyi.common.encrypt.core.encryptor.*;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import com.ruoyi.common.encrypt.core.encryptor.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 算法名称
|
||||||
|
*
|
||||||
|
* @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;
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package com.ruoyi.common.encrypt.enumd;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 编码类型
|
||||||
|
*
|
||||||
|
* @author 老马
|
||||||
|
* @version 4.6.0
|
||||||
|
*/
|
||||||
|
public enum EncodeType {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认使用yml配置
|
||||||
|
*/
|
||||||
|
DEFAULT,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* base64编码
|
||||||
|
*/
|
||||||
|
BASE64,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 16进制编码
|
||||||
|
*/
|
||||||
|
HEX;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,115 @@
|
|||||||
|
package com.ruoyi.common.encrypt.filter;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
|
import jakarta.servlet.*;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import com.ruoyi.common.core.constant.HttpStatus;
|
||||||
|
import com.ruoyi.common.core.exception.ServiceException;
|
||||||
|
import com.ruoyi.common.core.utils.SpringUtils;
|
||||||
|
import com.ruoyi.common.core.utils.StringUtils;
|
||||||
|
import com.ruoyi.common.encrypt.annotation.ApiEncrypt;
|
||||||
|
import com.ruoyi.common.encrypt.properties.ApiDecryptProperties;
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.web.method.HandlerMethod;
|
||||||
|
import org.springframework.web.servlet.HandlerExceptionResolver;
|
||||||
|
import org.springframework.web.servlet.HandlerExecutionChain;
|
||||||
|
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crypto 过滤器
|
||||||
|
*
|
||||||
|
* @author wdhcr
|
||||||
|
*/
|
||||||
|
public class CryptoFilter implements Filter {
|
||||||
|
private final ApiDecryptProperties properties;
|
||||||
|
|
||||||
|
public CryptoFilter(ApiDecryptProperties properties) {
|
||||||
|
this.properties = properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
|
||||||
|
HttpServletRequest servletRequest = (HttpServletRequest) request;
|
||||||
|
HttpServletResponse servletResponse = (HttpServletResponse) response;
|
||||||
|
|
||||||
|
boolean encryptFlag = false;
|
||||||
|
ServletRequest requestWrapper = null;
|
||||||
|
ServletResponse responseWrapper = null;
|
||||||
|
EncryptResponseBodyWrapper responseBodyWrapper = null;
|
||||||
|
|
||||||
|
// 是否为 json 请求
|
||||||
|
if (StringUtils.startsWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE)) {
|
||||||
|
// 是否为 put 或者 post 请求
|
||||||
|
if (HttpMethod.PUT.matches(servletRequest.getMethod()) || HttpMethod.POST.matches(servletRequest.getMethod())) {
|
||||||
|
// 是否存在加密标头
|
||||||
|
String headerValue = servletRequest.getHeader(properties.getHeaderFlag());
|
||||||
|
if (StringUtils.isNotBlank(headerValue)) {
|
||||||
|
// 请求解密
|
||||||
|
requestWrapper = new DecryptRequestBodyWrapper(servletRequest, properties.getPrivateKey(), properties.getHeaderFlag());
|
||||||
|
// 获取加密注解
|
||||||
|
ApiEncrypt apiEncrypt = this.getApiEncryptAnnotation(servletRequest);
|
||||||
|
if (ObjectUtil.isNotNull(apiEncrypt)) {
|
||||||
|
// 响应加密标志
|
||||||
|
encryptFlag = apiEncrypt.response();
|
||||||
|
} else {
|
||||||
|
// 是否有注解,有就报错,没有放行
|
||||||
|
HandlerExceptionResolver exceptionResolver = SpringUtils.getBean(HandlerExceptionResolver.class);
|
||||||
|
exceptionResolver.resolveException(
|
||||||
|
servletRequest, servletResponse, null,
|
||||||
|
new ServiceException("没有访问权限,请联系管理员授权", HttpStatus.FORBIDDEN));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 判断是否响应加密
|
||||||
|
if (encryptFlag) {
|
||||||
|
responseBodyWrapper = new EncryptResponseBodyWrapper(servletResponse);
|
||||||
|
responseWrapper = responseBodyWrapper;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
chain.doFilter(
|
||||||
|
ObjectUtil.defaultIfNull(requestWrapper, request),
|
||||||
|
ObjectUtil.defaultIfNull(responseWrapper, response));
|
||||||
|
|
||||||
|
if (encryptFlag) {
|
||||||
|
servletResponse.reset();
|
||||||
|
// 对原始内容加密
|
||||||
|
String encryptContent = responseBodyWrapper.getEncryptContent(
|
||||||
|
servletResponse, properties.getPublicKey(), properties.getHeaderFlag());
|
||||||
|
// 对加密后的内容写出
|
||||||
|
servletResponse.getWriter().write(encryptContent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取 ApiEncrypt 注解
|
||||||
|
*/
|
||||||
|
private ApiEncrypt getApiEncryptAnnotation(HttpServletRequest servletRequest) {
|
||||||
|
RequestMappingHandlerMapping handlerMapping = SpringUtils.getBean("requestMappingHandlerMapping", RequestMappingHandlerMapping.class);
|
||||||
|
// 获取注解
|
||||||
|
try {
|
||||||
|
HandlerExecutionChain mappingHandler = handlerMapping.getHandler(servletRequest);
|
||||||
|
if (ObjectUtil.isNotNull(mappingHandler)) {
|
||||||
|
Object handler = mappingHandler.getHandler();
|
||||||
|
if (ObjectUtil.isNotNull(handler)) {
|
||||||
|
// 从handler获取注解
|
||||||
|
if (handler instanceof HandlerMethod handlerMethod) {
|
||||||
|
return handlerMethod.getMethodAnnotation(ApiEncrypt.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void destroy() {
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,94 @@
|
|||||||
|
package com.ruoyi.common.encrypt.filter;
|
||||||
|
|
||||||
|
import cn.hutool.core.io.IoUtil;
|
||||||
|
import com.ruoyi.common.encrypt.utils.EncryptUtils;
|
||||||
|
import jakarta.servlet.ReadListener;
|
||||||
|
import jakarta.servlet.ServletInputStream;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletRequestWrapper;
|
||||||
|
import com.ruoyi.common.core.constant.Constants;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解密请求参数工具类
|
||||||
|
*
|
||||||
|
* @author wdhcr
|
||||||
|
*/
|
||||||
|
public class DecryptRequestBodyWrapper extends HttpServletRequestWrapper {
|
||||||
|
|
||||||
|
private final byte[] body;
|
||||||
|
|
||||||
|
public DecryptRequestBodyWrapper(HttpServletRequest request, String privateKey, String headerFlag) throws IOException {
|
||||||
|
super(request);
|
||||||
|
// 获取 AES 密码 采用 RSA 加密
|
||||||
|
String headerRsa = request.getHeader(headerFlag);
|
||||||
|
String decryptAes = EncryptUtils.decryptByRsa(headerRsa, privateKey);
|
||||||
|
// 解密 AES 密码
|
||||||
|
String aesPassword = EncryptUtils.decryptByBase64(decryptAes);
|
||||||
|
request.setCharacterEncoding(Constants.UTF8);
|
||||||
|
byte[] readBytes = IoUtil.readBytes(request.getInputStream(), false);
|
||||||
|
String requestBody = new String(readBytes, StandardCharsets.UTF_8);
|
||||||
|
// 解密 body 采用 AES 加密
|
||||||
|
String decryptBody = EncryptUtils.decryptByAes(requestBody, aesPassword);
|
||||||
|
body = decryptBody.getBytes(StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BufferedReader getReader() {
|
||||||
|
return new BufferedReader(new InputStreamReader(getInputStream()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getContentLength() {
|
||||||
|
return body.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getContentLengthLong() {
|
||||||
|
return body.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getContentType() {
|
||||||
|
return MediaType.APPLICATION_JSON_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ServletInputStream getInputStream() {
|
||||||
|
final ByteArrayInputStream bais = new ByteArrayInputStream(body);
|
||||||
|
return new ServletInputStream() {
|
||||||
|
@Override
|
||||||
|
public int read() {
|
||||||
|
return bais.read();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int available() {
|
||||||
|
return body.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isFinished() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isReady() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setReadListener(ReadListener readListener) {
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,120 @@
|
|||||||
|
package com.ruoyi.common.encrypt.filter;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.RandomUtil;
|
||||||
|
import com.ruoyi.common.encrypt.utils.EncryptUtils;
|
||||||
|
import jakarta.servlet.ServletOutputStream;
|
||||||
|
import jakarta.servlet.WriteListener;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import jakarta.servlet.http.HttpServletResponseWrapper;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加密响应参数包装类
|
||||||
|
*
|
||||||
|
* @author Michelle.Chung
|
||||||
|
*/
|
||||||
|
public class EncryptResponseBodyWrapper extends HttpServletResponseWrapper {
|
||||||
|
|
||||||
|
private final ByteArrayOutputStream byteArrayOutputStream;
|
||||||
|
private final ServletOutputStream servletOutputStream;
|
||||||
|
private final PrintWriter printWriter;
|
||||||
|
|
||||||
|
public EncryptResponseBodyWrapper(HttpServletResponse response) throws IOException {
|
||||||
|
super(response);
|
||||||
|
this.byteArrayOutputStream = new ByteArrayOutputStream();
|
||||||
|
this.servletOutputStream = this.getOutputStream();
|
||||||
|
this.printWriter = new PrintWriter(new OutputStreamWriter(byteArrayOutputStream));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PrintWriter getWriter() {
|
||||||
|
return printWriter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void flushBuffer() throws IOException {
|
||||||
|
if (servletOutputStream != null) {
|
||||||
|
servletOutputStream.flush();
|
||||||
|
}
|
||||||
|
if (printWriter != null) {
|
||||||
|
printWriter.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reset() {
|
||||||
|
byteArrayOutputStream.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getResponseData() throws IOException {
|
||||||
|
flushBuffer();
|
||||||
|
return byteArrayOutputStream.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getContent() throws IOException {
|
||||||
|
flushBuffer();
|
||||||
|
return byteArrayOutputStream.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取加密内容
|
||||||
|
*
|
||||||
|
* @param servletResponse response
|
||||||
|
* @param publicKey RSA公钥 (用于加密 AES 秘钥)
|
||||||
|
* @param headerFlag 请求头标志
|
||||||
|
* @return 加密内容
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public String getEncryptContent(HttpServletResponse servletResponse, String publicKey, String headerFlag) throws IOException {
|
||||||
|
// 生成秘钥
|
||||||
|
String aesPassword = RandomUtil.randomString(32);
|
||||||
|
// 秘钥使用 Base64 编码
|
||||||
|
String encryptAes = EncryptUtils.encryptByBase64(aesPassword);
|
||||||
|
// Rsa 公钥加密 Base64 编码
|
||||||
|
String encryptPassword = EncryptUtils.encryptByRsa(encryptAes, publicKey);
|
||||||
|
|
||||||
|
// 设置响应头
|
||||||
|
servletResponse.setHeader(headerFlag, encryptPassword);
|
||||||
|
servletResponse.setHeader("Access-Control-Allow-Origin", "*");
|
||||||
|
servletResponse.setHeader("Access-Control-Allow-Methods", "*");
|
||||||
|
servletResponse.setCharacterEncoding(StandardCharsets.UTF_8.toString());
|
||||||
|
|
||||||
|
// 获取原始内容
|
||||||
|
String originalBody = this.getContent();
|
||||||
|
// 对内容进行加密
|
||||||
|
return EncryptUtils.encryptByAes(originalBody, aesPassword);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ServletOutputStream getOutputStream() throws IOException {
|
||||||
|
return new ServletOutputStream() {
|
||||||
|
@Override
|
||||||
|
public boolean isReady() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setWriteListener(WriteListener writeListener) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(int b) throws IOException {
|
||||||
|
byteArrayOutputStream.write(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(byte[] b) throws IOException {
|
||||||
|
byteArrayOutputStream.write(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(byte[] b, int off, int len) throws IOException {
|
||||||
|
byteArrayOutputStream.write(b, off, len);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,116 @@
|
|||||||
|
package com.ruoyi.common.encrypt.interceptor;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import cn.hutool.core.convert.Convert;
|
||||||
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.ibatis.executor.resultset.ResultSetHandler;
|
||||||
|
import org.apache.ibatis.plugin.*;
|
||||||
|
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 java.lang.reflect.Field;
|
||||||
|
import java.sql.Statement;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 出参解密拦截器
|
||||||
|
*
|
||||||
|
* @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 (ObjectUtil.isNull(sourceObject)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (sourceObject instanceof Map<?, ?> map) {
|
||||||
|
new HashSet<>(map.values()).forEach(this::decryptHandler);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (sourceObject instanceof List<?> list) {
|
||||||
|
if(CollUtil.isEmpty(list)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 判断第一个元素是否含有注解。如果没有直接返回,提高效率
|
||||||
|
Object firstItem = list.get(0);
|
||||||
|
if (ObjectUtil.isNull(firstItem) || CollUtil.isEmpty(encryptorManager.getFieldCache(firstItem.getClass()))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
list.forEach(this::decryptHandler);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Set<Field> fields = encryptorManager.getFieldCache(sourceObject.getClass());
|
||||||
|
try {
|
||||||
|
for (Field field : fields) {
|
||||||
|
field.set(sourceObject, this.decryptField(Convert.toStr(field.get(sourceObject)), field));
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("处理解密字段时出错", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字段值进行加密。通过字段的批注注册新的加密算法
|
||||||
|
*
|
||||||
|
* @param value 待加密的值
|
||||||
|
* @param field 待加密字段
|
||||||
|
* @return 加密后结果
|
||||||
|
*/
|
||||||
|
private String decryptField(String value, Field field) {
|
||||||
|
if (ObjectUtil.isNull(value)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
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) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,120 @@
|
|||||||
|
package com.ruoyi.common.encrypt.interceptor;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import cn.hutool.core.convert.Convert;
|
||||||
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
|
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 com.ruoyi.common.core.utils.StringUtils;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 入参加密拦截器
|
||||||
|
*
|
||||||
|
* @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) {
|
||||||
|
// 进行加密操作
|
||||||
|
Object parameterObject = parameterHandler.getParameterObject();
|
||||||
|
if (ObjectUtil.isNotNull(parameterObject) && !(parameterObject instanceof String)) {
|
||||||
|
this.encryptHandler(parameterObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加密对象
|
||||||
|
*
|
||||||
|
* @param sourceObject 待加密对象
|
||||||
|
*/
|
||||||
|
private void encryptHandler(Object sourceObject) {
|
||||||
|
if (ObjectUtil.isNull(sourceObject)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (sourceObject instanceof Map<?, ?> map) {
|
||||||
|
new HashSet<>(map.values()).forEach(this::encryptHandler);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (sourceObject instanceof List<?> list) {
|
||||||
|
if(CollUtil.isEmpty(list)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 判断第一个元素是否含有注解。如果没有直接返回,提高效率
|
||||||
|
Object firstItem = list.get(0);
|
||||||
|
if (ObjectUtil.isNull(firstItem) || CollUtil.isEmpty(encryptorManager.getFieldCache(firstItem.getClass()))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
list.forEach(this::encryptHandler);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Set<Field> fields = encryptorManager.getFieldCache(sourceObject.getClass());
|
||||||
|
try {
|
||||||
|
for (Field field : fields) {
|
||||||
|
field.set(sourceObject, this.encryptField(Convert.toStr(field.get(sourceObject)), field));
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("处理加密字段时出错", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字段值进行加密。通过字段的批注注册新的加密算法
|
||||||
|
*
|
||||||
|
* @param value 待加密的值
|
||||||
|
* @param field 待加密字段
|
||||||
|
* @return 加密后结果
|
||||||
|
*/
|
||||||
|
private String encryptField(String value, Field field) {
|
||||||
|
if (ObjectUtil.isNull(value)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
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) {
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
package com.ruoyi.common.encrypt.properties;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* api解密属性配置类
|
||||||
|
* @author wdhcr
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ConfigurationProperties(prefix = "api-decrypt")
|
||||||
|
public class ApiDecryptProperties {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加密开关
|
||||||
|
*/
|
||||||
|
private Boolean enabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 头部标识
|
||||||
|
*/
|
||||||
|
private String headerFlag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 响应加密公钥
|
||||||
|
*/
|
||||||
|
private String publicKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求解密私钥
|
||||||
|
*/
|
||||||
|
private String privateKey;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加解密属性配置类
|
||||||
|
*
|
||||||
|
* @author 老马
|
||||||
|
* @version 4.6.0
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@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;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,311 @@
|
|||||||
|
package com.ruoyi.common.encrypt.utils;
|
||||||
|
|
||||||
|
import cn.hutool.core.codec.Base64;
|
||||||
|
import cn.hutool.core.util.ArrayUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.hutool.crypto.SecureUtil;
|
||||||
|
import cn.hutool.crypto.SmUtil;
|
||||||
|
import cn.hutool.crypto.asymmetric.KeyType;
|
||||||
|
import cn.hutool.crypto.asymmetric.RSA;
|
||||||
|
import cn.hutool.crypto.asymmetric.SM2;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安全相关工具类
|
||||||
|
*
|
||||||
|
* @author 老马
|
||||||
|
*/
|
||||||
|
public class EncryptUtils {
|
||||||
|
/**
|
||||||
|
* 公钥
|
||||||
|
*/
|
||||||
|
public static final String PUBLIC_KEY = "publicKey";
|
||||||
|
/**
|
||||||
|
* 私钥
|
||||||
|
*/
|
||||||
|
public static final String PRIVATE_KEY = "privateKey";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base64加密
|
||||||
|
*
|
||||||
|
* @param data 待加密数据
|
||||||
|
* @return 加密后字符串
|
||||||
|
*/
|
||||||
|
public static String encryptByBase64(String data) {
|
||||||
|
return Base64.encode(data, StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base64解密
|
||||||
|
*
|
||||||
|
* @param data 待解密数据
|
||||||
|
* @return 解密后字符串
|
||||||
|
*/
|
||||||
|
public static String decryptByBase64(String data) {
|
||||||
|
return Base64.decodeStr(data, StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AES加密
|
||||||
|
*
|
||||||
|
* @param data 待解密数据
|
||||||
|
* @param password 秘钥字符串
|
||||||
|
* @return 加密后字符串, 采用Base64编码
|
||||||
|
*/
|
||||||
|
public static String encryptByAes(String data, String password) {
|
||||||
|
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位");
|
||||||
|
}
|
||||||
|
return SecureUtil.aes(password.getBytes(StandardCharsets.UTF_8)).encryptBase64(data, StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AES加密
|
||||||
|
*
|
||||||
|
* @param data 待解密数据
|
||||||
|
* @param password 秘钥字符串
|
||||||
|
* @return 加密后字符串, 采用Hex编码
|
||||||
|
*/
|
||||||
|
public static String encryptByAesHex(String data, String password) {
|
||||||
|
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位");
|
||||||
|
}
|
||||||
|
return SecureUtil.aes(password.getBytes(StandardCharsets.UTF_8)).encryptHex(data, StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AES解密
|
||||||
|
*
|
||||||
|
* @param data 待解密数据
|
||||||
|
* @param password 秘钥字符串
|
||||||
|
* @return 解密后字符串
|
||||||
|
*/
|
||||||
|
public static String decryptByAes(String data, String password) {
|
||||||
|
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位");
|
||||||
|
}
|
||||||
|
return SecureUtil.aes(password.getBytes(StandardCharsets.UTF_8)).decryptStr(data, StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sm4加密
|
||||||
|
*
|
||||||
|
* @param data 待加密数据
|
||||||
|
* @param password 秘钥字符串
|
||||||
|
* @return 加密后字符串, 采用Base64编码
|
||||||
|
*/
|
||||||
|
public static String encryptBySm4(String data, String password) {
|
||||||
|
if (StrUtil.isBlank(password)) {
|
||||||
|
throw new IllegalArgumentException("SM4需要传入秘钥信息");
|
||||||
|
}
|
||||||
|
// sm4算法的秘钥要求是16位长度
|
||||||
|
int sm4PasswordLength = 16;
|
||||||
|
if (sm4PasswordLength != password.length()) {
|
||||||
|
throw new IllegalArgumentException("SM4秘钥长度要求为16位");
|
||||||
|
}
|
||||||
|
return SmUtil.sm4(password.getBytes(StandardCharsets.UTF_8)).encryptBase64(data, StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sm4加密
|
||||||
|
*
|
||||||
|
* @param data 待加密数据
|
||||||
|
* @param password 秘钥字符串
|
||||||
|
* @return 加密后字符串, 采用Base64编码
|
||||||
|
*/
|
||||||
|
public static String encryptBySm4Hex(String data, String password) {
|
||||||
|
if (StrUtil.isBlank(password)) {
|
||||||
|
throw new IllegalArgumentException("SM4需要传入秘钥信息");
|
||||||
|
}
|
||||||
|
// sm4算法的秘钥要求是16位长度
|
||||||
|
int sm4PasswordLength = 16;
|
||||||
|
if (sm4PasswordLength != password.length()) {
|
||||||
|
throw new IllegalArgumentException("SM4秘钥长度要求为16位");
|
||||||
|
}
|
||||||
|
return SmUtil.sm4(password.getBytes(StandardCharsets.UTF_8)).encryptHex(data, StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sm4解密
|
||||||
|
*
|
||||||
|
* @param data 待解密数据
|
||||||
|
* @param password 秘钥字符串
|
||||||
|
* @return 解密后字符串
|
||||||
|
*/
|
||||||
|
public static String decryptBySm4(String data, String password) {
|
||||||
|
if (StrUtil.isBlank(password)) {
|
||||||
|
throw new IllegalArgumentException("SM4需要传入秘钥信息");
|
||||||
|
}
|
||||||
|
// sm4算法的秘钥要求是16位长度
|
||||||
|
int sm4PasswordLength = 16;
|
||||||
|
if (sm4PasswordLength != password.length()) {
|
||||||
|
throw new IllegalArgumentException("SM4秘钥长度要求为16位");
|
||||||
|
}
|
||||||
|
return SmUtil.sm4(password.getBytes(StandardCharsets.UTF_8)).decryptStr(data, StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 产生sm2加解密需要的公钥和私钥
|
||||||
|
*
|
||||||
|
* @return 公私钥Map
|
||||||
|
*/
|
||||||
|
public static Map<String, String> generateSm2Key() {
|
||||||
|
Map<String, String> keyMap = new HashMap<>(2);
|
||||||
|
SM2 sm2 = SmUtil.sm2();
|
||||||
|
keyMap.put(PRIVATE_KEY, sm2.getPrivateKeyBase64());
|
||||||
|
keyMap.put(PUBLIC_KEY, sm2.getPublicKeyBase64());
|
||||||
|
return keyMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sm2公钥加密
|
||||||
|
*
|
||||||
|
* @param data 待加密数据
|
||||||
|
* @param publicKey 公钥
|
||||||
|
* @return 加密后字符串, 采用Base64编码
|
||||||
|
*/
|
||||||
|
public static String encryptBySm2(String data, String publicKey) {
|
||||||
|
if (StrUtil.isBlank(publicKey)) {
|
||||||
|
throw new IllegalArgumentException("SM2需要传入公钥进行加密");
|
||||||
|
}
|
||||||
|
SM2 sm2 = SmUtil.sm2(null, publicKey);
|
||||||
|
return sm2.encryptBase64(data, StandardCharsets.UTF_8, KeyType.PublicKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sm2公钥加密
|
||||||
|
*
|
||||||
|
* @param data 待加密数据
|
||||||
|
* @param publicKey 公钥
|
||||||
|
* @return 加密后字符串, 采用Hex编码
|
||||||
|
*/
|
||||||
|
public static String encryptBySm2Hex(String data, String publicKey) {
|
||||||
|
if (StrUtil.isBlank(publicKey)) {
|
||||||
|
throw new IllegalArgumentException("SM2需要传入公钥进行加密");
|
||||||
|
}
|
||||||
|
SM2 sm2 = SmUtil.sm2(null, publicKey);
|
||||||
|
return sm2.encryptHex(data, StandardCharsets.UTF_8, KeyType.PublicKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sm2私钥解密
|
||||||
|
*
|
||||||
|
* @param data 待加密数据
|
||||||
|
* @param privateKey 私钥
|
||||||
|
* @return 解密后字符串
|
||||||
|
*/
|
||||||
|
public static String decryptBySm2(String data, String privateKey) {
|
||||||
|
if (StrUtil.isBlank(privateKey)) {
|
||||||
|
throw new IllegalArgumentException("SM2需要传入私钥进行解密");
|
||||||
|
}
|
||||||
|
SM2 sm2 = SmUtil.sm2(privateKey, null);
|
||||||
|
return sm2.decryptStr(data, KeyType.PrivateKey, StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 产生RSA加解密需要的公钥和私钥
|
||||||
|
*
|
||||||
|
* @return 公私钥Map
|
||||||
|
*/
|
||||||
|
public static Map<String, String> generateRsaKey() {
|
||||||
|
Map<String, String> keyMap = new HashMap<>(2);
|
||||||
|
RSA rsa = SecureUtil.rsa();
|
||||||
|
keyMap.put(PRIVATE_KEY, rsa.getPrivateKeyBase64());
|
||||||
|
keyMap.put(PUBLIC_KEY, rsa.getPublicKeyBase64());
|
||||||
|
return keyMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rsa公钥加密
|
||||||
|
*
|
||||||
|
* @param data 待加密数据
|
||||||
|
* @param publicKey 公钥
|
||||||
|
* @return 加密后字符串, 采用Base64编码
|
||||||
|
*/
|
||||||
|
public static String encryptByRsa(String data, String publicKey) {
|
||||||
|
if (StrUtil.isBlank(publicKey)) {
|
||||||
|
throw new IllegalArgumentException("RSA需要传入公钥进行加密");
|
||||||
|
}
|
||||||
|
RSA rsa = SecureUtil.rsa(null, publicKey);
|
||||||
|
return rsa.encryptBase64(data, StandardCharsets.UTF_8, KeyType.PublicKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rsa公钥加密
|
||||||
|
*
|
||||||
|
* @param data 待加密数据
|
||||||
|
* @param publicKey 公钥
|
||||||
|
* @return 加密后字符串, 采用Hex编码
|
||||||
|
*/
|
||||||
|
public static String encryptByRsaHex(String data, String publicKey) {
|
||||||
|
if (StrUtil.isBlank(publicKey)) {
|
||||||
|
throw new IllegalArgumentException("RSA需要传入公钥进行加密");
|
||||||
|
}
|
||||||
|
RSA rsa = SecureUtil.rsa(null, publicKey);
|
||||||
|
return rsa.encryptHex(data, StandardCharsets.UTF_8, KeyType.PublicKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rsa私钥解密
|
||||||
|
*
|
||||||
|
* @param data 待加密数据
|
||||||
|
* @param privateKey 私钥
|
||||||
|
* @return 解密后字符串
|
||||||
|
*/
|
||||||
|
public static String decryptByRsa(String data, String privateKey) {
|
||||||
|
if (StrUtil.isBlank(privateKey)) {
|
||||||
|
throw new IllegalArgumentException("RSA需要传入私钥进行解密");
|
||||||
|
}
|
||||||
|
RSA rsa = SecureUtil.rsa(privateKey, null);
|
||||||
|
return rsa.decryptStr(data, KeyType.PrivateKey, StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* md5加密
|
||||||
|
*
|
||||||
|
* @param data 待加密数据
|
||||||
|
* @return 加密后字符串, 采用Hex编码
|
||||||
|
*/
|
||||||
|
public static String encryptByMd5(String data) {
|
||||||
|
return SecureUtil.md5(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sha256加密
|
||||||
|
*
|
||||||
|
* @param data 待加密数据
|
||||||
|
* @return 加密后字符串, 采用Hex编码
|
||||||
|
*/
|
||||||
|
public static String encryptBySha256(String data) {
|
||||||
|
return SecureUtil.sha256(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sm3加密
|
||||||
|
*
|
||||||
|
* @param data 待加密数据
|
||||||
|
* @return 加密后字符串, 采用Hex编码
|
||||||
|
*/
|
||||||
|
public static String encryptBySm3(String data) {
|
||||||
|
return SmUtil.sm3(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
com.ruoyi.common.encrypt.config.EncryptorAutoConfiguration
|
||||||
|
com.ruoyi.common.encrypt.config.ApiDecryptAutoConfiguration
|
||||||
|
|
@ -30,6 +30,11 @@
|
|||||||
<artifactId>ruoyi-common-core</artifactId>
|
<artifactId>ruoyi-common-core</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.ruoyi</groupId>
|
||||||
|
<artifactId>ruoyi-common-encrypt</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.ruoyi</groupId>
|
<groupId>com.ruoyi</groupId>
|
||||||
<artifactId>ruoyi-common-excel</artifactId>
|
<artifactId>ruoyi-common-excel</artifactId>
|
||||||
|
Loading…
Reference in New Issue
Block a user