mirror of
				https://github.com/dromara/RuoYi-Vue-Plus.git
				synced 2025-11-04 16:23:42 +08:00 
			
		
		
		
	add 新增 请求加密传输 合并优化 !pr377
This commit is contained in:
		@@ -87,13 +87,6 @@
 | 
			
		||||
            <artifactId>JustAuth</artifactId>
 | 
			
		||||
        </dependency>
 | 
			
		||||
 | 
			
		||||
        <!-- 接口请求参数加密模块 -->
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>org.dromara</groupId>
 | 
			
		||||
            <artifactId>ruoyi-common-cryptapi</artifactId>
 | 
			
		||||
        </dependency>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        <!-- skywalking 整合 logback -->
 | 
			
		||||
<!--        <dependency>-->
 | 
			
		||||
<!--            <groupId>org.apache.skywalking</groupId>-->
 | 
			
		||||
 
 | 
			
		||||
@@ -176,10 +176,10 @@ mybatis-encryptor:
 | 
			
		||||
# api接口加密
 | 
			
		||||
api-decrypt:
 | 
			
		||||
  # 是否开启全局接口加密
 | 
			
		||||
  enable: false
 | 
			
		||||
  enabled: true
 | 
			
		||||
  # AES 加密头标识
 | 
			
		||||
  headerFlag: AES
 | 
			
		||||
  # 公私钥 非对称算法的公私钥 如:SM2,RSA
 | 
			
		||||
  headerFlag: encrypt-key
 | 
			
		||||
  # 公私钥 非对称算法的公私钥 如:SM2,RSA 使用者请自行更换
 | 
			
		||||
  publicKey: MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ==
 | 
			
		||||
  privateKey: MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqhHyZfSsYourNxaY7Nt+PrgrxkiA50efORdI5U5lsW79MmFnusUA355oaSXcLhu5xxB38SMSyP2KvuKNPuH3owIDAQABAkAfoiLyL+Z4lf4Myxk6xUDgLaWGximj20CUf+5BKKnlrK+Ed8gAkM0HqoTt2UZwA5E2MzS4EI2gjfQhz5X28uqxAiEA3wNFxfrCZlSZHb0gn2zDpWowcSxQAgiCstxGUoOqlW8CIQDDOerGKH5OmCJ4Z21v+F25WaHYPxCFMvwxpcw99EcvDQIgIdhDTIqD2jfYjPTY8Jj3EDGPbH2HHuffvflECt3Ek60CIQCFRlCkHpi7hthhYhovyloRYsM+IS9h/0BzlEAuO0ktMQIgSPT3aFAgJYwKpqRYKlLDVcflZFCKY7u3UP8iWi1Qw0Y=
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -33,7 +33,6 @@
 | 
			
		||||
        <module>ruoyi-common-encrypt</module>
 | 
			
		||||
        <module>ruoyi-common-tenant</module>
 | 
			
		||||
        <module>ruoyi-common-websocket</module>
 | 
			
		||||
        <module>ruoyi-common-cryptapi</module>
 | 
			
		||||
    </modules>
 | 
			
		||||
 | 
			
		||||
    <artifactId>ruoyi-common</artifactId>
 | 
			
		||||
 
 | 
			
		||||
@@ -172,12 +172,6 @@
 | 
			
		||||
                <version>${revision}</version>
 | 
			
		||||
            </dependency>
 | 
			
		||||
 | 
			
		||||
            <!-- 接口请求参数加密模块 -->
 | 
			
		||||
            <dependency>
 | 
			
		||||
                <groupId>org.dromara</groupId>
 | 
			
		||||
                <artifactId>ruoyi-common-cryptapi</artifactId>
 | 
			
		||||
                <version>${revision}</version>
 | 
			
		||||
            </dependency>
 | 
			
		||||
        </dependencies>
 | 
			
		||||
    </dependencyManagement>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,34 +0,0 @@
 | 
			
		||||
<?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>org.dromara</groupId>
 | 
			
		||||
        <artifactId>ruoyi-common</artifactId>
 | 
			
		||||
        <version>${revision}</version>
 | 
			
		||||
    </parent>
 | 
			
		||||
    <modelVersion>4.0.0</modelVersion>
 | 
			
		||||
 | 
			
		||||
    <artifactId>ruoyi-common-cryptapi</artifactId>
 | 
			
		||||
 | 
			
		||||
    <description>
 | 
			
		||||
        ruoyi-common-cryptapi 接口请求参数加密模块
 | 
			
		||||
    </description>
 | 
			
		||||
 | 
			
		||||
    <dependencies>
 | 
			
		||||
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>org.dromara</groupId>
 | 
			
		||||
            <artifactId>ruoyi-common-core</artifactId>
 | 
			
		||||
        </dependency>
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>cn.hutool</groupId>
 | 
			
		||||
            <artifactId>hutool-crypto</artifactId>
 | 
			
		||||
        </dependency>
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>org.springframework</groupId>
 | 
			
		||||
            <artifactId>spring-webmvc</artifactId>
 | 
			
		||||
        </dependency>
 | 
			
		||||
    </dependencies>
 | 
			
		||||
 | 
			
		||||
</project>
 | 
			
		||||
@@ -1,15 +0,0 @@
 | 
			
		||||
package org.dromara.cryptapi.annotation;
 | 
			
		||||
 | 
			
		||||
import java.lang.annotation.ElementType;
 | 
			
		||||
import java.lang.annotation.Retention;
 | 
			
		||||
import java.lang.annotation.RetentionPolicy;
 | 
			
		||||
import java.lang.annotation.Target;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 当标有当前注解的接口,接口穿参为加密字符串,进行解密后为dto对象, 不影响后续参数校验。
 | 
			
		||||
 * @author wdhcr
 | 
			
		||||
 */
 | 
			
		||||
@Retention(RetentionPolicy.RUNTIME)
 | 
			
		||||
@Target(ElementType.METHOD)
 | 
			
		||||
public @interface ApiDecrypt {
 | 
			
		||||
}
 | 
			
		||||
@@ -1,47 +0,0 @@
 | 
			
		||||
package org.dromara.cryptapi.config;
 | 
			
		||||
 | 
			
		||||
import cn.hutool.core.collection.CollectionUtil;
 | 
			
		||||
import jakarta.servlet.DispatcherType;
 | 
			
		||||
import lombok.RequiredArgsConstructor;
 | 
			
		||||
import org.dromara.cryptapi.filter.CryptoFilter;
 | 
			
		||||
import org.dromara.cryptapi.handler.DecryptUrlHandler;
 | 
			
		||||
import org.dromara.cryptapi.properties.ApiDecryptProperties;
 | 
			
		||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
 | 
			
		||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
 | 
			
		||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
 | 
			
		||||
import org.springframework.context.annotation.Bean;
 | 
			
		||||
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
@AutoConfiguration
 | 
			
		||||
@RequiredArgsConstructor
 | 
			
		||||
@EnableConfigurationProperties(ApiDecryptProperties.class)
 | 
			
		||||
public class ApiDecryptConfig {
 | 
			
		||||
 | 
			
		||||
    private final DecryptUrlHandler decryptUrlHandler;
 | 
			
		||||
 | 
			
		||||
    private final ApiDecryptProperties apiDecryptProperties;
 | 
			
		||||
 | 
			
		||||
    @Bean
 | 
			
		||||
    public FilterRegistrationBean<CryptoFilter> cryptoFilterRegistration() {
 | 
			
		||||
        FilterRegistrationBean<CryptoFilter> registration = new FilterRegistrationBean<>();
 | 
			
		||||
        registration.setDispatcherTypes(DispatcherType.REQUEST);
 | 
			
		||||
        registration.setFilter(new CryptoFilter());
 | 
			
		||||
        List<String> urls = decryptUrlHandler.getUrls();
 | 
			
		||||
        if (CollectionUtil.isNotEmpty(urls) || apiDecryptProperties.getEnable()) {
 | 
			
		||||
            registration.setEnabled(true);
 | 
			
		||||
            registration.addUrlPatterns(urls.toArray(new String[0]));
 | 
			
		||||
        } else {
 | 
			
		||||
            registration.setEnabled(false);
 | 
			
		||||
        }
 | 
			
		||||
        registration.setName("cryptoFilter");
 | 
			
		||||
        HashMap<String, String> param = new HashMap<>();
 | 
			
		||||
        param.put(CryptoFilter.CRYPTO_PUBLIC_KEY, apiDecryptProperties.getPublicKey());
 | 
			
		||||
        param.put(CryptoFilter.CRYPTO_PRIVATE_KEY, apiDecryptProperties.getPrivateKey());
 | 
			
		||||
        param.put(CryptoFilter.CRYPTO_HEADER_FLAG, apiDecryptProperties.getHeaderFlag());
 | 
			
		||||
        registration.setInitParameters(param);
 | 
			
		||||
        registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE);
 | 
			
		||||
        return registration;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,57 +0,0 @@
 | 
			
		||||
package org.dromara.cryptapi.core;
 | 
			
		||||
 | 
			
		||||
import cn.hutool.core.util.ArrayUtil;
 | 
			
		||||
import cn.hutool.core.util.StrUtil;
 | 
			
		||||
import cn.hutool.crypto.SecureUtil;
 | 
			
		||||
import cn.hutool.crypto.symmetric.AES;
 | 
			
		||||
import org.dromara.cryptapi.enums.EncodeType;
 | 
			
		||||
 | 
			
		||||
import java.nio.charset.StandardCharsets;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * AES算法实现
 | 
			
		||||
 *
 | 
			
		||||
 * @author 老马
 | 
			
		||||
 * @version 4.6.0
 | 
			
		||||
 */
 | 
			
		||||
public class AesEncryptor {
 | 
			
		||||
 | 
			
		||||
    private final AES aes;
 | 
			
		||||
 | 
			
		||||
    public AesEncryptor(EncryptContext context) {
 | 
			
		||||
        String password = context.getPassword();
 | 
			
		||||
        if (StrUtil.isBlank(password)) {
 | 
			
		||||
            throw new IllegalArgumentException("AES没有获得秘钥信息");
 | 
			
		||||
        }
 | 
			
		||||
        // aes算法的秘钥要求是16位、24位、32位
 | 
			
		||||
        int[] array = {16, 24, 32};
 | 
			
		||||
        if (!ArrayUtil.contains(array, password.length())) {
 | 
			
		||||
            throw new IllegalArgumentException("AES秘钥长度应该为16位、24位、32位,实际为" + password.length() + "位");
 | 
			
		||||
        }
 | 
			
		||||
        aes = SecureUtil.aes(context.getPassword().getBytes(StandardCharsets.UTF_8));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 加密
 | 
			
		||||
     *
 | 
			
		||||
     * @param value      待加密字符串
 | 
			
		||||
     * @param encodeType 加密后的编码格式
 | 
			
		||||
     */
 | 
			
		||||
    public String encrypt(String value, EncodeType encodeType) {
 | 
			
		||||
        if (encodeType == EncodeType.HEX) {
 | 
			
		||||
            return aes.encryptHex(value);
 | 
			
		||||
        } else {
 | 
			
		||||
            return aes.encryptBase64(value);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 解密
 | 
			
		||||
     *
 | 
			
		||||
     * @param value      待加密字符串
 | 
			
		||||
     */
 | 
			
		||||
    public String decrypt(String value) {
 | 
			
		||||
        return this.aes.decryptStr(value);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,35 +0,0 @@
 | 
			
		||||
package org.dromara.cryptapi.core;
 | 
			
		||||
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import org.dromara.cryptapi.enums.EncodeType;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 加密上下文 用于encryptor传递必要的参数。
 | 
			
		||||
 *
 | 
			
		||||
 * @author 老马
 | 
			
		||||
 * @version 4.6.0
 | 
			
		||||
 */
 | 
			
		||||
@Data
 | 
			
		||||
public class EncryptContext {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 安全秘钥
 | 
			
		||||
     */
 | 
			
		||||
    private String password;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 公钥
 | 
			
		||||
     */
 | 
			
		||||
    private String publicKey;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 私钥
 | 
			
		||||
     */
 | 
			
		||||
    private String privateKey;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 编码方式,base64/hex
 | 
			
		||||
     */
 | 
			
		||||
    private EncodeType encode;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,52 +0,0 @@
 | 
			
		||||
package org.dromara.cryptapi.core;
 | 
			
		||||
 | 
			
		||||
import cn.hutool.core.codec.Base64;
 | 
			
		||||
import cn.hutool.crypto.SecureUtil;
 | 
			
		||||
import cn.hutool.crypto.asymmetric.KeyType;
 | 
			
		||||
import cn.hutool.crypto.asymmetric.RSA;
 | 
			
		||||
import org.dromara.common.core.utils.StringUtils;
 | 
			
		||||
import org.dromara.cryptapi.enums.EncodeType;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * RSA算法实现
 | 
			
		||||
 *
 | 
			
		||||
 * @author 老马
 | 
			
		||||
 * @version 4.6.0
 | 
			
		||||
 */
 | 
			
		||||
public class RsaEncryptor {
 | 
			
		||||
 | 
			
		||||
    private final RSA rsa;
 | 
			
		||||
 | 
			
		||||
    public RsaEncryptor(EncryptContext context) {
 | 
			
		||||
        String privateKey = context.getPrivateKey();
 | 
			
		||||
        String publicKey = context.getPublicKey();
 | 
			
		||||
        if (StringUtils.isAnyEmpty(privateKey, publicKey)) {
 | 
			
		||||
            throw new IllegalArgumentException("RSA公私钥均需要提供,公钥加密,私钥解密。");
 | 
			
		||||
        }
 | 
			
		||||
        this.rsa = SecureUtil.rsa(Base64.decode(privateKey), Base64.decode(publicKey));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 加密
 | 
			
		||||
     *
 | 
			
		||||
     * @param value      待加密字符串
 | 
			
		||||
     * @param encodeType 加密后的编码格式
 | 
			
		||||
     */
 | 
			
		||||
    public String encrypt(String value, EncodeType encodeType) {
 | 
			
		||||
        if (encodeType == EncodeType.HEX) {
 | 
			
		||||
            return rsa.encryptHex(value, KeyType.PublicKey);
 | 
			
		||||
        } else {
 | 
			
		||||
            return rsa.encryptBase64(value, KeyType.PublicKey);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 解密
 | 
			
		||||
     *
 | 
			
		||||
     * @param value      待加密字符串
 | 
			
		||||
     */
 | 
			
		||||
    public String decrypt(String value) {
 | 
			
		||||
        return this.rsa.decryptStr(value, KeyType.PrivateKey);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,13 +0,0 @@
 | 
			
		||||
package org.dromara.cryptapi.enums;
 | 
			
		||||
 | 
			
		||||
public enum EncodeType {
 | 
			
		||||
    /**
 | 
			
		||||
     * base64编码
 | 
			
		||||
     */
 | 
			
		||||
    BASE64,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 16进制编码
 | 
			
		||||
     */
 | 
			
		||||
    HEX
 | 
			
		||||
}
 | 
			
		||||
@@ -1,54 +0,0 @@
 | 
			
		||||
package org.dromara.cryptapi.filter;
 | 
			
		||||
 | 
			
		||||
import jakarta.servlet.*;
 | 
			
		||||
import jakarta.servlet.http.HttpServletRequest;
 | 
			
		||||
import lombok.SneakyThrows;
 | 
			
		||||
import org.dromara.common.core.utils.StringUtils;
 | 
			
		||||
import org.dromara.cryptapi.core.EncryptContext;
 | 
			
		||||
import org.dromara.cryptapi.core.RsaEncryptor;
 | 
			
		||||
import org.springframework.http.HttpMethod;
 | 
			
		||||
import org.springframework.http.MediaType;
 | 
			
		||||
 | 
			
		||||
import java.util.Objects;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Crypto 过滤器
 | 
			
		||||
 *
 | 
			
		||||
 * @author wdhcr
 | 
			
		||||
 */
 | 
			
		||||
public class CryptoFilter implements Filter {
 | 
			
		||||
 | 
			
		||||
    public static final String CRYPTO_PUBLIC_KEY = "publicKey";
 | 
			
		||||
    public static final String CRYPTO_PRIVATE_KEY = "privateKey";
 | 
			
		||||
    public static final String CRYPTO_HEADER_FLAG = "headerFlag";
 | 
			
		||||
    private RsaEncryptor rsaEncryptor;
 | 
			
		||||
    private String headerFlag;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void init(FilterConfig filterConfig) {
 | 
			
		||||
        EncryptContext encryptContext = new EncryptContext();
 | 
			
		||||
        encryptContext.setPublicKey(filterConfig.getInitParameter(CryptoFilter.CRYPTO_PUBLIC_KEY));
 | 
			
		||||
        encryptContext.setPrivateKey(filterConfig.getInitParameter(CryptoFilter.CRYPTO_PRIVATE_KEY));
 | 
			
		||||
        headerFlag = filterConfig.getInitParameter(CryptoFilter.CRYPTO_HEADER_FLAG);
 | 
			
		||||
        rsaEncryptor = new RsaEncryptor(encryptContext);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @SneakyThrows
 | 
			
		||||
    @Override
 | 
			
		||||
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
 | 
			
		||||
        ServletRequest requestWrapper = null;
 | 
			
		||||
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
 | 
			
		||||
        if (StringUtils.startsWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE)
 | 
			
		||||
            && (HttpMethod.PUT.matches(httpServletRequest.getMethod()) || HttpMethod.POST.matches(httpServletRequest.getMethod()))) {
 | 
			
		||||
            requestWrapper = new DecryptRequestBodyWrapper(httpServletRequest, rsaEncryptor, headerFlag);
 | 
			
		||||
        }
 | 
			
		||||
        chain.doFilter(Objects.requireNonNullElse(requestWrapper, request), response);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void destroy() {
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,55 +0,0 @@
 | 
			
		||||
package org.dromara.cryptapi.handler;
 | 
			
		||||
 | 
			
		||||
import cn.hutool.core.collection.CollectionUtil;
 | 
			
		||||
import cn.hutool.core.util.ReUtil;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.RequiredArgsConstructor;
 | 
			
		||||
import org.dromara.cryptapi.annotation.ApiDecrypt;
 | 
			
		||||
import org.springframework.beans.factory.InitializingBean;
 | 
			
		||||
import org.springframework.stereotype.Component;
 | 
			
		||||
import org.springframework.web.bind.annotation.RequestMethod;
 | 
			
		||||
import org.springframework.web.method.HandlerMethod;
 | 
			
		||||
import org.springframework.web.servlet.mvc.condition.PathPatternsRequestCondition;
 | 
			
		||||
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
 | 
			
		||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
 | 
			
		||||
 | 
			
		||||
import java.util.*;
 | 
			
		||||
import java.util.regex.Pattern;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 获取需要解密的Url配置
 | 
			
		||||
 *
 | 
			
		||||
 * @author wdhcr
 | 
			
		||||
 */
 | 
			
		||||
@Data
 | 
			
		||||
@Component
 | 
			
		||||
@RequiredArgsConstructor
 | 
			
		||||
public class DecryptUrlHandler implements InitializingBean {
 | 
			
		||||
 | 
			
		||||
    private static final Pattern PATTERN = Pattern.compile("\\{(.*?)}");
 | 
			
		||||
 | 
			
		||||
    private List<String> urls = new ArrayList<>();
 | 
			
		||||
 | 
			
		||||
    private final RequestMappingHandlerMapping requestMappingHandlerMapping;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void afterPropertiesSet() {
 | 
			
		||||
        Set<String> set = new HashSet<>();
 | 
			
		||||
        Map<RequestMappingInfo, HandlerMethod> map = requestMappingHandlerMapping.getHandlerMethods();
 | 
			
		||||
        List<RequestMappingInfo> requestMappingInfos = map.entrySet().stream().filter(item -> {
 | 
			
		||||
            HandlerMethod method = item.getValue();
 | 
			
		||||
            ApiDecrypt decrypt = method.getMethodAnnotation(ApiDecrypt.class);
 | 
			
		||||
            // 标有解密注解的并且是post 或者put 请求的handler
 | 
			
		||||
            return decrypt != null && CollectionUtil.containsAny(item.getKey().getMethodsCondition().getMethods(), Arrays.asList(RequestMethod.PUT, RequestMethod.POST));
 | 
			
		||||
        }).map(Map.Entry::getKey).toList();
 | 
			
		||||
        requestMappingInfos.forEach(info -> {
 | 
			
		||||
            // 获取注解上边的 path 替代 path variable 为 *
 | 
			
		||||
            Optional.ofNullable(info.getPathPatternsCondition())
 | 
			
		||||
                .map(PathPatternsRequestCondition::getPatterns)
 | 
			
		||||
                .orElseGet(HashSet::new)
 | 
			
		||||
                .forEach(url -> set.add(ReUtil.replaceAll(url.getPatternString(), PATTERN, "*")));
 | 
			
		||||
        });
 | 
			
		||||
        urls.addAll(set);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
org.dromara.cryptapi.config.ApiDecryptConfig
 | 
			
		||||
@@ -0,0 +1,32 @@
 | 
			
		||||
package org.dromara.common.encrypt.config;
 | 
			
		||||
 | 
			
		||||
import jakarta.servlet.DispatcherType;
 | 
			
		||||
import org.dromara.common.encrypt.filter.CryptoFilter;
 | 
			
		||||
import org.dromara.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,48 @@
 | 
			
		||||
package org.dromara.common.encrypt.filter;
 | 
			
		||||
 | 
			
		||||
import jakarta.servlet.*;
 | 
			
		||||
import jakarta.servlet.http.HttpServletRequest;
 | 
			
		||||
import org.dromara.common.core.utils.StringUtils;
 | 
			
		||||
import org.dromara.common.encrypt.properties.ApiDecryptProperties;
 | 
			
		||||
import org.springframework.http.HttpMethod;
 | 
			
		||||
import org.springframework.http.MediaType;
 | 
			
		||||
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.util.Objects;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 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 {
 | 
			
		||||
        ServletRequest requestWrapper = null;
 | 
			
		||||
        HttpServletRequest servletRequest = (HttpServletRequest) request;
 | 
			
		||||
        // 是否为 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.getPublicKey(), properties.getPrivateKey(), properties.getHeaderFlag());
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        chain.doFilter(Objects.requireNonNullElse(requestWrapper, request), response);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void destroy() {
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,18 +1,12 @@
 | 
			
		||||
package org.dromara.cryptapi.filter;
 | 
			
		||||
package org.dromara.common.encrypt.filter;
 | 
			
		||||
 | 
			
		||||
import cn.hutool.core.codec.Base64;
 | 
			
		||||
import cn.hutool.core.io.IoUtil;
 | 
			
		||||
import jakarta.servlet.ReadListener;
 | 
			
		||||
import jakarta.servlet.ServletInputStream;
 | 
			
		||||
import jakarta.servlet.http.HttpServletRequest;
 | 
			
		||||
import jakarta.servlet.http.HttpServletRequestWrapper;
 | 
			
		||||
import org.dromara.common.core.constant.Constants;
 | 
			
		||||
import org.dromara.common.core.exception.base.BaseException;
 | 
			
		||||
import org.dromara.common.core.utils.StringUtils;
 | 
			
		||||
import org.dromara.cryptapi.core.AesEncryptor;
 | 
			
		||||
import org.dromara.cryptapi.core.EncryptContext;
 | 
			
		||||
import org.dromara.cryptapi.core.RsaEncryptor;
 | 
			
		||||
import org.dromara.cryptapi.enums.EncodeType;
 | 
			
		||||
import org.dromara.common.encrypt.utils.EncryptUtils;
 | 
			
		||||
import org.springframework.http.MediaType;
 | 
			
		||||
 | 
			
		||||
import java.io.BufferedReader;
 | 
			
		||||
@@ -30,21 +24,18 @@ public class DecryptRequestBodyWrapper extends HttpServletRequestWrapper {
 | 
			
		||||
 | 
			
		||||
    private final byte[] body;
 | 
			
		||||
 | 
			
		||||
    public DecryptRequestBodyWrapper(HttpServletRequest request, RsaEncryptor rsaEncryptor, String headerFlag) throws IOException {
 | 
			
		||||
    public DecryptRequestBodyWrapper(HttpServletRequest request, String publicKey, String privateKey, String headerFlag) throws IOException {
 | 
			
		||||
        super(request);
 | 
			
		||||
        String requestRsa = request.getHeader(headerFlag);
 | 
			
		||||
        if (StringUtils.isEmpty(requestRsa)) {
 | 
			
		||||
            throw new BaseException("加密AES的动态密码不能为空");
 | 
			
		||||
        }
 | 
			
		||||
        String decryptAes = new String(Base64.decode(rsaEncryptor.decrypt(requestRsa)));
 | 
			
		||||
        // 获取 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 = StringUtils.toEncodedString(readBytes, StandardCharsets.UTF_8);
 | 
			
		||||
        EncryptContext encryptContext = new EncryptContext();
 | 
			
		||||
        encryptContext.setPassword(decryptAes);
 | 
			
		||||
        encryptContext.setEncode(EncodeType.BASE64);
 | 
			
		||||
        AesEncryptor aesEncryptor = new AesEncryptor(encryptContext);
 | 
			
		||||
        String decryptBody = aesEncryptor.decrypt(requestBody);
 | 
			
		||||
        String requestBody = new String(readBytes, StandardCharsets.UTF_8);
 | 
			
		||||
        // 解密 body 采用 AES 加密
 | 
			
		||||
        String decryptBody = EncryptUtils.decryptByAes(requestBody, aesPassword);
 | 
			
		||||
        body = decryptBody.getBytes(StandardCharsets.UTF_8);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
package org.dromara.cryptapi.properties;
 | 
			
		||||
package org.dromara.common.encrypt.properties;
 | 
			
		||||
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import org.springframework.boot.context.properties.ConfigurationProperties;
 | 
			
		||||
@@ -14,7 +14,7 @@ public class ApiDecryptProperties {
 | 
			
		||||
    /**
 | 
			
		||||
     * 加密开关
 | 
			
		||||
     */
 | 
			
		||||
    private Boolean enable;
 | 
			
		||||
    private Boolean enabled;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 头部标识
 | 
			
		||||
@@ -1 +1,3 @@
 | 
			
		||||
org.dromara.common.encrypt.config.EncryptorAutoConfiguration
 | 
			
		||||
org.dromara.common.encrypt.config.ApiDecryptAutoConfiguration
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user