This commit is contained in:
2025-11-28 16:23:32 +08:00
commit a9e0e16c29
826 changed files with 89805 additions and 0 deletions

View File

@@ -0,0 +1,4 @@
## 参考
encrypt-body-spring-boot-starter: https://github.com/Licoy/encrypt-body-spring-boot-starter
micahttps://github.com/lets-mica/mica

View File

@@ -0,0 +1,34 @@
<?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>
<artifactId>BladeX-Tool</artifactId>
<groupId>org.springblade</groupId>
<version>${revision}</version>
</parent>
<artifactId>blade-starter-api-crypto</artifactId>
<name>${project.artifactId}</name>
<version>${project.parent.version}</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-tool</artifactId>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-auto</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-context</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,22 @@
package org.springblade.core.api.crypto.annotation.crypto;
import org.springblade.core.api.crypto.annotation.decrypt.ApiDecrypt;
import org.springblade.core.api.crypto.annotation.encrypt.ApiEncrypt;
import org.springblade.core.api.crypto.enums.CryptoType;
import java.lang.annotation.*;
/**
* <p>AES加密解密含有{@link org.springframework.web.bind.annotation.RequestBody}注解的参数请求数据</p>
*
* @author Chill
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@ApiEncrypt(CryptoType.AES)
@ApiDecrypt(CryptoType.AES)
public @interface ApiCrypto {
}

View File

@@ -0,0 +1,22 @@
package org.springblade.core.api.crypto.annotation.crypto;
import org.springblade.core.api.crypto.annotation.decrypt.ApiDecrypt;
import org.springblade.core.api.crypto.annotation.encrypt.ApiEncrypt;
import org.springblade.core.api.crypto.enums.CryptoType;
import java.lang.annotation.*;
/**
* <p>AES加密解密含有{@link org.springframework.web.bind.annotation.RequestBody}注解的参数请求数据</p>
*
* @author Chill
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@ApiEncrypt(CryptoType.AES)
@ApiDecrypt(CryptoType.AES)
public @interface ApiCryptoAes {
}

View File

@@ -0,0 +1,22 @@
package org.springblade.core.api.crypto.annotation.crypto;
import org.springblade.core.api.crypto.annotation.decrypt.ApiDecrypt;
import org.springblade.core.api.crypto.annotation.encrypt.ApiEncrypt;
import org.springblade.core.api.crypto.enums.CryptoType;
import java.lang.annotation.*;
/**
* <p>DES加密解密含有{@link org.springframework.web.bind.annotation.RequestBody}注解的参数请求数据</p>
*
* @author Chill
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@ApiEncrypt(CryptoType.DES)
@ApiDecrypt(CryptoType.DES)
public @interface ApiCryptoDes {
}

View File

@@ -0,0 +1,22 @@
package org.springblade.core.api.crypto.annotation.crypto;
import org.springblade.core.api.crypto.annotation.decrypt.ApiDecrypt;
import org.springblade.core.api.crypto.annotation.encrypt.ApiEncrypt;
import org.springblade.core.api.crypto.enums.CryptoType;
import java.lang.annotation.*;
/**
* <p>RSA加密解密含有{@link org.springframework.web.bind.annotation.RequestBody}注解的参数请求数据</p>
*
* @author Chill
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@ApiEncrypt(CryptoType.RSA)
@ApiDecrypt(CryptoType.RSA)
public @interface ApiCryptoRsa {
}

View File

@@ -0,0 +1,32 @@
package org.springblade.core.api.crypto.annotation.decrypt;
import org.springblade.core.api.crypto.enums.CryptoType;
import java.lang.annotation.*;
/**
* <p>解密含有{@link org.springframework.web.bind.annotation.RequestBody}注解的参数请求数据,可用于整个控制类或者某个控制器上</p>
*
* @author licoy.cn, L.cm
*/
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface ApiDecrypt {
/**
* 解密类型
*
* @return 类型
*/
CryptoType value();
/**
* 私钥,用于某些需要单独配置私钥的方法,没有时读取全局配置的私钥
*
* @return 私钥
*/
String secretKey() default "";
}

View File

@@ -0,0 +1,28 @@
package org.springblade.core.api.crypto.annotation.decrypt;
import org.springblade.core.api.crypto.enums.CryptoType;
import org.springframework.core.annotation.AliasFor;
import java.lang.annotation.*;
/**
* aes 解密
*
* @author licoy.cn, L.cm
* @see ApiDecrypt
*/
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@ApiDecrypt(CryptoType.AES)
public @interface ApiDecryptAes {
/**
* Alias for {@link ApiDecrypt#secretKey()}.
*
* @return {String}
*/
@AliasFor(annotation = ApiDecrypt.class)
String secretKey() default "";
}

View File

@@ -0,0 +1,28 @@
package org.springblade.core.api.crypto.annotation.decrypt;
import org.springblade.core.api.crypto.enums.CryptoType;
import org.springframework.core.annotation.AliasFor;
import java.lang.annotation.*;
/**
* des 解密
*
* @author licoy.cn
* @see ApiDecrypt
*/
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@ApiDecrypt(CryptoType.DES)
public @interface ApiDecryptDes {
/**
* Alias for {@link ApiDecrypt#secretKey()}.
*
* @return {String}
*/
@AliasFor(annotation = ApiDecrypt.class)
String secretKey() default "";
}

View File

@@ -0,0 +1,18 @@
package org.springblade.core.api.crypto.annotation.decrypt;
import org.springblade.core.api.crypto.enums.CryptoType;
import java.lang.annotation.*;
/**
* rsa 解密
*
* @author licoy.cn
* @see ApiDecrypt
*/
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@ApiDecrypt(CryptoType.RSA)
public @interface ApiDecryptRsa {
}

View File

@@ -0,0 +1,33 @@
package org.springblade.core.api.crypto.annotation.encrypt;
import org.springblade.core.api.crypto.enums.CryptoType;
import java.lang.annotation.*;
/**
* <p>加密{@link org.springframework.web.bind.annotation.ResponseBody}响应数据,可用于整个控制类或者某个控制器上</p>
*
* @author licoy.cn, L.cm
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface ApiEncrypt {
/**
* 加密类型
*
* @return 类型
*/
CryptoType value();
/**
* 私钥,用于某些需要单独配置私钥的方法,没有时读取全局配置的私钥
*
* @return 私钥
*/
String secretKey() default "";
}

View File

@@ -0,0 +1,28 @@
package org.springblade.core.api.crypto.annotation.encrypt;
import org.springblade.core.api.crypto.enums.CryptoType;
import org.springframework.core.annotation.AliasFor;
import java.lang.annotation.*;
/**
* aes 加密
*
* @author licoy.cn, L.cm
* @see ApiEncrypt
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@ApiEncrypt(CryptoType.AES)
public @interface ApiEncryptAes {
/**
* Alias for {@link ApiEncrypt#secretKey()}.
*
* @return {String}
*/
@AliasFor(annotation = ApiEncrypt.class)
String secretKey() default "";
}

View File

@@ -0,0 +1,28 @@
package org.springblade.core.api.crypto.annotation.encrypt;
import org.springblade.core.api.crypto.enums.CryptoType;
import org.springframework.core.annotation.AliasFor;
import java.lang.annotation.*;
/**
* des 加密
*
* @author licoy.cn, L.cm
* @see ApiEncrypt
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@ApiEncrypt(CryptoType.DES)
public @interface ApiEncryptDes {
/**
* Alias for {@link ApiEncrypt#secretKey()}.
*
* @return {String}
*/
@AliasFor(annotation = ApiEncrypt.class)
String secretKey() default "";
}

View File

@@ -0,0 +1,18 @@
package org.springblade.core.api.crypto.annotation.encrypt;
import org.springblade.core.api.crypto.enums.CryptoType;
import java.lang.annotation.*;
/**
* rsa 加密
*
* @author licoy.cn, L.cm
* @see ApiEncrypt
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@ApiEncrypt(CryptoType.RSA)
public @interface ApiEncryptRsa {
}

View File

@@ -0,0 +1,25 @@
package org.springblade.core.api.crypto.bean;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springblade.core.api.crypto.enums.CryptoType;
/**
* <p>加密注解信息</p>
*
* @author licoy.cn, L.cm
*/
@Getter
@RequiredArgsConstructor
public class CryptoInfoBean {
/**
* 加密类型
*/
private final CryptoType type;
/**
* 私钥
*/
private final String secretKey;
}

View File

@@ -0,0 +1,20 @@
package org.springblade.core.api.crypto.bean;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;
import java.io.InputStream;
/**
* <p>解密信息输入流</p>
*
* @author licoy.cn, L.cm
*/
@Getter
@RequiredArgsConstructor
public class DecryptHttpInputMessage implements HttpInputMessage {
private final InputStream body;
private final HttpHeaders headers;
}

View File

@@ -0,0 +1,30 @@
package org.springblade.core.api.crypto.config;
import lombok.RequiredArgsConstructor;
import org.springblade.core.api.crypto.core.ApiDecryptParamResolver;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.List;
/**
* api 签名自动配置
*
* @author L.cm
*/
@AutoConfiguration
@RequiredArgsConstructor
@EnableConfigurationProperties(ApiCryptoProperties.class)
@ConditionalOnProperty(value = ApiCryptoProperties.PREFIX + ".enabled", havingValue = "true", matchIfMissing = true)
public class ApiCryptoConfiguration implements WebMvcConfigurer {
private final ApiCryptoProperties apiCryptoProperties;
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new ApiDecryptParamResolver(apiCryptoProperties));
}
}

View File

@@ -0,0 +1,46 @@
package org.springblade.core.api.crypto.config;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* api 签名配置类
*
* @author licoy.cn, L.cm
*/
@Getter
@Setter
@ConfigurationProperties(ApiCryptoProperties.PREFIX)
public class ApiCryptoProperties {
/**
* 前缀
*/
public static final String PREFIX = "blade.api.crypto";
/**
* 是否开启 api 签名
*/
private Boolean enabled = Boolean.TRUE;
/**
* url的参数签名传递的参数名。例如/user?data=签名后的数据
*/
private String paramName = "data";
/**
* aes 密钥
*/
private String aesKey;
/**
* des 密钥
*/
private String desKey;
/**
* rsa 私钥
*/
private String rsaPrivateKey;
}

View File

@@ -0,0 +1,76 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.api.crypto.core;
import lombok.RequiredArgsConstructor;
import org.springblade.core.api.crypto.annotation.decrypt.ApiDecrypt;
import org.springblade.core.api.crypto.bean.CryptoInfoBean;
import org.springblade.core.api.crypto.config.ApiCryptoProperties;
import org.springblade.core.api.crypto.util.ApiCryptoUtil;
import org.springblade.core.tool.jackson.JsonUtil;
import org.springblade.core.tool.utils.Charsets;
import org.springblade.core.tool.utils.StringUtil;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.lang.Nullable;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import java.lang.reflect.Parameter;
/**
* param 参数 解析
*
* @author L.cm
*/
@RequiredArgsConstructor
public class ApiDecryptParamResolver implements HandlerMethodArgumentResolver {
private final ApiCryptoProperties properties;
@Override
public boolean supportsParameter(MethodParameter parameter) {
return AnnotatedElementUtils.hasAnnotation(parameter.getParameter(), ApiDecrypt.class);
}
@Nullable
@Override
public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) {
Parameter parameter = methodParameter.getParameter();
ApiDecrypt apiDecrypt = AnnotatedElementUtils.getMergedAnnotation(parameter, ApiDecrypt.class);
String text = webRequest.getParameter(properties.getParamName());
if (StringUtil.isBlank(text)) {
return null;
}
CryptoInfoBean infoBean = new CryptoInfoBean(apiDecrypt.value(), apiDecrypt.secretKey());
byte[] textBytes = text.getBytes(Charsets.UTF_8);
byte[] decryptData = ApiCryptoUtil.decryptData(properties, textBytes, infoBean);
return JsonUtil.readValue(decryptData, parameter.getType());
}
}

View File

@@ -0,0 +1,87 @@
package org.springblade.core.api.crypto.core;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.api.crypto.annotation.decrypt.ApiDecrypt;
import org.springblade.core.api.crypto.bean.CryptoInfoBean;
import org.springblade.core.api.crypto.bean.DecryptHttpInputMessage;
import org.springblade.core.api.crypto.config.ApiCryptoProperties;
import org.springblade.core.api.crypto.exception.DecryptBodyFailException;
import org.springblade.core.api.crypto.util.ApiCryptoUtil;
import org.springblade.core.tool.utils.ClassUtil;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.lang.NonNull;
import org.springframework.util.StreamUtils;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;
/**
* 请求数据的加密信息解密处理<br>
* 本类只对控制器参数中含有<strong>{@link org.springframework.web.bind.annotation.RequestBody}</strong>
* 以及package为<strong><code>org.springblade.core.api.signature.annotation.decrypt</code></strong>下的注解有效
*
* @author licoy.cn, L.cm
* @see RequestBodyAdvice
*/
@Slf4j
@Order(1)
@AutoConfiguration
@ControllerAdvice
@RequiredArgsConstructor
@ConditionalOnProperty(value = ApiCryptoProperties.PREFIX + ".enabled", havingValue = "true", matchIfMissing = true)
public class ApiDecryptRequestBodyAdvice implements RequestBodyAdvice {
private final ApiCryptoProperties properties;
@Override
public boolean supports(MethodParameter methodParameter, @NonNull Type targetType, @NonNull Class<? extends HttpMessageConverter<?>> converterType) {
return ClassUtil.isAnnotated(methodParameter.getMethod(), ApiDecrypt.class);
}
@Override
public Object handleEmptyBody(Object body, @NonNull HttpInputMessage inputMessage, @NonNull MethodParameter parameter,
@NonNull Type targetType, @NonNull Class<? extends HttpMessageConverter<?>> converterType) {
return body;
}
@NonNull
@Override
public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, @NonNull MethodParameter parameter,
@NonNull Type targetType, @NonNull Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
// 判断 body 是否为空
InputStream messageBody = inputMessage.getBody();
if (messageBody.available() <= 0) {
return inputMessage;
}
byte[] decryptedBody = null;
CryptoInfoBean cryptoInfoBean = ApiCryptoUtil.getDecryptInfo(parameter);
if (cryptoInfoBean != null) {
// base64 byte array
byte[] bodyByteArray = StreamUtils.copyToByteArray(messageBody);
decryptedBody = ApiCryptoUtil.decryptData(properties, bodyByteArray, cryptoInfoBean);
}
if (decryptedBody == null) {
throw new DecryptBodyFailException("Decryption error, " +
"please check if the selected source data is encrypted correctly." +
" (解密错误,请检查选择的源数据的加密方式是否正确。)");
}
InputStream inputStream = new ByteArrayInputStream(decryptedBody);
return new DecryptHttpInputMessage(inputStream, inputMessage.getHeaders());
}
@NonNull
@Override
public Object afterBodyRead(@NonNull Object body, @NonNull HttpInputMessage inputMessage, @NonNull MethodParameter parameter, @NonNull Type targetType, @NonNull Class<? extends HttpMessageConverter<?>> converterType) {
return body;
}
}

View File

@@ -0,0 +1,64 @@
package org.springblade.core.api.crypto.core;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.api.crypto.annotation.encrypt.ApiEncrypt;
import org.springblade.core.api.crypto.bean.CryptoInfoBean;
import org.springblade.core.api.crypto.config.ApiCryptoProperties;
import org.springblade.core.api.crypto.exception.EncryptBodyFailException;
import org.springblade.core.api.crypto.util.ApiCryptoUtil;
import org.springblade.core.tool.jackson.JsonUtil;
import org.springblade.core.tool.utils.ClassUtil;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.Order;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
/**
* 响应数据的加密处理<br>
* 本类只对控制器参数中含有<strong>{@link org.springframework.web.bind.annotation.ResponseBody}</strong>
* 或者控制类上含有<strong>{@link org.springframework.web.bind.annotation.RestController}</strong>
* 以及package为<strong><code>org.springblade.core.api.signature.annotation.encrypt</code></strong>下的注解有效
*
* @author licoy.cn, L.cm
* @see ResponseBodyAdvice
*/
@Slf4j
@Order(1)
@AutoConfiguration
@ControllerAdvice
@RequiredArgsConstructor
@ConditionalOnProperty(value = ApiCryptoProperties.PREFIX + ".enabled", havingValue = "true", matchIfMissing = true)
public class ApiEncryptResponseBodyAdvice implements ResponseBodyAdvice<Object> {
private final ApiCryptoProperties properties;
@Override
public boolean supports(MethodParameter returnType, @NonNull Class converterType) {
return ClassUtil.isAnnotated(returnType.getMethod(), ApiEncrypt.class);
}
@Nullable
@Override
public Object beforeBodyWrite(Object body, @NonNull MethodParameter returnType, @NonNull MediaType selectedContentType,
@NonNull Class selectedConverterType, @NonNull ServerHttpRequest request, @NonNull ServerHttpResponse response) {
if (body == null) {
return null;
}
response.getHeaders().setContentType(MediaType.TEXT_PLAIN);
CryptoInfoBean cryptoInfoBean = ApiCryptoUtil.getEncryptInfo(returnType);
if (cryptoInfoBean != null) {
byte[] bodyJsonBytes = JsonUtil.toJsonAsBytes(body);
return ApiCryptoUtil.encryptData(properties, bodyJsonBytes, cryptoInfoBean);
}
throw new EncryptBodyFailException();
}
}

View File

@@ -0,0 +1,25 @@
package org.springblade.core.api.crypto.enums;
/**
* <p>加密方式</p>
*
* @author licoy.cn, L.cm
*/
public enum CryptoType {
/**
* des
*/
DES,
/**
* aes
*/
AES,
/**
* rsa
*/
RSA
}

View File

@@ -0,0 +1,13 @@
package org.springblade.core.api.crypto.exception;
/**
* <p>解密数据失败异常</p>
*
* @author licoy.cn
*/
public class DecryptBodyFailException extends RuntimeException {
public DecryptBodyFailException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,17 @@
package org.springblade.core.api.crypto.exception;
/**
* <p>加密数据失败异常</p>
*
* @author licoy.cn
*/
public class EncryptBodyFailException extends RuntimeException {
public EncryptBodyFailException() {
super("Encrypted data failed. (加密数据失败)");
}
public EncryptBodyFailException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,14 @@
package org.springblade.core.api.crypto.exception;
/**
* <p>加密方式未找到或未定义异常</p>
*
* @author licoy.cn
*/
public class EncryptMethodNotFoundException extends RuntimeException {
public EncryptMethodNotFoundException() {
super("Encryption method is not defined. (加密方式未定义)");
}
}

View File

@@ -0,0 +1,14 @@
package org.springblade.core.api.crypto.exception;
/**
* <p>未配置KEY运行时异常</p>
*
* @author licoy.cn, L.cm
*/
public class KeyNotConfiguredException extends RuntimeException {
public KeyNotConfiguredException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,124 @@
package org.springblade.core.api.crypto.util;
import org.springblade.core.api.crypto.annotation.decrypt.ApiDecrypt;
import org.springblade.core.api.crypto.annotation.encrypt.ApiEncrypt;
import org.springblade.core.api.crypto.bean.CryptoInfoBean;
import org.springblade.core.api.crypto.config.ApiCryptoProperties;
import org.springblade.core.api.crypto.enums.CryptoType;
import org.springblade.core.api.crypto.exception.EncryptBodyFailException;
import org.springblade.core.api.crypto.exception.EncryptMethodNotFoundException;
import org.springblade.core.api.crypto.exception.KeyNotConfiguredException;
import org.springblade.core.tool.utils.*;
import org.springframework.core.MethodParameter;
import org.springframework.lang.Nullable;
import java.util.Objects;
/**
* <p>辅助检测工具类</p>
*
* @author licoy.cn, L.cm
*/
public class ApiCryptoUtil {
/**
* 获取方法控制器上的加密注解信息
*
* @param methodParameter 控制器方法
* @return 加密注解信息
*/
@Nullable
public static CryptoInfoBean getEncryptInfo(MethodParameter methodParameter) {
ApiEncrypt encryptBody = ClassUtil.getAnnotation(methodParameter.getMethod(), ApiEncrypt.class);
if (encryptBody == null) {
return null;
}
return new CryptoInfoBean(encryptBody.value(), encryptBody.secretKey());
}
/**
* 获取方法控制器上的解密注解信息
*
* @param methodParameter 控制器方法
* @return 加密注解信息
*/
@Nullable
public static CryptoInfoBean getDecryptInfo(MethodParameter methodParameter) {
ApiDecrypt decryptBody = ClassUtil.getAnnotation(methodParameter.getMethod(), ApiDecrypt.class);
if (decryptBody == null) {
return null;
}
return new CryptoInfoBean(decryptBody.value(), decryptBody.secretKey());
}
/**
* 选择加密方式并进行加密
*
* @param jsonData json 数据
* @param infoBean 加密信息
* @return 加密结果
*/
public static String encryptData(ApiCryptoProperties properties, byte[] jsonData, CryptoInfoBean infoBean) {
CryptoType type = infoBean.getType();
if (type == null) {
throw new EncryptMethodNotFoundException();
}
String secretKey = infoBean.getSecretKey();
if (type == CryptoType.DES) {
secretKey = ApiCryptoUtil.checkSecretKey(properties.getDesKey(), secretKey, "DES");
return DesUtil.encryptToBase64(jsonData, secretKey);
}
if (type == CryptoType.AES) {
secretKey = ApiCryptoUtil.checkSecretKey(properties.getAesKey(), secretKey, "AES");
return AesUtil.encryptToBase64(jsonData, secretKey);
}
if (type == CryptoType.RSA) {
String privateKey = Objects.requireNonNull(properties.getRsaPrivateKey());
return RsaUtil.encryptByPrivateKeyToBase64(privateKey, jsonData);
}
throw new EncryptBodyFailException();
}
/**
* 选择加密方式并进行解密
*
* @param bodyData byte array
* @param infoBean 加密信息
* @return 解密结果
*/
public static byte[] decryptData(ApiCryptoProperties properties, byte[] bodyData, CryptoInfoBean infoBean) {
CryptoType type = infoBean.getType();
if (type == null) {
throw new EncryptMethodNotFoundException();
}
String secretKey = infoBean.getSecretKey();
if (type == CryptoType.AES) {
secretKey = ApiCryptoUtil.checkSecretKey(properties.getAesKey(), secretKey, "AES");
return AesUtil.decryptFormBase64(bodyData, secretKey);
}
if (type == CryptoType.DES) {
secretKey = ApiCryptoUtil.checkSecretKey(properties.getDesKey(), secretKey, "DES");
return DesUtil.decryptFormBase64(bodyData, secretKey);
}
if (type == CryptoType.RSA) {
String privateKey = Objects.requireNonNull(properties.getRsaPrivateKey());
return RsaUtil.decryptFromBase64(privateKey, bodyData);
}
throw new EncryptMethodNotFoundException();
}
/**
* 检验私钥
*
* @param k1 配置的私钥
* @param k2 注解上的私钥
* @param keyName key名称
* @return 私钥
*/
private static String checkSecretKey(String k1, String k2, String keyName) {
if (StringUtil.isBlank(k1) && StringUtil.isBlank(k2)) {
throw new KeyNotConfiguredException(String.format("%s key is not configured (未配置%s)", keyName, keyName));
}
return StringUtil.isBlank(k2) ? k1 : k2;
}
}