add 新增 请求加密传输 合并优化 !pr377
parent
10b5b0e82a
commit
af08632c37
@ -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,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,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 +1,3 @@
|
||||
org.dromara.common.encrypt.config.EncryptorAutoConfiguration
|
||||
org.dromara.common.encrypt.config.ApiDecryptAutoConfiguration
|
||||
|
||||
|
Loading…
Reference in New Issue