mirror of
https://github.com/continew-org/continew-starter.git
synced 2025-09-09 08:57:17 +08:00
refactor(encrypt): 拆分字段加密、API 加密模块
This commit is contained in:
@@ -160,8 +160,11 @@ continew-starter
|
|||||||
│ ├─ continew-starter-data-core(核心模块)
|
│ ├─ continew-starter-data-core(核心模块)
|
||||||
│ ├─ continew-starter-data-mp(MyBatis Plus)
|
│ ├─ continew-starter-data-mp(MyBatis Plus)
|
||||||
│ └─ continew-starter-data-mf(MyBatis Flex)
|
│ └─ continew-starter-data-mf(MyBatis Flex)
|
||||||
|
├─ continew-starter-encrypt(加密模块)
|
||||||
|
│ ├─ continew-starter-encrypt-core(核心模块)
|
||||||
|
│ ├─ continew-starter-encrypt-field(字段加密)
|
||||||
|
│ └─ continew-starter-encrypt-api(API 加密)
|
||||||
├─ continew-starter-security(安全模块)
|
├─ continew-starter-security(安全模块)
|
||||||
│ ├─ continew-starter-security-crypto(加密:字段加解密)
|
|
||||||
│ ├─ continew-starter-security-mask(脱敏:JSON 数据脱敏)
|
│ ├─ continew-starter-security-mask(脱敏:JSON 数据脱敏)
|
||||||
│ ├─ continew-starter-security-xss(XSS 过滤)
|
│ ├─ continew-starter-security-xss(XSS 过滤)
|
||||||
│ └─ continew-starter-security-sensitivewords(敏感词)
|
│ └─ continew-starter-security-sensitivewords(敏感词)
|
||||||
|
@@ -104,12 +104,25 @@
|
|||||||
<version>${revision}</version>
|
<version>${revision}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- 安全模块 - 加密 -->
|
<!-- 加密模块 - 字段加密 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>top.continew.starter</groupId>
|
<groupId>top.continew.starter</groupId>
|
||||||
<artifactId>continew-starter-security-crypto</artifactId>
|
<artifactId>continew-starter-encrypt-field</artifactId>
|
||||||
<version>${revision}</version>
|
<version>${revision}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- 加密模块 - API 加密 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>top.continew.starter</groupId>
|
||||||
|
<artifactId>continew-starter-encrypt-api</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
|
<!-- 加密模块 - 核心模块 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>top.continew.starter</groupId>
|
||||||
|
<artifactId>continew-starter-encrypt-core</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- 安全模块 - 脱敏 -->
|
<!-- 安全模块 - 脱敏 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>top.continew.starter</groupId>
|
<groupId>top.continew.starter</groupId>
|
||||||
|
@@ -32,9 +32,9 @@ public class OrderedConstants {
|
|||||||
public static final class Filter {
|
public static final class Filter {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* API加/密过滤器顺序
|
* API 加密过滤器顺序
|
||||||
*/
|
*/
|
||||||
public static final int API_CRYPTO_FILTER = Ordered.HIGHEST_PRECEDENCE;
|
public static final int API_ENCRYPT_FILTER = Ordered.HIGHEST_PRECEDENCE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 链路追踪过滤器顺序
|
* 链路追踪过滤器顺序
|
||||||
|
@@ -49,31 +49,41 @@ public class PropertiesConstants {
|
|||||||
*/
|
*/
|
||||||
public static final String WEB_RESPONSE = WEB + StringConstants.DOT + "response";
|
public static final String WEB_RESPONSE = WEB + StringConstants.DOT + "response";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加密配置
|
||||||
|
*/
|
||||||
|
public static final String ENCRYPT = CONTINEW_STARTER + StringConstants.DOT + "encrypt";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加密-密码编码器
|
||||||
|
*/
|
||||||
|
public static final String ENCRYPT_PASSWORD_ENCODER = ENCRYPT + StringConstants.DOT + "password-encoder";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加密-字段加密
|
||||||
|
*/
|
||||||
|
public static final String ENCRYPT_FIELD = ENCRYPT + StringConstants.DOT + "field";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加密-API 加密
|
||||||
|
*/
|
||||||
|
public static final String ENCRYPT_API = ENCRYPT + StringConstants.DOT + "api";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 安全配置
|
* 安全配置
|
||||||
*/
|
*/
|
||||||
public static final String SECURITY = CONTINEW_STARTER + StringConstants.DOT + "security";
|
public static final String SECURITY = CONTINEW_STARTER + StringConstants.DOT + "security";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 安全-数据加/解密配置
|
* 安全-XSS 配置
|
||||||
*/
|
*/
|
||||||
public static final String SECURITY_CRYPTO = SECURITY + StringConstants.DOT + "crypto";
|
public static final String SECURITY_XSS = SECURITY + StringConstants.DOT + "xss";
|
||||||
|
|
||||||
/**
|
|
||||||
* 安全-API加/解密配置
|
|
||||||
*/
|
|
||||||
public static final String SECURITY_API_CRYPTO = SECURITY + StringConstants.DOT + "api-crypto";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 安全-敏感词配置
|
* 安全-敏感词配置
|
||||||
*/
|
*/
|
||||||
public static final String SECURITY_SENSITIVE_WORDS = SECURITY + StringConstants.DOT + "sensitive-words";
|
public static final String SECURITY_SENSITIVE_WORDS = SECURITY + StringConstants.DOT + "sensitive-words";
|
||||||
|
|
||||||
/**
|
|
||||||
* 安全-XSS 配置
|
|
||||||
*/
|
|
||||||
public static final String SECURITY_XSS = SECURITY + StringConstants.DOT + "xss";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 限流配置
|
* 限流配置
|
||||||
*/
|
*/
|
||||||
|
@@ -20,12 +20,16 @@ import cn.hutool.core.text.CharSequenceUtil;
|
|||||||
import cn.hutool.core.util.ReflectUtil;
|
import cn.hutool.core.util.ReflectUtil;
|
||||||
import cn.hutool.extra.spring.SpringUtil;
|
import cn.hutool.extra.spring.SpringUtil;
|
||||||
import jakarta.servlet.ServletContext;
|
import jakarta.servlet.ServletContext;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.http.server.PathContainer;
|
import org.springframework.http.server.PathContainer;
|
||||||
import org.springframework.web.accept.ContentNegotiationManager;
|
import org.springframework.web.accept.ContentNegotiationManager;
|
||||||
|
import org.springframework.web.method.HandlerMethod;
|
||||||
|
import org.springframework.web.servlet.HandlerExecutionChain;
|
||||||
import org.springframework.web.servlet.HandlerMapping;
|
import org.springframework.web.servlet.HandlerMapping;
|
||||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
||||||
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
|
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
|
||||||
|
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
|
||||||
import org.springframework.web.util.UrlPathHelper;
|
import org.springframework.web.util.UrlPathHelper;
|
||||||
import org.springframework.web.util.pattern.PathPattern;
|
import org.springframework.web.util.pattern.PathPattern;
|
||||||
import org.springframework.web.util.pattern.PathPatternParser;
|
import org.springframework.web.util.pattern.PathPatternParser;
|
||||||
@@ -135,4 +139,31 @@ public class SpringWebUtils {
|
|||||||
.getUrlMap();
|
.getUrlMap();
|
||||||
ReflectUtil.<Void>invoke(resourceHandlerMapping, "registerHandlers", additionalUrlMap);
|
ReflectUtil.<Void>invoke(resourceHandlerMapping, "registerHandlers", additionalUrlMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取处理器方法
|
||||||
|
*
|
||||||
|
* @param request 请求
|
||||||
|
* @return 处理器方法
|
||||||
|
* @since 2.14.0
|
||||||
|
*/
|
||||||
|
public static HandlerMethod getHandlerMethod(HttpServletRequest request) {
|
||||||
|
try {
|
||||||
|
RequestMappingHandlerMapping handlerMapping = SpringUtil
|
||||||
|
.getBean("requestMappingHandlerMapping", RequestMappingHandlerMapping.class);
|
||||||
|
HandlerExecutionChain handlerExecutionChain = handlerMapping.getHandler(request);
|
||||||
|
// 检查是否存在处理链
|
||||||
|
if (handlerExecutionChain == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// 获取处理器
|
||||||
|
Object handler = handlerExecutionChain.getHandler();
|
||||||
|
if (handler instanceof HandlerMethod handlerMethod) {
|
||||||
|
return handlerMethod;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,25 @@
|
|||||||
|
<?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">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>top.continew.starter</groupId>
|
||||||
|
<artifactId>continew-starter-encrypt</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>continew-starter-encrypt-api</artifactId>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<name>${project.artifactId}</name>
|
||||||
|
<description>ContiNew Starter 加密模块 - API 加密</description>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<!-- 加密模块 - 核心模块 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>top.continew.starter</groupId>
|
||||||
|
<artifactId>continew-starter-encrypt-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
@@ -14,12 +14,12 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package top.continew.starter.security.crypto.annotation;
|
package top.continew.starter.encrypt.api.annotation;
|
||||||
|
|
||||||
import java.lang.annotation.*;
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* API加密注解
|
* API 加密注解
|
||||||
*
|
*
|
||||||
* @author lishuyan
|
* @author lishuyan
|
||||||
* @since 2.14.0
|
* @since 2.14.0
|
||||||
@@ -30,8 +30,7 @@ import java.lang.annotation.*;
|
|||||||
public @interface ApiEncrypt {
|
public @interface ApiEncrypt {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 默认API响应加密
|
* 是否加密响应
|
||||||
*/
|
*/
|
||||||
boolean response() default true;
|
boolean response() default true;
|
||||||
|
|
||||||
}
|
}
|
@@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package top.continew.starter.security.crypto.autoconfigure;
|
package top.continew.starter.encrypt.api.autoconfigure;
|
||||||
|
|
||||||
import jakarta.annotation.PostConstruct;
|
import jakarta.annotation.PostConstruct;
|
||||||
import jakarta.servlet.DispatcherType;
|
import jakarta.servlet.DispatcherType;
|
||||||
@@ -27,40 +27,38 @@ import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
|||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import top.continew.starter.core.constant.OrderedConstants;
|
import top.continew.starter.core.constant.OrderedConstants;
|
||||||
import top.continew.starter.core.constant.PropertiesConstants;
|
import top.continew.starter.core.constant.PropertiesConstants;
|
||||||
import top.continew.starter.security.crypto.filter.ApiCryptoFilter;
|
import top.continew.starter.core.constant.StringConstants;
|
||||||
|
import top.continew.starter.encrypt.api.filter.ApiEncryptFilter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* API加/解密自动配置
|
* API 加密自动配置
|
||||||
*
|
*
|
||||||
* @author lishuyan
|
* @author lishuyan
|
||||||
|
* @author Charles7c
|
||||||
* @since 2.14.0
|
* @since 2.14.0
|
||||||
*/
|
*/
|
||||||
@AutoConfiguration
|
@AutoConfiguration
|
||||||
@EnableConfigurationProperties(ApiCryptoProperties.class)
|
@EnableConfigurationProperties(ApiEncryptProperties.class)
|
||||||
@ConditionalOnProperty(prefix = PropertiesConstants.SECURITY_API_CRYPTO, name = PropertiesConstants.ENABLED, havingValue = "true", matchIfMissing = true)
|
@ConditionalOnProperty(prefix = PropertiesConstants.ENCRYPT_API, name = PropertiesConstants.ENABLED, havingValue = "true", matchIfMissing = true)
|
||||||
public class ApiCryptoAutoConfiguration {
|
public class ApiEncryptAutoConfiguration {
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(ApiCryptoAutoConfiguration.class);
|
private static final Logger log = LoggerFactory.getLogger(ApiEncryptAutoConfiguration.class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* API加/解密过滤器
|
* API 加密过滤器
|
||||||
*
|
|
||||||
* @param properties 配置
|
|
||||||
* @return 过滤器
|
|
||||||
*/
|
*/
|
||||||
@Bean
|
@Bean
|
||||||
public FilterRegistrationBean<ApiCryptoFilter> apiCryptoFilterRegistration(ApiCryptoProperties properties) {
|
public FilterRegistrationBean<ApiEncryptFilter> apiEncryptFilter(ApiEncryptProperties properties) {
|
||||||
FilterRegistrationBean<ApiCryptoFilter> registration = new FilterRegistrationBean<>();
|
FilterRegistrationBean<ApiEncryptFilter> registrationBean = new FilterRegistrationBean<>();
|
||||||
registration.setDispatcherTypes(DispatcherType.REQUEST);
|
registrationBean.setFilter(new ApiEncryptFilter(properties));
|
||||||
registration.setFilter(new ApiCryptoFilter(properties));
|
registrationBean.setOrder(OrderedConstants.Filter.API_ENCRYPT_FILTER);
|
||||||
registration.addUrlPatterns("/*");
|
registrationBean.addUrlPatterns(StringConstants.PATH_PATTERN_CURRENT_DIR);
|
||||||
registration.setName("apiCryptoFilter");
|
registrationBean.setDispatcherTypes(DispatcherType.REQUEST);
|
||||||
registration.setOrder(OrderedConstants.Filter.API_CRYPTO_FILTER);
|
return registrationBean;
|
||||||
return registration;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void postConstruct() {
|
public void postConstruct() {
|
||||||
log.debug("[ContiNew Starter] - Auto Configuration 'Security-API-Crypto' completed initialization.");
|
log.debug("[ContiNew Starter] - Auto Configuration 'Encrypt-API' completed initialization.");
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -14,19 +14,19 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package top.continew.starter.security.crypto.autoconfigure;
|
package top.continew.starter.encrypt.api.autoconfigure;
|
||||||
|
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
import top.continew.starter.core.constant.PropertiesConstants;
|
import top.continew.starter.core.constant.PropertiesConstants;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* API加/解密属性配置
|
* API 加密配置属性
|
||||||
*
|
*
|
||||||
* @author lishuyan
|
* @author lishuyan
|
||||||
* @since 2.14.0
|
* @since 2.14.0
|
||||||
*/
|
*/
|
||||||
@ConfigurationProperties(PropertiesConstants.SECURITY_API_CRYPTO)
|
@ConfigurationProperties(PropertiesConstants.ENCRYPT_API)
|
||||||
public class ApiCryptoProperties {
|
public class ApiEncryptProperties {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否启用
|
* 是否启用
|
||||||
@@ -36,7 +36,7 @@ public class ApiCryptoProperties {
|
|||||||
/**
|
/**
|
||||||
* 请求头中 AES 密钥 键名
|
* 请求头中 AES 密钥 键名
|
||||||
*/
|
*/
|
||||||
private String secretKeyHeader = "X-Api-Crypto";
|
private String secretKeyHeader = "X-Api-Encrypt";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 响应加密公钥
|
* 响应加密公钥
|
@@ -14,37 +14,33 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package top.continew.starter.security.crypto.filter;
|
package top.continew.starter.encrypt.api.filter;
|
||||||
|
|
||||||
import cn.hutool.core.text.CharSequenceUtil;
|
import cn.hutool.core.text.CharSequenceUtil;
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import cn.hutool.extra.spring.SpringUtil;
|
|
||||||
import jakarta.servlet.*;
|
import jakarta.servlet.*;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import org.springframework.http.HttpMethod;
|
import org.springframework.http.HttpMethod;
|
||||||
import org.springframework.web.method.HandlerMethod;
|
import top.continew.starter.core.util.SpringWebUtils;
|
||||||
import org.springframework.web.servlet.HandlerExecutionChain;
|
import top.continew.starter.encrypt.api.annotation.ApiEncrypt;
|
||||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
|
import top.continew.starter.encrypt.api.autoconfigure.ApiEncryptProperties;
|
||||||
import top.continew.starter.security.crypto.annotation.ApiEncrypt;
|
|
||||||
import top.continew.starter.security.crypto.autoconfigure.ApiCryptoProperties;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* API加/解密过滤器
|
* API 加密过滤器
|
||||||
*
|
*
|
||||||
* @author lishuyan
|
* @author lishuyan
|
||||||
|
* @author Charles7c
|
||||||
* @since 2.14.0
|
* @since 2.14.0
|
||||||
*/
|
*/
|
||||||
public class ApiCryptoFilter implements Filter {
|
public class ApiEncryptFilter implements Filter {
|
||||||
|
|
||||||
/**
|
private final ApiEncryptProperties properties;
|
||||||
* API加/密配置
|
|
||||||
*/
|
|
||||||
private final ApiCryptoProperties properties;
|
|
||||||
|
|
||||||
public ApiCryptoFilter(ApiCryptoProperties properties) {
|
public ApiEncryptFilter(ApiEncryptProperties properties) {
|
||||||
this.properties = properties;
|
this.properties = properties;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,10 +50,8 @@ public class ApiCryptoFilter implements Filter {
|
|||||||
FilterChain chain) throws IOException, ServletException {
|
FilterChain chain) throws IOException, ServletException {
|
||||||
HttpServletRequest request = (HttpServletRequest)servletRequest;
|
HttpServletRequest request = (HttpServletRequest)servletRequest;
|
||||||
HttpServletResponse response = (HttpServletResponse)servletResponse;
|
HttpServletResponse response = (HttpServletResponse)servletResponse;
|
||||||
// 获取API加密注解
|
// 是否加密响应
|
||||||
ApiEncrypt apiEncrypt = this.getApiEncryptAnnotation(request);
|
boolean isResponseEncrypt = this.isResponseEncrypt(request);
|
||||||
// 响应加密标识
|
|
||||||
boolean responseEncryptFlag = ObjectUtil.isNotNull(apiEncrypt) && apiEncrypt.response();
|
|
||||||
// 密钥标头
|
// 密钥标头
|
||||||
String secretKeyHeader = properties.getSecretKeyHeader();
|
String secretKeyHeader = properties.getSecretKeyHeader();
|
||||||
ServletRequest requestWrapper = null;
|
ServletRequest requestWrapper = null;
|
||||||
@@ -73,7 +67,7 @@ public class ApiCryptoFilter implements Filter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 响应加密,响应包装器替换响应体加密包装器
|
// 响应加密,响应包装器替换响应体加密包装器
|
||||||
if (responseEncryptFlag) {
|
if (isResponseEncrypt) {
|
||||||
responseBodyEncryptWrapper = new ResponseBodyEncryptWrapper(response);
|
responseBodyEncryptWrapper = new ResponseBodyEncryptWrapper(response);
|
||||||
responseWrapper = responseBodyEncryptWrapper;
|
responseWrapper = responseBodyEncryptWrapper;
|
||||||
}
|
}
|
||||||
@@ -81,7 +75,7 @@ public class ApiCryptoFilter implements Filter {
|
|||||||
chain.doFilter(ObjectUtil.defaultIfNull(requestWrapper, request), ObjectUtil
|
chain.doFilter(ObjectUtil.defaultIfNull(requestWrapper, request), ObjectUtil
|
||||||
.defaultIfNull(responseWrapper, response));
|
.defaultIfNull(responseWrapper, response));
|
||||||
// 响应加密,执行完成后,响应密文
|
// 响应加密,执行完成后,响应密文
|
||||||
if (responseEncryptFlag) {
|
if (isResponseEncrypt) {
|
||||||
servletResponse.reset();
|
servletResponse.reset();
|
||||||
// 获取密文
|
// 获取密文
|
||||||
String encryptContent = responseBodyEncryptWrapper.getEncryptContent(response, properties
|
String encryptContent = responseBodyEncryptWrapper.getEncryptContent(response, properties
|
||||||
@@ -92,30 +86,16 @@ public class ApiCryptoFilter implements Filter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取 ApiEncrypt 注解
|
* 是否加密响应
|
||||||
*
|
*
|
||||||
* @param request HTTP请求
|
* @param request 请求对象
|
||||||
* @return ApiEncrypt注解,如果未找到则返回null
|
* @return 是否加密响应
|
||||||
*/
|
*/
|
||||||
private ApiEncrypt getApiEncryptAnnotation(HttpServletRequest request) {
|
private boolean isResponseEncrypt(HttpServletRequest request) {
|
||||||
try {
|
// 获取 API 加密注解
|
||||||
RequestMappingHandlerMapping handlerMapping = SpringUtil
|
ApiEncrypt apiEncrypt = Optional.ofNullable(SpringWebUtils.getHandlerMethod(request))
|
||||||
.getBean("requestMappingHandlerMapping", RequestMappingHandlerMapping.class);
|
.map(h -> h.getMethodAnnotation(ApiEncrypt.class))
|
||||||
HandlerExecutionChain mappingHandler = handlerMapping.getHandler(request);
|
.orElse(null);
|
||||||
// 检查是否存在处理链
|
return apiEncrypt != null && apiEncrypt.response();
|
||||||
if (ObjectUtil.isNull(mappingHandler)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
// 获取处理器
|
|
||||||
Object handler = mappingHandler.getHandler();
|
|
||||||
// 检查是否为HandlerMethod类型
|
|
||||||
if (!(handler instanceof HandlerMethod handlerMethod)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
// 获取方法上的ApiEncrypt注解
|
|
||||||
return handlerMethod.getMethodAnnotation(ApiEncrypt.class);
|
|
||||||
} catch (Exception e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package top.continew.starter.security.crypto.filter;
|
package top.continew.starter.encrypt.api.filter;
|
||||||
|
|
||||||
import cn.hutool.core.io.IoUtil;
|
import cn.hutool.core.io.IoUtil;
|
||||||
import cn.hutool.core.util.CharsetUtil;
|
import cn.hutool.core.util.CharsetUtil;
|
||||||
@@ -23,7 +23,7 @@ import jakarta.servlet.ServletInputStream;
|
|||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletRequestWrapper;
|
import jakarta.servlet.http.HttpServletRequestWrapper;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import top.continew.starter.security.crypto.util.EncryptHelper;
|
import top.continew.starter.encrypt.util.EncryptUtils;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
@@ -63,14 +63,14 @@ public class RequestBodyDecryptWrapper extends HttpServletRequestWrapper {
|
|||||||
// 通过 请求头 获取 AES 密钥,密钥内容经过 RSA 加密
|
// 通过 请求头 获取 AES 密钥,密钥内容经过 RSA 加密
|
||||||
String secretKeyByRsa = request.getHeader(secretKeyHeader);
|
String secretKeyByRsa = request.getHeader(secretKeyHeader);
|
||||||
// 通过 RSA 解密,获取 AES 密钥,密钥内容经过 Base64 编码
|
// 通过 RSA 解密,获取 AES 密钥,密钥内容经过 Base64 编码
|
||||||
String secretKeyByBase64 = EncryptHelper.decryptByRsa(secretKeyByRsa, privateKey);
|
String secretKeyByBase64 = EncryptUtils.decryptByRsa(secretKeyByRsa, privateKey);
|
||||||
// 通过 Base64 解码,获取 AES 密钥
|
// 通过 Base64 解码,获取 AES 密钥
|
||||||
String aesSecretKey = EncryptHelper.decodeByBase64(secretKeyByBase64);
|
String aesSecretKey = EncryptUtils.decodeByBase64(secretKeyByBase64);
|
||||||
request.setCharacterEncoding(CharsetUtil.UTF_8);
|
request.setCharacterEncoding(CharsetUtil.UTF_8);
|
||||||
byte[] readBytes = IoUtil.readBytes(request.getInputStream(), false);
|
byte[] readBytes = IoUtil.readBytes(request.getInputStream(), false);
|
||||||
String requestBody = new String(readBytes, StandardCharsets.UTF_8);
|
String requestBody = new String(readBytes, StandardCharsets.UTF_8);
|
||||||
// 通过 AES 密钥,解密 请求体
|
// 通过 AES 密钥,解密 请求体
|
||||||
return EncryptHelper.decryptByAes(requestBody, aesSecretKey).getBytes(StandardCharsets.UTF_8);
|
return EncryptUtils.decryptByAes(requestBody, aesSecretKey).getBytes(StandardCharsets.UTF_8);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
@@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package top.continew.starter.security.crypto.filter;
|
package top.continew.starter.encrypt.api.filter;
|
||||||
|
|
||||||
import cn.hutool.core.util.CharsetUtil;
|
import cn.hutool.core.util.CharsetUtil;
|
||||||
import cn.hutool.core.util.RandomUtil;
|
import cn.hutool.core.util.RandomUtil;
|
||||||
@@ -24,7 +24,7 @@ import jakarta.servlet.http.HttpServletResponse;
|
|||||||
import jakarta.servlet.http.HttpServletResponseWrapper;
|
import jakarta.servlet.http.HttpServletResponseWrapper;
|
||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
import top.continew.starter.core.constant.StringConstants;
|
import top.continew.starter.core.constant.StringConstants;
|
||||||
import top.continew.starter.security.crypto.util.EncryptHelper;
|
import top.continew.starter.encrypt.util.EncryptUtils;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -94,9 +94,9 @@ public class ResponseBodyEncryptWrapper extends HttpServletResponseWrapper {
|
|||||||
// 生成 AES 密钥
|
// 生成 AES 密钥
|
||||||
String aesSecretKey = RandomUtil.randomString(32);
|
String aesSecretKey = RandomUtil.randomString(32);
|
||||||
// Base64 编码
|
// Base64 编码
|
||||||
String secretKeyByBase64 = EncryptHelper.encodeByBase64(aesSecretKey);
|
String secretKeyByBase64 = EncryptUtils.encodeByBase64(aesSecretKey);
|
||||||
// RSA 加密
|
// RSA 加密
|
||||||
String secretKeyByRsa = EncryptHelper.encryptByRsa(secretKeyByBase64, publicKey);
|
String secretKeyByRsa = EncryptUtils.encryptByRsa(secretKeyByBase64, publicKey);
|
||||||
// 设置响应头
|
// 设置响应头
|
||||||
response.addHeader(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, secretKeyHeader);
|
response.addHeader(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, secretKeyHeader);
|
||||||
response.setHeader(secretKeyHeader, secretKeyByRsa);
|
response.setHeader(secretKeyHeader, secretKeyByRsa);
|
||||||
@@ -104,7 +104,7 @@ public class ResponseBodyEncryptWrapper extends HttpServletResponseWrapper {
|
|||||||
response.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, StringConstants.ASTERISK);
|
response.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, StringConstants.ASTERISK);
|
||||||
response.setCharacterEncoding(CharsetUtil.UTF_8);
|
response.setCharacterEncoding(CharsetUtil.UTF_8);
|
||||||
// 通过 AES 密钥,对原始内容进行加密
|
// 通过 AES 密钥,对原始内容进行加密
|
||||||
return EncryptHelper.encryptByAes(this.getContent(), aesSecretKey);
|
return EncryptUtils.encryptByAes(this.getContent(), aesSecretKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
@@ -0,0 +1 @@
|
|||||||
|
top.continew.starter.encrypt.api.autoconfigure.ApiEncryptAutoConfiguration
|
@@ -5,17 +5,23 @@
|
|||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>top.continew.starter</groupId>
|
<groupId>top.continew.starter</groupId>
|
||||||
<artifactId>continew-starter-security</artifactId>
|
<artifactId>continew-starter-encrypt</artifactId>
|
||||||
<version>${revision}</version>
|
<version>${revision}</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>continew-starter-security-crypto</artifactId>
|
<artifactId>continew-starter-encrypt-core</artifactId>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<name>${project.artifactId}</name>
|
<name>${project.artifactId}</name>
|
||||||
<description>ContiNew Starter 安全模块 - 加密</description>
|
<description>ContiNew Starter 加密模块 - 核心模块</description>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
<!-- 核心模块 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>top.continew.starter</groupId>
|
||||||
|
<artifactId>continew-starter-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- Hutool 加密解密模块(封装 JDK 中加密解密算法) -->
|
<!-- Hutool 加密解密模块(封装 JDK 中加密解密算法) -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
@@ -27,11 +33,5 @@
|
|||||||
<groupId>org.springframework.security</groupId>
|
<groupId>org.springframework.security</groupId>
|
||||||
<artifactId>spring-security-crypto</artifactId>
|
<artifactId>spring-security-crypto</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- MyBatis Plus(MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,简化开发、提高效率) -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.baomidou</groupId>
|
|
||||||
<artifactId>mybatis-plus-extension</artifactId>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
@@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package top.continew.starter.security.crypto.autoconfigure;
|
package top.continew.starter.encrypt.autoconfigure;
|
||||||
|
|
||||||
import jakarta.annotation.PostConstruct;
|
import jakarta.annotation.PostConstruct;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -24,59 +24,29 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
|
|||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.PropertySource;
|
|
||||||
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
|
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
|
||||||
import org.springframework.security.crypto.password.DelegatingPasswordEncoder;
|
import org.springframework.security.crypto.password.DelegatingPasswordEncoder;
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
import top.continew.starter.core.constant.PropertiesConstants;
|
import top.continew.starter.core.constant.PropertiesConstants;
|
||||||
import top.continew.starter.core.util.GeneralPropertySourceFactory;
|
|
||||||
import top.continew.starter.core.util.validation.CheckUtils;
|
import top.continew.starter.core.util.validation.CheckUtils;
|
||||||
import top.continew.starter.security.crypto.enums.PasswordEncoderAlgorithm;
|
import top.continew.starter.encrypt.enums.PasswordEncoderAlgorithm;
|
||||||
import top.continew.starter.security.crypto.mybatis.MyBatisDecryptInterceptor;
|
import top.continew.starter.encrypt.util.PasswordEncoderUtil;
|
||||||
import top.continew.starter.security.crypto.mybatis.MyBatisEncryptInterceptor;
|
|
||||||
import top.continew.starter.security.crypto.util.EncryptHelper;
|
|
||||||
import top.continew.starter.security.crypto.util.PasswordEncoderUtil;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 加/解密自动配置
|
* 密码编码器自动配置
|
||||||
*
|
*
|
||||||
* @author Charles7c
|
* @author Charles7c
|
||||||
* @author lishuyan
|
* @since 2.14.0
|
||||||
* @since 1.4.0
|
|
||||||
*/
|
*/
|
||||||
@AutoConfiguration
|
@AutoConfiguration
|
||||||
@EnableConfigurationProperties(CryptoProperties.class)
|
@EnableConfigurationProperties(PasswordEncoderProperties.class)
|
||||||
@ConditionalOnProperty(prefix = PropertiesConstants.SECURITY_CRYPTO, name = PropertiesConstants.ENABLED, havingValue = "true", matchIfMissing = true)
|
@ConditionalOnProperty(prefix = PropertiesConstants.ENCRYPT_PASSWORD_ENCODER, name = PropertiesConstants.ENABLED, havingValue = "true")
|
||||||
@PropertySource(value = "classpath:default-crypto.yml", factory = GeneralPropertySourceFactory.class)
|
public class PasswordEncoderAutoConfiguration {
|
||||||
public class CryptoAutoConfiguration {
|
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(CryptoAutoConfiguration.class);
|
private static final Logger log = LoggerFactory.getLogger(PasswordEncoderAutoConfiguration.class);
|
||||||
private final CryptoProperties properties;
|
|
||||||
|
|
||||||
public CryptoAutoConfiguration(CryptoProperties properties) {
|
|
||||||
this.properties = properties;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* MyBatis 加密拦截器配置
|
|
||||||
*/
|
|
||||||
@Bean
|
|
||||||
@ConditionalOnMissingBean
|
|
||||||
public MyBatisEncryptInterceptor mybatisEncryptInterceptor() {
|
|
||||||
return new MyBatisEncryptInterceptor();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* MyBatis 解密拦截器配置
|
|
||||||
*/
|
|
||||||
@Bean
|
|
||||||
@ConditionalOnMissingBean(MyBatisDecryptInterceptor.class)
|
|
||||||
public MyBatisDecryptInterceptor mybatisDecryptInterceptor() {
|
|
||||||
return new MyBatisDecryptInterceptor();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 密码编码器配置
|
* 密码编码器配置
|
||||||
@@ -86,9 +56,7 @@ public class CryptoAutoConfiguration {
|
|||||||
*/
|
*/
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean
|
@ConditionalOnMissingBean
|
||||||
@ConditionalOnProperty(prefix = PropertiesConstants.SECURITY_CRYPTO + ".password-encoder", name = PropertiesConstants.ENABLED, havingValue = "true")
|
public PasswordEncoder passwordEncoder(PasswordEncoderProperties properties) {
|
||||||
public PasswordEncoder passwordEncoder() {
|
|
||||||
PasswordEncoderProperties passwordEncoderProperties = properties.getPasswordEncoder();
|
|
||||||
Map<String, PasswordEncoder> encoders = new HashMap<>();
|
Map<String, PasswordEncoder> encoders = new HashMap<>();
|
||||||
encoders.put(PasswordEncoderAlgorithm.BCRYPT.name().toLowerCase(), PasswordEncoderUtil
|
encoders.put(PasswordEncoderAlgorithm.BCRYPT.name().toLowerCase(), PasswordEncoderUtil
|
||||||
.getEncoder(PasswordEncoderAlgorithm.BCRYPT));
|
.getEncoder(PasswordEncoderAlgorithm.BCRYPT));
|
||||||
@@ -98,14 +66,13 @@ public class CryptoAutoConfiguration {
|
|||||||
.getEncoder(PasswordEncoderAlgorithm.PBKDF2));
|
.getEncoder(PasswordEncoderAlgorithm.PBKDF2));
|
||||||
encoders.put(PasswordEncoderAlgorithm.ARGON2.name().toLowerCase(), PasswordEncoderUtil
|
encoders.put(PasswordEncoderAlgorithm.ARGON2.name().toLowerCase(), PasswordEncoderUtil
|
||||||
.getEncoder(PasswordEncoderAlgorithm.ARGON2));
|
.getEncoder(PasswordEncoderAlgorithm.ARGON2));
|
||||||
PasswordEncoderAlgorithm algorithm = passwordEncoderProperties.getAlgorithm();
|
PasswordEncoderAlgorithm algorithm = properties.getAlgorithm();
|
||||||
CheckUtils.throwIf(PasswordEncoderUtil.getEncoder(algorithm) == null, "不支持的加密算法: {}", algorithm);
|
CheckUtils.throwIf(PasswordEncoderUtil.getEncoder(algorithm) == null, "不支持的加密算法: {}", algorithm);
|
||||||
return new DelegatingPasswordEncoder(algorithm.name().toLowerCase(), encoders);
|
return new DelegatingPasswordEncoder(algorithm.name().toLowerCase(), encoders);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void postConstruct() {
|
public void postConstruct() {
|
||||||
EncryptHelper.init(properties);
|
log.debug("[ContiNew Starter] - Auto Configuration 'Encrypt-Password Encoder' completed initialization.");
|
||||||
log.debug("[ContiNew Starter] - Auto Configuration 'Security-Crypto' completed initialization.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -14,14 +14,15 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package top.continew.starter.security.crypto.autoconfigure;
|
package top.continew.starter.encrypt.autoconfigure;
|
||||||
|
|
||||||
import top.continew.starter.security.crypto.enums.PasswordEncoderAlgorithm;
|
import top.continew.starter.encrypt.enums.PasswordEncoderAlgorithm;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 密码编码器配置属性
|
* 密码编码器配置属性
|
||||||
*
|
*
|
||||||
* @author Jasmine
|
* @author Jasmine
|
||||||
|
* @author Charles7c
|
||||||
* @since 1.3.0
|
* @since 1.3.0
|
||||||
*/
|
*/
|
||||||
public class PasswordEncoderProperties {
|
public class PasswordEncoderProperties {
|
||||||
@@ -29,18 +30,18 @@ public class PasswordEncoderProperties {
|
|||||||
/**
|
/**
|
||||||
* 是否启用
|
* 是否启用
|
||||||
*/
|
*/
|
||||||
private boolean enabled = true;
|
private Boolean enabled;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 默认启用的编码器算法(默认:BCrypt 加密算法)
|
* 默认启用的编码器算法(默认:BCrypt 加密算法)
|
||||||
*/
|
*/
|
||||||
private PasswordEncoderAlgorithm algorithm = PasswordEncoderAlgorithm.BCRYPT;
|
private PasswordEncoderAlgorithm algorithm = PasswordEncoderAlgorithm.BCRYPT;
|
||||||
|
|
||||||
public boolean isEnabled() {
|
public Boolean getEnabled() {
|
||||||
return enabled;
|
return enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setEnabled(boolean enabled) {
|
public void setEnabled(Boolean enabled) {
|
||||||
this.enabled = enabled;
|
this.enabled = enabled;
|
||||||
}
|
}
|
||||||
|
|
@@ -14,10 +14,10 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package top.continew.starter.security.crypto.autoconfigure;
|
package top.continew.starter.encrypt.context;
|
||||||
|
|
||||||
import top.continew.starter.security.crypto.encryptor.IEncryptor;
|
import top.continew.starter.encrypt.encryptor.IEncryptor;
|
||||||
import top.continew.starter.security.crypto.enums.Algorithm;
|
import top.continew.starter.encrypt.enums.Algorithm;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
@@ -14,9 +14,9 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package top.continew.starter.security.crypto.encryptor;
|
package top.continew.starter.encrypt.encryptor;
|
||||||
|
|
||||||
import top.continew.starter.security.crypto.autoconfigure.CryptoContext;
|
import top.continew.starter.encrypt.context.CryptoContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 加密器基类
|
* 加密器基类
|
@@ -14,20 +14,20 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package top.continew.starter.security.crypto.encryptor;
|
package top.continew.starter.encrypt.encryptor;
|
||||||
|
|
||||||
import cn.hutool.core.text.CharSequenceUtil;
|
import cn.hutool.core.text.CharSequenceUtil;
|
||||||
import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
|
import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
|
||||||
import cn.hutool.crypto.symmetric.SymmetricCrypto;
|
import cn.hutool.crypto.symmetric.SymmetricCrypto;
|
||||||
import top.continew.starter.core.constant.StringConstants;
|
import top.continew.starter.core.constant.StringConstants;
|
||||||
import top.continew.starter.security.crypto.autoconfigure.CryptoContext;
|
import top.continew.starter.encrypt.context.CryptoContext;
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 对称加/解密处理器
|
* 对称加密器
|
||||||
*
|
*
|
||||||
* @author Charles7c
|
* @author Charles7c
|
||||||
* @author lishuyan
|
* @author lishuyan
|
@@ -14,13 +14,13 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package top.continew.starter.security.crypto.encryptor;
|
package top.continew.starter.encrypt.encryptor;
|
||||||
|
|
||||||
import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
|
import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
|
||||||
import top.continew.starter.security.crypto.autoconfigure.CryptoContext;
|
import top.continew.starter.encrypt.context.CryptoContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AES(Advanced Encryption Standard) 加/解密处理器
|
* AES(Advanced Encryption Standard) 加密器
|
||||||
* <p>
|
* <p>
|
||||||
* 美国国家标准与技术研究院(NIST)采纳的对称加密算法标准,提供128位、192位和256位三种密钥长度,以高效和安全性著称。
|
* 美国国家标准与技术研究院(NIST)采纳的对称加密算法标准,提供128位、192位和256位三种密钥长度,以高效和安全性著称。
|
||||||
* </p>
|
* </p>
|
@@ -14,12 +14,12 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package top.continew.starter.security.crypto.encryptor;
|
package top.continew.starter.encrypt.encryptor;
|
||||||
|
|
||||||
import cn.hutool.core.codec.Base64;
|
import cn.hutool.core.codec.Base64;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base64 加/解密处理器
|
* Base64 加密器
|
||||||
* <p>
|
* <p>
|
||||||
* 一种用于编码二进制数据到文本格式的算法,常用于邮件附件、网页传输等场合,但它不是一种加密算法,只提供数据的编码和解码,不保证数据的安全性。
|
* 一种用于编码二进制数据到文本格式的算法,常用于邮件附件、网页传输等场合,但它不是一种加密算法,只提供数据的编码和解码,不保证数据的安全性。
|
||||||
* </p>
|
* </p>
|
@@ -14,13 +14,13 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package top.continew.starter.security.crypto.encryptor;
|
package top.continew.starter.encrypt.encryptor;
|
||||||
|
|
||||||
import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
|
import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
|
||||||
import top.continew.starter.security.crypto.autoconfigure.CryptoContext;
|
import top.continew.starter.encrypt.context.CryptoContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DES(Data Encryption Standard) 加/解密处理器
|
* DES(Data Encryption Standard) 加密器
|
||||||
* <p>
|
* <p>
|
||||||
* 一种对称加密算法,使用相同的密钥进行加密和解密。DES 使用 56 位密钥(实际上有 64 位,但有 8 位用于奇偶校验)和一系列置换和替换操作来加密数据。
|
* 一种对称加密算法,使用相同的密钥进行加密和解密。DES 使用 56 位密钥(实际上有 64 位,但有 8 位用于奇偶校验)和一系列置换和替换操作来加密数据。
|
||||||
* </p>
|
* </p>
|
@@ -14,10 +14,10 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package top.continew.starter.security.crypto.encryptor;
|
package top.continew.starter.encrypt.encryptor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 加/解密接口
|
* 加密器接口
|
||||||
*
|
*
|
||||||
* @author Charles7c
|
* @author Charles7c
|
||||||
* @author lishuyan
|
* @author lishuyan
|
@@ -14,16 +14,16 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package top.continew.starter.security.crypto.encryptor;
|
package top.continew.starter.encrypt.encryptor;
|
||||||
|
|
||||||
import cn.hutool.extra.spring.SpringUtil;
|
import cn.hutool.extra.spring.SpringUtil;
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
import top.continew.starter.security.crypto.autoconfigure.CryptoContext;
|
import top.continew.starter.core.util.SpringUtils;
|
||||||
import top.continew.starter.security.crypto.autoconfigure.CryptoProperties;
|
import top.continew.starter.encrypt.autoconfigure.PasswordEncoderProperties;
|
||||||
import top.continew.starter.security.crypto.autoconfigure.PasswordEncoderProperties;
|
import top.continew.starter.encrypt.context.CryptoContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 密码编码器加/解密处理器
|
* 密码编码器加密器
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* 使用前必须注入 {@link PasswordEncoder},此加密方式不可逆,适合于密码场景
|
* 使用前必须注入 {@link PasswordEncoder},此加密方式不可逆,适合于密码场景
|
||||||
@@ -37,8 +37,7 @@ import top.continew.starter.security.crypto.autoconfigure.PasswordEncoderPropert
|
|||||||
*/
|
*/
|
||||||
public class PasswordEncoderEncryptor extends AbstractEncryptor {
|
public class PasswordEncoderEncryptor extends AbstractEncryptor {
|
||||||
|
|
||||||
private final PasswordEncoder passwordEncoder = SpringUtil.getBean(PasswordEncoder.class);
|
private final PasswordEncoderProperties properties = SpringUtils.getBean(PasswordEncoderProperties.class, true);
|
||||||
private final CryptoProperties properties = SpringUtil.getBean(CryptoProperties.class);
|
|
||||||
|
|
||||||
public PasswordEncoderEncryptor(CryptoContext context) {
|
public PasswordEncoderEncryptor(CryptoContext context) {
|
||||||
super(context);
|
super(context);
|
||||||
@@ -47,10 +46,10 @@ public class PasswordEncoderEncryptor extends AbstractEncryptor {
|
|||||||
@Override
|
@Override
|
||||||
public String encrypt(String plaintext) {
|
public String encrypt(String plaintext) {
|
||||||
// 如果已经是加密格式,直接返回
|
// 如果已经是加密格式,直接返回
|
||||||
if (properties.getPasswordEncoder().getAlgorithm().getPattern().matcher(plaintext).matches()) {
|
if (properties == null || properties.getAlgorithm().getPattern().matcher(plaintext).matches()) {
|
||||||
return plaintext;
|
return plaintext;
|
||||||
}
|
}
|
||||||
return passwordEncoder.encode(plaintext);
|
return SpringUtil.getBean(PasswordEncoder.class).encode(plaintext);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
@@ -14,13 +14,13 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package top.continew.starter.security.crypto.encryptor;
|
package top.continew.starter.encrypt.encryptor;
|
||||||
|
|
||||||
import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
|
import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
|
||||||
import top.continew.starter.security.crypto.autoconfigure.CryptoContext;
|
import top.continew.starter.encrypt.context.CryptoContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PBEWithMD5AndDES(Password Based Encryption With MD5 And DES) 加/解密处理器
|
* PBEWithMD5AndDES(Password Based Encryption With MD5 And DES) 加密器
|
||||||
* <p>
|
* <p>
|
||||||
* 混合加密算法,结合了 MD5 散列算法和 DES(Data Encryption Standard)加密算法
|
* 混合加密算法,结合了 MD5 散列算法和 DES(Data Encryption Standard)加密算法
|
||||||
* </p>
|
* </p>
|
@@ -14,15 +14,15 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package top.continew.starter.security.crypto.encryptor;
|
package top.continew.starter.encrypt.encryptor;
|
||||||
|
|
||||||
import cn.hutool.core.codec.Base64;
|
import cn.hutool.core.codec.Base64;
|
||||||
import cn.hutool.crypto.SecureUtil;
|
import cn.hutool.crypto.SecureUtil;
|
||||||
import cn.hutool.crypto.asymmetric.KeyType;
|
import cn.hutool.crypto.asymmetric.KeyType;
|
||||||
import top.continew.starter.security.crypto.autoconfigure.CryptoContext;
|
import top.continew.starter.encrypt.context.CryptoContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* RSA 加/解密处理器
|
* RSA 加密器
|
||||||
* <p>
|
* <p>
|
||||||
* 非对称加密算法,由罗纳德·李维斯特(Ron Rivest)、阿迪·沙米尔(Adi Shamir)和伦纳德·阿德曼(Leonard Adleman)于1977年提出,安全性基于大数因子分解问题的困难性。
|
* 非对称加密算法,由罗纳德·李维斯特(Ron Rivest)、阿迪·沙米尔(Adi Shamir)和伦纳德·阿德曼(Leonard Adleman)于1977年提出,安全性基于大数因子分解问题的困难性。
|
||||||
* </p>
|
* </p>
|
@@ -14,12 +14,12 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package top.continew.starter.security.crypto.enums;
|
package top.continew.starter.encrypt.enums;
|
||||||
|
|
||||||
import top.continew.starter.security.crypto.encryptor.*;
|
import top.continew.starter.encrypt.encryptor.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 加密/解密算法枚举
|
* 加密算法枚举
|
||||||
*
|
*
|
||||||
* @author Charles7c
|
* @author Charles7c
|
||||||
* @author lishuyan
|
* @author lishuyan
|
@@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package top.continew.starter.security.crypto.enums;
|
package top.continew.starter.encrypt.enums;
|
||||||
|
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
@@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package top.continew.starter.security.crypto.exception;
|
package top.continew.starter.encrypt.exception;
|
||||||
|
|
||||||
import top.continew.starter.core.exception.BaseException;
|
import top.continew.starter.core.exception.BaseException;
|
||||||
|
|
@@ -0,0 +1,132 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.continew.starter.encrypt.util;
|
||||||
|
|
||||||
|
import cn.hutool.core.codec.Base64;
|
||||||
|
import cn.hutool.core.text.CharSequenceUtil;
|
||||||
|
import cn.hutool.core.util.ArrayUtil;
|
||||||
|
import cn.hutool.crypto.SecureUtil;
|
||||||
|
import cn.hutool.crypto.asymmetric.KeyType;
|
||||||
|
import cn.hutool.crypto.asymmetric.RSA;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加密工具类
|
||||||
|
*
|
||||||
|
* @author Charles7c
|
||||||
|
* @since 2.14.0
|
||||||
|
*/
|
||||||
|
public class EncryptUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base64 编码
|
||||||
|
*
|
||||||
|
* @param data 待编码数据
|
||||||
|
* @return 编码后字符串
|
||||||
|
* @author lishuyan
|
||||||
|
*/
|
||||||
|
public static String encodeByBase64(String data) {
|
||||||
|
return Base64.encode(data, StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base64 解码
|
||||||
|
*
|
||||||
|
* @param data 待解码数据
|
||||||
|
* @return 解码后字符串
|
||||||
|
* @author lishuyan
|
||||||
|
*/
|
||||||
|
public static String decodeByBase64(String data) {
|
||||||
|
return Base64.decodeStr(data, StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AES 加密
|
||||||
|
*
|
||||||
|
* @param data 待加密数据
|
||||||
|
* @param password 秘钥字符串
|
||||||
|
* @return 加密后字符串, 采用 Base64 编码
|
||||||
|
* @author lishuyan
|
||||||
|
*/
|
||||||
|
public static String encryptByAes(String data, String password) {
|
||||||
|
if (CharSequenceUtil.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 解密后字符串
|
||||||
|
* @author lishuyan
|
||||||
|
*/
|
||||||
|
public static String decryptByAes(String data, String password) {
|
||||||
|
if (CharSequenceUtil.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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RSA 公钥加密
|
||||||
|
*
|
||||||
|
* @param data 待加密数据
|
||||||
|
* @param publicKey 公钥
|
||||||
|
* @return 加密后字符串, 采用Base64编码
|
||||||
|
* @author lishuyan
|
||||||
|
*/
|
||||||
|
public static String encryptByRsa(String data, String publicKey) {
|
||||||
|
if (CharSequenceUtil.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 privateKey 私钥
|
||||||
|
* @return 解密后字符串
|
||||||
|
* @author lishuyan
|
||||||
|
*/
|
||||||
|
public static String decryptByRsa(String data, String privateKey) {
|
||||||
|
if (CharSequenceUtil.isBlank(privateKey)) {
|
||||||
|
throw new IllegalArgumentException("RSA需要传入私钥进行解密");
|
||||||
|
}
|
||||||
|
RSA rsa = SecureUtil.rsa(privateKey, null);
|
||||||
|
return rsa.decryptStr(data, KeyType.PrivateKey, StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
private EncryptUtils() {
|
||||||
|
}
|
||||||
|
}
|
@@ -14,15 +14,15 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package top.continew.starter.security.crypto.util;
|
package top.continew.starter.encrypt.util;
|
||||||
|
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
import org.springframework.security.crypto.argon2.Argon2PasswordEncoder;
|
import org.springframework.security.crypto.argon2.Argon2PasswordEncoder;
|
||||||
import org.springframework.security.crypto.password.Pbkdf2PasswordEncoder;
|
import org.springframework.security.crypto.password.Pbkdf2PasswordEncoder;
|
||||||
import org.springframework.security.crypto.scrypt.SCryptPasswordEncoder;
|
import org.springframework.security.crypto.scrypt.SCryptPasswordEncoder;
|
||||||
import top.continew.starter.security.crypto.enums.PasswordEncoderAlgorithm;
|
import top.continew.starter.encrypt.enums.PasswordEncoderAlgorithm;
|
||||||
import top.continew.starter.security.crypto.exception.PasswordEncodeException;
|
import top.continew.starter.encrypt.exception.PasswordEncodeException;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
@@ -0,0 +1 @@
|
|||||||
|
top.continew.starter.encrypt.autoconfigure.PasswordEncoderAutoConfiguration
|
@@ -0,0 +1,31 @@
|
|||||||
|
<?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">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>top.continew.starter</groupId>
|
||||||
|
<artifactId>continew-starter-encrypt</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>continew-starter-encrypt-field</artifactId>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<name>${project.artifactId}</name>
|
||||||
|
<description>ContiNew Starter 加密模块 - 字段加密</description>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<!-- 加密模块 - 核心模块 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>top.continew.starter</groupId>
|
||||||
|
<artifactId>continew-starter-encrypt-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- MyBatis Plus(MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,简化开发、提高效率) -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.baomidou</groupId>
|
||||||
|
<artifactId>mybatis-plus-extension</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
@@ -14,10 +14,10 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package top.continew.starter.security.crypto.annotation;
|
package top.continew.starter.encrypt.field.annotation;
|
||||||
|
|
||||||
import top.continew.starter.security.crypto.encryptor.IEncryptor;
|
import top.continew.starter.encrypt.encryptor.IEncryptor;
|
||||||
import top.continew.starter.security.crypto.enums.Algorithm;
|
import top.continew.starter.encrypt.enums.Algorithm;
|
||||||
|
|
||||||
import java.lang.annotation.ElementType;
|
import java.lang.annotation.ElementType;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
@@ -25,7 +25,7 @@ import java.lang.annotation.RetentionPolicy;
|
|||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 字段加/解密注解
|
* 字段加密注解
|
||||||
*
|
*
|
||||||
* @author Charles7c
|
* @author Charles7c
|
||||||
* @author lishuyan
|
* @author lishuyan
|
||||||
@@ -36,14 +36,14 @@ import java.lang.annotation.Target;
|
|||||||
public @interface FieldEncrypt {
|
public @interface FieldEncrypt {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 加密/解密算法
|
* 加密算法
|
||||||
*/
|
*/
|
||||||
Algorithm value() default Algorithm.DEFAULT;
|
Algorithm value() default Algorithm.DEFAULT;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 加密/解密处理器
|
* 加密处理器
|
||||||
* <p>
|
* <p>
|
||||||
* 优先级高于加密/解密算法
|
* 优先级高于加密算法
|
||||||
* </p>
|
* </p>
|
||||||
*/
|
*/
|
||||||
Class<? extends IEncryptor> encryptor() default IEncryptor.class;
|
Class<? extends IEncryptor> encryptor() default IEncryptor.class;
|
@@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.continew.starter.encrypt.field.autoconfigure;
|
||||||
|
|
||||||
|
import jakarta.annotation.PostConstruct;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import top.continew.starter.core.constant.PropertiesConstants;
|
||||||
|
import top.continew.starter.encrypt.field.interceptor.MyBatisDecryptInterceptor;
|
||||||
|
import top.continew.starter.encrypt.field.interceptor.MyBatisEncryptInterceptor;
|
||||||
|
import top.continew.starter.encrypt.field.util.EncryptHelper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字段加密自动配置
|
||||||
|
*
|
||||||
|
* @author Charles7c
|
||||||
|
* @author lishuyan
|
||||||
|
* @since 1.4.0
|
||||||
|
*/
|
||||||
|
@AutoConfiguration
|
||||||
|
@EnableConfigurationProperties(FieldEncryptProperties.class)
|
||||||
|
@ConditionalOnProperty(prefix = PropertiesConstants.ENCRYPT_FIELD, name = PropertiesConstants.ENABLED, havingValue = "true", matchIfMissing = true)
|
||||||
|
public class FieldEncryptAutoConfiguration {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(FieldEncryptAutoConfiguration.class);
|
||||||
|
private final FieldEncryptProperties properties;
|
||||||
|
|
||||||
|
public FieldEncryptAutoConfiguration(FieldEncryptProperties properties) {
|
||||||
|
this.properties = properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MyBatis 加密拦截器配置
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnMissingBean
|
||||||
|
public MyBatisEncryptInterceptor mybatisEncryptInterceptor() {
|
||||||
|
return new MyBatisEncryptInterceptor();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MyBatis 解密拦截器配置
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnMissingBean(MyBatisDecryptInterceptor.class)
|
||||||
|
public MyBatisDecryptInterceptor mybatisDecryptInterceptor() {
|
||||||
|
return new MyBatisDecryptInterceptor();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void postConstruct() {
|
||||||
|
EncryptHelper.init(properties);
|
||||||
|
log.debug("[ContiNew Starter] - Auto Configuration 'Encrypt-Field' completed initialization.");
|
||||||
|
}
|
||||||
|
}
|
@@ -14,27 +14,28 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package top.continew.starter.security.crypto.autoconfigure;
|
package top.continew.starter.encrypt.field.autoconfigure;
|
||||||
|
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
import org.springframework.boot.context.properties.NestedConfigurationProperty;
|
import org.springframework.boot.context.properties.NestedConfigurationProperty;
|
||||||
import top.continew.starter.core.constant.PropertiesConstants;
|
import top.continew.starter.core.constant.PropertiesConstants;
|
||||||
import top.continew.starter.security.crypto.enums.Algorithm;
|
import top.continew.starter.encrypt.autoconfigure.PasswordEncoderProperties;
|
||||||
|
import top.continew.starter.encrypt.enums.Algorithm;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 加/解密配置属性
|
* 字段加密配置属性
|
||||||
*
|
*
|
||||||
* @author Charles7c
|
* @author Charles7c
|
||||||
* @author lishuyan
|
* @author lishuyan
|
||||||
* @since 1.4.0
|
* @since 1.4.0
|
||||||
*/
|
*/
|
||||||
@ConfigurationProperties(PropertiesConstants.SECURITY_CRYPTO)
|
@ConfigurationProperties(PropertiesConstants.ENCRYPT_FIELD)
|
||||||
public class CryptoProperties {
|
public class FieldEncryptProperties {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否启用
|
* 是否启用
|
||||||
*/
|
*/
|
||||||
private boolean enabled = true;
|
private Boolean enabled;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 默认算法
|
* 默认算法
|
||||||
@@ -62,11 +63,11 @@ public class CryptoProperties {
|
|||||||
@NestedConfigurationProperty
|
@NestedConfigurationProperty
|
||||||
private PasswordEncoderProperties passwordEncoder;
|
private PasswordEncoderProperties passwordEncoder;
|
||||||
|
|
||||||
public boolean isEnabled() {
|
public Boolean getEnabled() {
|
||||||
return enabled;
|
return enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setEnabled(boolean enabled) {
|
public void setEnabled(Boolean enabled) {
|
||||||
this.enabled = enabled;
|
this.enabled = enabled;
|
||||||
}
|
}
|
||||||
|
|
@@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package top.continew.starter.security.crypto.mybatis;
|
package top.continew.starter.encrypt.field.interceptor;
|
||||||
|
|
||||||
import cn.hutool.core.text.CharSequenceUtil;
|
import cn.hutool.core.text.CharSequenceUtil;
|
||||||
import cn.hutool.core.util.ReflectUtil;
|
import cn.hutool.core.util.ReflectUtil;
|
||||||
@@ -22,7 +22,7 @@ import org.apache.ibatis.annotations.Param;
|
|||||||
import org.apache.ibatis.mapping.MappedStatement;
|
import org.apache.ibatis.mapping.MappedStatement;
|
||||||
import top.continew.starter.core.constant.StringConstants;
|
import top.continew.starter.core.constant.StringConstants;
|
||||||
import top.continew.starter.core.exception.BaseException;
|
import top.continew.starter.core.exception.BaseException;
|
||||||
import top.continew.starter.security.crypto.annotation.FieldEncrypt;
|
import top.continew.starter.encrypt.field.annotation.FieldEncrypt;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
@@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package top.continew.starter.security.crypto.mybatis;
|
package top.continew.starter.encrypt.field.interceptor;
|
||||||
|
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.core.text.CharSequenceUtil;
|
import cn.hutool.core.text.CharSequenceUtil;
|
||||||
@@ -26,8 +26,8 @@ import org.apache.ibatis.plugin.Intercepts;
|
|||||||
import org.apache.ibatis.plugin.Invocation;
|
import org.apache.ibatis.plugin.Invocation;
|
||||||
import org.apache.ibatis.plugin.Signature;
|
import org.apache.ibatis.plugin.Signature;
|
||||||
import org.apache.ibatis.type.SimpleTypeRegistry;
|
import org.apache.ibatis.type.SimpleTypeRegistry;
|
||||||
import top.continew.starter.security.crypto.annotation.FieldEncrypt;
|
import top.continew.starter.encrypt.field.annotation.FieldEncrypt;
|
||||||
import top.continew.starter.security.crypto.util.EncryptHelper;
|
import top.continew.starter.encrypt.field.util.EncryptHelper;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.sql.Statement;
|
import java.sql.Statement;
|
@@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package top.continew.starter.security.crypto.mybatis;
|
package top.continew.starter.encrypt.field.interceptor;
|
||||||
|
|
||||||
import cn.hutool.core.text.CharSequenceUtil;
|
import cn.hutool.core.text.CharSequenceUtil;
|
||||||
import cn.hutool.core.util.ClassUtil;
|
import cn.hutool.core.util.ClassUtil;
|
||||||
@@ -29,8 +29,8 @@ import org.apache.ibatis.mapping.MappedStatement;
|
|||||||
import org.apache.ibatis.session.ResultHandler;
|
import org.apache.ibatis.session.ResultHandler;
|
||||||
import org.apache.ibatis.session.RowBounds;
|
import org.apache.ibatis.session.RowBounds;
|
||||||
import top.continew.starter.core.constant.StringConstants;
|
import top.continew.starter.core.constant.StringConstants;
|
||||||
import top.continew.starter.security.crypto.annotation.FieldEncrypt;
|
import top.continew.starter.encrypt.field.annotation.FieldEncrypt;
|
||||||
import top.continew.starter.security.crypto.util.EncryptHelper;
|
import top.continew.starter.encrypt.field.util.EncryptHelper;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
@@ -14,25 +14,19 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package top.continew.starter.security.crypto.util;
|
package top.continew.starter.encrypt.field.util;
|
||||||
|
|
||||||
import cn.hutool.core.codec.Base64;
|
|
||||||
import cn.hutool.core.text.CharSequenceUtil;
|
import cn.hutool.core.text.CharSequenceUtil;
|
||||||
import cn.hutool.core.util.ArrayUtil;
|
|
||||||
import cn.hutool.core.util.ReflectUtil;
|
import cn.hutool.core.util.ReflectUtil;
|
||||||
import cn.hutool.crypto.SecureUtil;
|
|
||||||
import cn.hutool.crypto.asymmetric.KeyType;
|
|
||||||
import cn.hutool.crypto.asymmetric.RSA;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import top.continew.starter.security.crypto.annotation.FieldEncrypt;
|
import top.continew.starter.encrypt.context.CryptoContext;
|
||||||
import top.continew.starter.security.crypto.autoconfigure.CryptoContext;
|
import top.continew.starter.encrypt.encryptor.IEncryptor;
|
||||||
import top.continew.starter.security.crypto.autoconfigure.CryptoProperties;
|
import top.continew.starter.encrypt.enums.Algorithm;
|
||||||
import top.continew.starter.security.crypto.encryptor.IEncryptor;
|
import top.continew.starter.encrypt.field.annotation.FieldEncrypt;
|
||||||
import top.continew.starter.security.crypto.enums.Algorithm;
|
import top.continew.starter.encrypt.field.autoconfigure.FieldEncryptProperties;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
@@ -49,7 +43,7 @@ public class EncryptHelper {
|
|||||||
/**
|
/**
|
||||||
* 默认加密配置
|
* 默认加密配置
|
||||||
*/
|
*/
|
||||||
private static CryptoProperties defaultProperties;
|
private static FieldEncryptProperties defaultProperties;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 加密器缓存
|
* 加密器缓存
|
||||||
@@ -64,7 +58,7 @@ public class EncryptHelper {
|
|||||||
*
|
*
|
||||||
* @param properties 加密配置
|
* @param properties 加密配置
|
||||||
*/
|
*/
|
||||||
public static void init(CryptoProperties properties) {
|
public static void init(FieldEncryptProperties properties) {
|
||||||
defaultProperties = properties;
|
defaultProperties = properties;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,7 +99,7 @@ public class EncryptHelper {
|
|||||||
* @return 加密后的字符串
|
* @return 加密后的字符串
|
||||||
*/
|
*/
|
||||||
public static String encrypt(String value, FieldEncrypt fieldEncrypt) {
|
public static String encrypt(String value, FieldEncrypt fieldEncrypt) {
|
||||||
if (CharSequenceUtil.isBlank(value) || fieldEncrypt == null || !defaultProperties.isEnabled()) {
|
if (CharSequenceUtil.isBlank(value) || fieldEncrypt == null) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
String ciphertext = value;
|
String ciphertext = value;
|
||||||
@@ -126,7 +120,7 @@ public class EncryptHelper {
|
|||||||
* @return 加密后的字符串
|
* @return 加密后的字符串
|
||||||
*/
|
*/
|
||||||
public static String encrypt(String value) {
|
public static String encrypt(String value) {
|
||||||
if (CharSequenceUtil.isBlank(value) || !defaultProperties.isEnabled()) {
|
if (CharSequenceUtil.isBlank(value)) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
String ciphertext = value;
|
String ciphertext = value;
|
||||||
@@ -148,7 +142,7 @@ public class EncryptHelper {
|
|||||||
* @return 解密后的字符串
|
* @return 解密后的字符串
|
||||||
*/
|
*/
|
||||||
public static String decrypt(String value, FieldEncrypt fieldEncrypt) {
|
public static String decrypt(String value, FieldEncrypt fieldEncrypt) {
|
||||||
if (CharSequenceUtil.isBlank(value) || fieldEncrypt == null || !defaultProperties.isEnabled()) {
|
if (CharSequenceUtil.isBlank(value) || fieldEncrypt == null) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
String plaintext = value;
|
String plaintext = value;
|
||||||
@@ -169,7 +163,7 @@ public class EncryptHelper {
|
|||||||
* @return 解密后的字符串
|
* @return 解密后的字符串
|
||||||
*/
|
*/
|
||||||
public static String decrypt(String value) {
|
public static String decrypt(String value) {
|
||||||
if (CharSequenceUtil.isBlank(value) || !defaultProperties.isEnabled()) {
|
if (CharSequenceUtil.isBlank(value)) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
String plaintext = value;
|
String plaintext = value;
|
||||||
@@ -223,98 +217,4 @@ public class EncryptHelper {
|
|||||||
cryptoContext.setPublicKey(defaultProperties.getPublicKey());
|
cryptoContext.setPublicKey(defaultProperties.getPublicKey());
|
||||||
return cryptoContext;
|
return cryptoContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Base64编码
|
|
||||||
*
|
|
||||||
* @param data 待编码数据
|
|
||||||
* @return 编码后字符串
|
|
||||||
* @since 2.14.0
|
|
||||||
*/
|
|
||||||
public static String encodeByBase64(String data) {
|
|
||||||
return Base64.encode(data, StandardCharsets.UTF_8);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base64解码
|
|
||||||
*
|
|
||||||
* @param data 待解码数据
|
|
||||||
* @return 解码后字符串
|
|
||||||
* @since 2.14.0
|
|
||||||
*/
|
|
||||||
public static String decodeByBase64(String data) {
|
|
||||||
return Base64.decodeStr(data, StandardCharsets.UTF_8);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* AES加密
|
|
||||||
*
|
|
||||||
* @param data 待加密数据
|
|
||||||
* @param password 秘钥字符串
|
|
||||||
* @return 加密后字符串, 采用Base64编码
|
|
||||||
* @since 2.14.0
|
|
||||||
*/
|
|
||||||
public static String encryptByAes(String data, String password) {
|
|
||||||
if (CharSequenceUtil.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 解密后字符串
|
|
||||||
* @since 2.14.0
|
|
||||||
*/
|
|
||||||
public static String decryptByAes(String data, String password) {
|
|
||||||
if (CharSequenceUtil.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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* RSA公钥加密
|
|
||||||
*
|
|
||||||
* @param data 待加密数据
|
|
||||||
* @param publicKey 公钥
|
|
||||||
* @return 加密后字符串, 采用Base64编码
|
|
||||||
* @since 2.14.0
|
|
||||||
*/
|
|
||||||
public static String encryptByRsa(String data, String publicKey) {
|
|
||||||
if (CharSequenceUtil.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 privateKey 私钥
|
|
||||||
* @return 解密后字符串
|
|
||||||
* @since 2.14.0
|
|
||||||
*/
|
|
||||||
public static String decryptByRsa(String data, String privateKey) {
|
|
||||||
if (CharSequenceUtil.isBlank(privateKey)) {
|
|
||||||
throw new IllegalArgumentException("RSA需要传入私钥进行解密");
|
|
||||||
}
|
|
||||||
RSA rsa = SecureUtil.rsa(privateKey, null);
|
|
||||||
return rsa.decryptStr(data, KeyType.PrivateKey, StandardCharsets.UTF_8);
|
|
||||||
}
|
|
||||||
}
|
}
|
@@ -0,0 +1 @@
|
|||||||
|
top.continew.starter.encrypt.field.autoconfigure.FieldEncryptAutoConfiguration
|
23
continew-starter-encrypt/pom.xml
Normal file
23
continew-starter-encrypt/pom.xml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<?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">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>top.continew.starter</groupId>
|
||||||
|
<artifactId>continew-starter</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>continew-starter-encrypt</artifactId>
|
||||||
|
<packaging>pom</packaging>
|
||||||
|
|
||||||
|
<name>${project.artifactId}</name>
|
||||||
|
<description>ContiNew Starter 加密模块</description>
|
||||||
|
|
||||||
|
<modules>
|
||||||
|
<module>continew-starter-encrypt-core</module>
|
||||||
|
<module>continew-starter-encrypt-field</module>
|
||||||
|
<module>continew-starter-encrypt-api</module>
|
||||||
|
</modules>
|
||||||
|
</project>
|
@@ -1,2 +0,0 @@
|
|||||||
top.continew.starter.security.crypto.autoconfigure.CryptoAutoConfiguration
|
|
||||||
top.continew.starter.security.crypto.autoconfigure.ApiCryptoAutoConfiguration
|
|
@@ -1,6 +0,0 @@
|
|||||||
--- ### 安全配置:字段加/解密配置
|
|
||||||
continew-starter.security:
|
|
||||||
crypto:
|
|
||||||
enabled: true
|
|
||||||
# 默认算法,即 @FieldEncrypt 默认采用的算法(默认:AES 对称加密算法)
|
|
||||||
algorithm: AES
|
|
@@ -16,7 +16,6 @@
|
|||||||
<description>ContiNew Starter 安全模块</description>
|
<description>ContiNew Starter 安全模块</description>
|
||||||
|
|
||||||
<modules>
|
<modules>
|
||||||
<module>continew-starter-security-crypto</module>
|
|
||||||
<module>continew-starter-security-mask</module>
|
<module>continew-starter-security-mask</module>
|
||||||
<module>continew-starter-security-xss</module>
|
<module>continew-starter-security-xss</module>
|
||||||
<module>continew-starter-security-sensitivewords</module>
|
<module>continew-starter-security-sensitivewords</module>
|
||||||
|
1
pom.xml
1
pom.xml
@@ -42,6 +42,7 @@
|
|||||||
<module>continew-starter-cache</module>
|
<module>continew-starter-cache</module>
|
||||||
<module>continew-starter-auth</module>
|
<module>continew-starter-auth</module>
|
||||||
<module>continew-starter-data</module>
|
<module>continew-starter-data</module>
|
||||||
|
<module>continew-starter-encrypt</module>
|
||||||
<module>continew-starter-security</module>
|
<module>continew-starter-security</module>
|
||||||
<module>continew-starter-ratelimiter</module>
|
<module>continew-starter-ratelimiter</module>
|
||||||
<module>continew-starter-idempotent</module>
|
<module>continew-starter-idempotent</module>
|
||||||
|
Reference in New Issue
Block a user