mirror of
https://github.com/chillzhuang/blade-tool
synced 2024-11-14 14:39:29 +08:00
🎉 3.5.0.RELEASE 新增报文加密 令牌签名校验提示
This commit is contained in:
parent
1700db64c5
commit
d178675c53
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springblade</groupId>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<version>3.4.1</version>
|
||||
<version>3.5.0</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -101,7 +101,7 @@ mybatis-plus:
|
||||
swagger:
|
||||
title: SpringBlade 接口文档系统
|
||||
description: SpringBlade 接口文档系统
|
||||
version: 3.4.1
|
||||
version: 3.5.0
|
||||
license: Powered By SpringBlade
|
||||
licenseUrl: https://bladex.vip
|
||||
terms-of-service-url: https://bladex.vip
|
||||
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<groupId>org.springblade</groupId>
|
||||
<version>3.4.1</version>
|
||||
<version>3.5.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
29
blade-core-crypto/pom.xml
Normal file
29
blade-core-crypto/pom.xml
Normal file
@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<groupId>org.springblade</groupId>
|
||||
<version>3.5.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>blade-core-crypto</artifactId>
|
||||
<name>${project.artifactId}</name>
|
||||
<version>${blade.tool.version}</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springblade</groupId>
|
||||
<artifactId>blade-core-tool</artifactId>
|
||||
<version>${blade.tool.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-context</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
@ -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 "";
|
||||
|
||||
}
|
@ -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 "";
|
||||
|
||||
}
|
@ -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 "";
|
||||
|
||||
}
|
@ -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 "";
|
||||
|
||||
}
|
@ -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 "";
|
||||
|
||||
}
|
@ -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 "";
|
||||
|
||||
}
|
@ -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;
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2028, DreamLu 卢春梦 (qq596392912@gmail.com).
|
||||
* <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 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());
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package org.springblade.core.api.crypto.enums;
|
||||
|
||||
/**
|
||||
* <p>加密方式</p>
|
||||
*
|
||||
* @author licoy.cn, L.cm
|
||||
*/
|
||||
public enum CryptoType {
|
||||
|
||||
/**
|
||||
* des
|
||||
*/
|
||||
DES,
|
||||
|
||||
/**
|
||||
* aes
|
||||
*/
|
||||
AES
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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. (加密方式未定义)");
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -0,0 +1,117 @@
|
||||
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.AesUtil;
|
||||
import org.springblade.core.tool.utils.ClassUtil;
|
||||
import org.springblade.core.tool.utils.DesUtil;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* <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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<groupId>org.springblade</groupId>
|
||||
<version>3.4.1</version>
|
||||
<version>3.5.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<groupId>org.springblade</groupId>
|
||||
<version>3.4.1</version>
|
||||
<version>3.5.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@ -40,5 +40,4 @@
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
</project>
|
||||
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<groupId>org.springblade</groupId>
|
||||
<version>3.4.1</version>
|
||||
<version>3.5.0</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -25,7 +25,7 @@ public interface AppConstant {
|
||||
/**
|
||||
* 应用版本
|
||||
*/
|
||||
String APPLICATION_VERSION = "3.4.1";
|
||||
String APPLICATION_VERSION = "3.5.0";
|
||||
|
||||
/**
|
||||
* 基础包
|
||||
|
@ -22,7 +22,6 @@ package org.springblade.core.launch.constant;
|
||||
*/
|
||||
public interface TokenConstant {
|
||||
|
||||
String SIGN_KEY = "bladexisapowerfulmicroservicearchitectureupgradedandoptimizedfromacommercialproject";
|
||||
String AVATAR = "avatar";
|
||||
String HEADER = "blade-auth";
|
||||
String BEARER = "bearer";
|
||||
@ -44,4 +43,13 @@ public interface TokenConstant {
|
||||
String DEFAULT_AVATAR = "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png";
|
||||
Integer AUTH_LENGTH = 7;
|
||||
|
||||
/**
|
||||
* token签名
|
||||
*/
|
||||
String SIGN_KEY = "bladexisapowerfulmicroservicearchitectureupgradedandoptimizedfromacommercialproject";
|
||||
/**
|
||||
* key安全长度,具体见:https://tools.ietf.org/html/rfc7518#section-3.2
|
||||
*/
|
||||
int SIGN_KEY_LENGTH = 32;
|
||||
|
||||
}
|
||||
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<groupId>org.springblade</groupId>
|
||||
<version>3.4.1</version>
|
||||
<version>3.5.0</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<groupId>org.springblade</groupId>
|
||||
<version>3.4.1</version>
|
||||
<version>3.5.0</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
@ -39,5 +39,4 @@
|
||||
<version>${mybatis.plus.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
@ -19,6 +19,7 @@ package org.springblade.core.log.config;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springblade.core.log.error.BladeErrorAttributes;
|
||||
import org.springblade.core.log.error.BladeErrorController;
|
||||
import org.springblade.core.log.props.BladeLogProperties;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
@ -28,6 +29,7 @@ import org.springframework.boot.autoconfigure.condition.SearchStrategy;
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||
import org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController;
|
||||
import org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.web.servlet.error.DefaultErrorAttributes;
|
||||
import org.springframework.boot.web.servlet.error.ErrorAttributes;
|
||||
import org.springframework.boot.web.servlet.error.ErrorController;
|
||||
@ -45,15 +47,18 @@ import javax.servlet.Servlet;
|
||||
@AllArgsConstructor
|
||||
@ConditionalOnWebApplication
|
||||
@AutoConfigureBefore(ErrorMvcAutoConfiguration.class)
|
||||
@EnableConfigurationProperties(BladeLogProperties.class)
|
||||
@ConditionalOnClass({Servlet.class, DispatcherServlet.class})
|
||||
public class BladeErrorMvcAutoConfiguration {
|
||||
|
||||
private final ServerProperties serverProperties;
|
||||
private final BladeLogProperties bladeLogProperties;
|
||||
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(value = ErrorAttributes.class, search = SearchStrategy.CURRENT)
|
||||
public DefaultErrorAttributes errorAttributes() {
|
||||
return new BladeErrorAttributes();
|
||||
return new BladeErrorAttributes(bladeLogProperties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
@ -25,8 +25,11 @@ import org.springblade.core.log.event.ErrorLogListener;
|
||||
import org.springblade.core.log.event.UsualLogListener;
|
||||
import org.springblade.core.log.feign.ILogClient;
|
||||
import org.springblade.core.log.logger.BladeLogger;
|
||||
import org.springblade.core.log.props.BladeLogProperties;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
/**
|
||||
@ -44,28 +47,33 @@ public class BladeLogToolAutoConfiguration {
|
||||
private final BladeProperties bladeProperties;
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(value = BladeLogProperties.PREFIX + "api.enabled", havingValue = "true", matchIfMissing = true)
|
||||
public ApiLogAspect apiLogAspect() {
|
||||
return new ApiLogAspect();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public BladeLogger bladeLogger() {
|
||||
return new BladeLogger();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(value = BladeLogProperties.PREFIX + "api.enabled", havingValue = "true", matchIfMissing = true)
|
||||
public ApiLogListener apiLogListener() {
|
||||
return new ApiLogListener(logService, serverInfo, bladeProperties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(value = BladeLogProperties.PREFIX + "error.enabled", havingValue = "true", matchIfMissing = true)
|
||||
public ErrorLogListener errorEventListener() {
|
||||
return new ErrorLogListener(logService, serverInfo, bladeProperties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(value = BladeLogProperties.PREFIX + "usual.enabled", havingValue = "true", matchIfMissing = true)
|
||||
public UsualLogListener bladeEventListener() {
|
||||
return new UsualLogListener(logService, serverInfo, bladeProperties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(value = BladeLogProperties.PREFIX + "usual.enabled", havingValue = "true", matchIfMissing = true)
|
||||
public BladeLogger bladeLogger() {
|
||||
return new BladeLogger();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -15,7 +15,9 @@
|
||||
*/
|
||||
package org.springblade.core.log.error;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.core.log.props.BladeLogProperties;
|
||||
import org.springblade.core.log.publisher.ErrorLogPublisher;
|
||||
import org.springblade.core.tool.api.R;
|
||||
import org.springblade.core.tool.api.ResultCode;
|
||||
@ -34,7 +36,9 @@ import java.util.Map;
|
||||
* @author Chill
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class BladeErrorAttributes extends DefaultErrorAttributes {
|
||||
private final BladeLogProperties bladeLogProperties;
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getErrorAttributes(WebRequest webRequest, ErrorAttributeOptions options) {
|
||||
@ -50,7 +54,9 @@ public class BladeErrorAttributes extends DefaultErrorAttributes {
|
||||
result = R.fail(status, error.getMessage());
|
||||
}
|
||||
//发送服务异常事件
|
||||
ErrorLogPublisher.publishEvent(error, requestUri);
|
||||
if (bladeLogProperties.getError()) {
|
||||
ErrorLogPublisher.publishEvent(error, requestUri);
|
||||
}
|
||||
return BeanUtil.toMap(result);
|
||||
}
|
||||
|
||||
|
@ -15,9 +15,11 @@
|
||||
*/
|
||||
package org.springblade.core.log.error;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.hibernate.validator.internal.engine.path.PathImpl;
|
||||
import org.springblade.core.log.exception.ServiceException;
|
||||
import org.springblade.core.log.props.BladeLogProperties;
|
||||
import org.springblade.core.log.publisher.ErrorLogPublisher;
|
||||
import org.springblade.core.secure.exception.SecureException;
|
||||
import org.springblade.core.tool.api.R;
|
||||
@ -59,8 +61,11 @@ import java.util.Set;
|
||||
@ConditionalOnClass({Servlet.class, DispatcherServlet.class})
|
||||
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
|
||||
@RestControllerAdvice
|
||||
@RequiredArgsConstructor
|
||||
public class BladeRestExceptionTranslator {
|
||||
|
||||
private final BladeLogProperties bladeLogProperties;
|
||||
|
||||
@ExceptionHandler(MissingServletRequestParameterException.class)
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
public R handleError(MissingServletRequestParameterException e) {
|
||||
@ -155,7 +160,9 @@ public class BladeRestExceptionTranslator {
|
||||
public R handleError(Throwable e) {
|
||||
log.error("服务器异常", e);
|
||||
//发送服务异常事件
|
||||
ErrorLogPublisher.publishEvent(e, UrlUtil.getPath(WebUtil.getRequest().getRequestURI()));
|
||||
if (bladeLogProperties.getError()) {
|
||||
ErrorLogPublisher.publishEvent(e, UrlUtil.getPath(WebUtil.getRequest().getRequestURI()));
|
||||
}
|
||||
return R.fail(ResultCode.INTERNAL_SERVER_ERROR, (Func.isEmpty(e.getMessage()) ? ResultCode.INTERNAL_SERVER_ERROR.getMessage() : e.getMessage()));
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
|
||||
* <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 org.springblade.core.log.props;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* 异步配置
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@ConfigurationProperties(BladeLogProperties.PREFIX)
|
||||
public class BladeLogProperties {
|
||||
/**
|
||||
* 前缀
|
||||
*/
|
||||
public static final String PREFIX = "blade.log";
|
||||
|
||||
/**
|
||||
* 是否开启 api 日志
|
||||
*/
|
||||
private Boolean api = Boolean.TRUE;
|
||||
/**
|
||||
* 是否开启 error 日志
|
||||
*/
|
||||
private Boolean error = Boolean.TRUE;
|
||||
/**
|
||||
* 是否开启 usual 日志
|
||||
*/
|
||||
private Boolean usual = Boolean.TRUE;
|
||||
}
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<groupId>org.springblade</groupId>
|
||||
<version>3.4.1</version>
|
||||
<version>3.5.0</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -1,18 +1,17 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2028, DreamLu All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the dreamlu.net developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: DreamLu 卢春梦 (596392912@qq.com)
|
||||
/**
|
||||
* Copyright (c) 2018-2028, DreamLu 卢春梦 (qq596392912@gmail.com).
|
||||
* <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 org.springblade.core.mp.intercept;
|
||||
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<groupId>org.springblade</groupId>
|
||||
<version>3.4.1</version>
|
||||
<version>3.5.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@ -21,17 +21,23 @@
|
||||
<artifactId>blade-core-tool</artifactId>
|
||||
<version>${blade.tool.version}</version>
|
||||
</dependency>
|
||||
<!--Aliyun-->
|
||||
<dependency>
|
||||
<groupId>com.aliyun.oss</groupId>
|
||||
<artifactId>aliyun-sdk-oss</artifactId>
|
||||
<version>3.14.0</version>
|
||||
</dependency>
|
||||
<!--MinIO-->
|
||||
<dependency>
|
||||
<groupId>io.minio</groupId>
|
||||
<artifactId>minio</artifactId>
|
||||
<version>8.3.7</version>
|
||||
</dependency>
|
||||
<!--QiNiu-->
|
||||
<dependency>
|
||||
<groupId>com.qiniu</groupId>
|
||||
<artifactId>qiniu-java-sdk</artifactId>
|
||||
<version>7.2.18</version>
|
||||
</dependency>
|
||||
<!--alioss-->
|
||||
<dependency>
|
||||
<groupId>com.aliyun.oss</groupId>
|
||||
<artifactId>aliyun-sdk-oss</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<version>7.9.4</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
@ -45,11 +45,12 @@ import java.util.Map;
|
||||
* @author Chill
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public class AliossTemplate {
|
||||
private OSSClient ossClient;
|
||||
private OssProperties ossProperties;
|
||||
private OssRule ossRule;
|
||||
public class AliossTemplate implements OssTemplate {
|
||||
private final OSSClient ossClient;
|
||||
private final OssProperties ossProperties;
|
||||
private final OssRule ossRule;
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void makeBucket(String bucketName) {
|
||||
if (!bucketExists(bucketName)) {
|
||||
@ -57,31 +58,37 @@ public class AliossTemplate {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void removeBucket(String bucketName) {
|
||||
ossClient.deleteBucket(getBucketName(bucketName));
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public boolean bucketExists(String bucketName) {
|
||||
return ossClient.doesBucketExist(getBucketName(bucketName));
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void copyFile(String bucketName, String fileName, String destBucketName) {
|
||||
ossClient.copyObject(getBucketName(bucketName), fileName, getBucketName(destBucketName), fileName);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void copyFile(String bucketName, String fileName, String destBucketName, String destFileName) {
|
||||
ossClient.copyObject(getBucketName(bucketName), fileName, getBucketName(destBucketName), destFileName);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public OssFile statFile(String fileName) {
|
||||
return statFile(ossProperties.getBucketName(), fileName);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public OssFile statFile(String bucketName, String fileName) {
|
||||
ObjectMetadata stat = ossClient.getObjectMetadata(getBucketName(bucketName), fileName);
|
||||
@ -95,46 +102,55 @@ public class AliossTemplate {
|
||||
return ossFile;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public String filePath(String fileName) {
|
||||
return getOssHost().concat(StringPool.SLASH).concat(fileName);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public String filePath(String bucketName, String fileName) {
|
||||
return getOssHost(bucketName).concat(StringPool.SLASH).concat(fileName);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public String fileLink(String fileName) {
|
||||
return getOssHost().concat(StringPool.SLASH).concat(fileName);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public String fileLink(String bucketName, String fileName) {
|
||||
return getOssHost(bucketName).concat(StringPool.SLASH).concat(fileName);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public BladeFile putFile(MultipartFile file) {
|
||||
return putFile(ossProperties.getBucketName(), file.getOriginalFilename(), file);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public BladeFile putFile(String fileName, MultipartFile file) {
|
||||
return putFile(ossProperties.getBucketName(), fileName, file);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public BladeFile putFile(String bucketName, String fileName, MultipartFile file) {
|
||||
return putFile(bucketName, fileName, file.getInputStream());
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public BladeFile putFile(String fileName, InputStream stream) {
|
||||
return putFile(ossProperties.getBucketName(), fileName, stream);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public BladeFile putFile(String bucketName, String fileName, InputStream stream) {
|
||||
return put(bucketName, stream, fileName, false);
|
||||
@ -160,25 +176,30 @@ public class AliossTemplate {
|
||||
BladeFile file = new BladeFile();
|
||||
file.setOriginalName(originalName);
|
||||
file.setName(key);
|
||||
file.setDomain(getOssHost(bucketName));
|
||||
file.setLink(fileLink(bucketName, key));
|
||||
return file;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void removeFile(String fileName) {
|
||||
ossClient.deleteObject(getBucketName(), fileName);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void removeFile(String bucketName, String fileName) {
|
||||
ossClient.deleteObject(getBucketName(bucketName), fileName);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void removeFiles(List<String> fileNames) {
|
||||
fileNames.forEach(this::removeFile);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void removeFiles(String bucketName, List<String> fileNames) {
|
||||
fileNames.forEach(fileName -> removeFile(getBucketName(bucketName), fileName));
|
||||
@ -227,11 +248,6 @@ public class AliossTemplate {
|
||||
return getUploadToken(bucketName, ossProperties.getArgs().get("expireTime", 3600L));
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO 上传大小限制、基础路径
|
||||
* <p>
|
||||
* 获取上传凭证,普通上传
|
||||
*/
|
||||
public String getUploadToken(String bucketName, long expireTime) {
|
||||
String baseDir = "upload";
|
||||
|
||||
@ -258,11 +274,22 @@ public class AliossTemplate {
|
||||
return JsonUtil.toJson(respMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取域名
|
||||
*
|
||||
* @param bucketName 存储桶名称
|
||||
* @return String
|
||||
*/
|
||||
public String getOssHost(String bucketName) {
|
||||
String prefix = ossProperties.getEndpoint().contains("https://") ? "https://" : "http://";
|
||||
return prefix + getBucketName(bucketName) + StringPool.DOT + ossProperties.getEndpoint().replaceFirst(prefix, StringPool.EMPTY);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取域名
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public String getOssHost() {
|
||||
return getOssHost(ossProperties.getBucketName());
|
||||
}
|
||||
|
@ -0,0 +1,417 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
|
||||
* <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 org.springblade.core.oss;
|
||||
|
||||
import io.minio.*;
|
||||
import io.minio.http.Method;
|
||||
import io.minio.messages.Bucket;
|
||||
import io.minio.messages.DeleteObject;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import org.springblade.core.oss.enums.PolicyType;
|
||||
import org.springblade.core.oss.model.BladeFile;
|
||||
import org.springblade.core.oss.model.OssFile;
|
||||
import org.springblade.core.oss.props.OssProperties;
|
||||
import org.springblade.core.oss.rule.OssRule;
|
||||
import org.springblade.core.tool.utils.DateUtil;
|
||||
import org.springblade.core.tool.utils.Func;
|
||||
import org.springblade.core.tool.utils.StringPool;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* MinIOTemplate
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public class MinioTemplate implements OssTemplate {
|
||||
|
||||
/**
|
||||
* MinIO客户端
|
||||
*/
|
||||
private final MinioClient client;
|
||||
|
||||
/**
|
||||
* 存储桶命名规则
|
||||
*/
|
||||
private final OssRule ossRule;
|
||||
|
||||
/**
|
||||
* 配置类
|
||||
*/
|
||||
private final OssProperties ossProperties;
|
||||
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void makeBucket(String bucketName) {
|
||||
if (
|
||||
!client.bucketExists(
|
||||
BucketExistsArgs.builder().bucket(getBucketName(bucketName)).build()
|
||||
)
|
||||
) {
|
||||
client.makeBucket(
|
||||
MakeBucketArgs.builder().bucket(getBucketName(bucketName)).build()
|
||||
);
|
||||
client.setBucketPolicy(
|
||||
SetBucketPolicyArgs.builder().bucket(getBucketName(bucketName)).config(getPolicyType(getBucketName(bucketName), PolicyType.READ)).build()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public Bucket getBucket() {
|
||||
return getBucket(getBucketName());
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public Bucket getBucket(String bucketName) {
|
||||
Optional<Bucket> bucketOptional = client.listBuckets().stream().filter(bucket -> bucket.name().equals(getBucketName(bucketName))).findFirst();
|
||||
return bucketOptional.orElse(null);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public List<Bucket> listBuckets() {
|
||||
return client.listBuckets();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void removeBucket(String bucketName) {
|
||||
client.removeBucket(
|
||||
RemoveBucketArgs.builder().bucket(getBucketName(bucketName)).build()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public boolean bucketExists(String bucketName) {
|
||||
return client.bucketExists(
|
||||
BucketExistsArgs.builder().bucket(getBucketName(bucketName)).build()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void copyFile(String bucketName, String fileName, String destBucketName) {
|
||||
copyFile(bucketName, fileName, destBucketName, fileName);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void copyFile(String bucketName, String fileName, String destBucketName, String destFileName) {
|
||||
client.copyObject(
|
||||
CopyObjectArgs.builder()
|
||||
.source(CopySource.builder().bucket(getBucketName(bucketName)).object(fileName).build())
|
||||
.bucket(getBucketName(destBucketName))
|
||||
.object(destFileName)
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public OssFile statFile(String fileName) {
|
||||
return statFile(ossProperties.getBucketName(), fileName);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public OssFile statFile(String bucketName, String fileName) {
|
||||
StatObjectResponse stat = client.statObject(
|
||||
StatObjectArgs.builder().bucket(getBucketName(bucketName)).object(fileName).build()
|
||||
);
|
||||
OssFile ossFile = new OssFile();
|
||||
ossFile.setName(Func.isEmpty(stat.object()) ? fileName : stat.object());
|
||||
ossFile.setLink(fileLink(ossFile.getName()));
|
||||
ossFile.setHash(String.valueOf(stat.hashCode()));
|
||||
ossFile.setLength(stat.size());
|
||||
ossFile.setPutTime(DateUtil.toDate(stat.lastModified().toLocalDateTime()));
|
||||
ossFile.setContentType(stat.contentType());
|
||||
return ossFile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String filePath(String fileName) {
|
||||
return getBucketName().concat(StringPool.SLASH).concat(fileName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String filePath(String bucketName, String fileName) {
|
||||
return getBucketName(bucketName).concat(StringPool.SLASH).concat(fileName);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public String fileLink(String fileName) {
|
||||
return ossProperties.getEndpoint().concat(StringPool.SLASH).concat(getBucketName()).concat(StringPool.SLASH).concat(fileName);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public String fileLink(String bucketName, String fileName) {
|
||||
return ossProperties.getEndpoint().concat(StringPool.SLASH).concat(getBucketName(bucketName)).concat(StringPool.SLASH).concat(fileName);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public BladeFile putFile(MultipartFile file) {
|
||||
return putFile(ossProperties.getBucketName(), file.getOriginalFilename(), file);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public BladeFile putFile(String fileName, MultipartFile file) {
|
||||
return putFile(ossProperties.getBucketName(), fileName, file);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public BladeFile putFile(String bucketName, String fileName, MultipartFile file) {
|
||||
return putFile(bucketName, file.getOriginalFilename(), file.getInputStream());
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public BladeFile putFile(String fileName, InputStream stream) {
|
||||
return putFile(ossProperties.getBucketName(), fileName, stream);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public BladeFile putFile(String bucketName, String fileName, InputStream stream) {
|
||||
return putFile(bucketName, fileName, stream, "application/octet-stream");
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public BladeFile putFile(String bucketName, String fileName, InputStream stream, String contentType) {
|
||||
makeBucket(bucketName);
|
||||
String originalName = fileName;
|
||||
fileName = getFileName(fileName);
|
||||
client.putObject(
|
||||
PutObjectArgs.builder()
|
||||
.bucket(getBucketName(bucketName))
|
||||
.object(fileName)
|
||||
.stream(stream, stream.available(), -1)
|
||||
.contentType(contentType)
|
||||
.build()
|
||||
);
|
||||
BladeFile file = new BladeFile();
|
||||
file.setOriginalName(originalName);
|
||||
file.setName(fileName);
|
||||
file.setDomain(getOssHost(bucketName));
|
||||
file.setLink(fileLink(bucketName, fileName));
|
||||
return file;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void removeFile(String fileName) {
|
||||
removeFile(ossProperties.getBucketName(), fileName);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void removeFile(String bucketName, String fileName) {
|
||||
client.removeObject(
|
||||
RemoveObjectArgs.builder().bucket(getBucketName(bucketName)).object(fileName).build()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void removeFiles(List<String> fileNames) {
|
||||
removeFiles(ossProperties.getBucketName(), fileNames);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void removeFiles(String bucketName, List<String> fileNames) {
|
||||
Stream<DeleteObject> stream = fileNames.stream().map(DeleteObject::new);
|
||||
client.removeObjects(RemoveObjectsArgs.builder().bucket(getBucketName(bucketName)).objects(stream::iterator).build());
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据规则生成存储桶名称规则
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
private String getBucketName() {
|
||||
return getBucketName(ossProperties.getBucketName());
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据规则生成存储桶名称规则
|
||||
*
|
||||
* @param bucketName 存储桶名称
|
||||
* @return String
|
||||
*/
|
||||
private String getBucketName(String bucketName) {
|
||||
return ossRule.bucketName(bucketName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据规则生成文件名称规则
|
||||
*
|
||||
* @param originalFilename 原始文件名
|
||||
* @return string
|
||||
*/
|
||||
private String getFileName(String originalFilename) {
|
||||
return ossRule.fileName(originalFilename);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件外链
|
||||
*
|
||||
* @param bucketName bucket名称
|
||||
* @param fileName 文件名称
|
||||
* @param expires 过期时间
|
||||
* @return url
|
||||
*/
|
||||
@SneakyThrows
|
||||
public String getPresignedObjectUrl(String bucketName, String fileName, Integer expires) {
|
||||
return client.getPresignedObjectUrl(
|
||||
GetPresignedObjectUrlArgs.builder()
|
||||
.method(Method.GET)
|
||||
.bucket(getBucketName(bucketName))
|
||||
.object(fileName)
|
||||
.expiry(expires)
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取存储桶策略
|
||||
*
|
||||
* @param policyType 策略枚举
|
||||
* @return String
|
||||
*/
|
||||
public String getPolicyType(PolicyType policyType) {
|
||||
return getPolicyType(getBucketName(), policyType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取存储桶策略
|
||||
*
|
||||
* @param bucketName 存储桶名称
|
||||
* @param policyType 策略枚举
|
||||
* @return String
|
||||
*/
|
||||
public static String getPolicyType(String bucketName, PolicyType policyType) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("{\n");
|
||||
builder.append(" \"Statement\": [\n");
|
||||
builder.append(" {\n");
|
||||
builder.append(" \"Action\": [\n");
|
||||
|
||||
switch (policyType) {
|
||||
case WRITE:
|
||||
builder.append(" \"s3:GetBucketLocation\",\n");
|
||||
builder.append(" \"s3:ListBucketMultipartUploads\"\n");
|
||||
break;
|
||||
case READ_WRITE:
|
||||
builder.append(" \"s3:GetBucketLocation\",\n");
|
||||
builder.append(" \"s3:ListBucket\",\n");
|
||||
builder.append(" \"s3:ListBucketMultipartUploads\"\n");
|
||||
break;
|
||||
default:
|
||||
builder.append(" \"s3:GetBucketLocation\"\n");
|
||||
break;
|
||||
}
|
||||
|
||||
builder.append(" ],\n");
|
||||
builder.append(" \"Effect\": \"Allow\",\n");
|
||||
builder.append(" \"Principal\": \"*\",\n");
|
||||
builder.append(" \"Resource\": \"arn:aws:s3:::");
|
||||
builder.append(bucketName);
|
||||
builder.append("\"\n");
|
||||
builder.append(" },\n");
|
||||
if (PolicyType.READ.equals(policyType)) {
|
||||
builder.append(" {\n");
|
||||
builder.append(" \"Action\": [\n");
|
||||
builder.append(" \"s3:ListBucket\"\n");
|
||||
builder.append(" ],\n");
|
||||
builder.append(" \"Effect\": \"Deny\",\n");
|
||||
builder.append(" \"Principal\": \"*\",\n");
|
||||
builder.append(" \"Resource\": \"arn:aws:s3:::");
|
||||
builder.append(bucketName);
|
||||
builder.append("\"\n");
|
||||
builder.append(" },\n");
|
||||
|
||||
}
|
||||
builder.append(" {\n");
|
||||
builder.append(" \"Action\": ");
|
||||
|
||||
switch (policyType) {
|
||||
case WRITE:
|
||||
builder.append("[\n");
|
||||
builder.append(" \"s3:AbortMultipartUpload\",\n");
|
||||
builder.append(" \"s3:DeleteObject\",\n");
|
||||
builder.append(" \"s3:ListMultipartUploadParts\",\n");
|
||||
builder.append(" \"s3:PutObject\"\n");
|
||||
builder.append(" ],\n");
|
||||
break;
|
||||
case READ_WRITE:
|
||||
builder.append("[\n");
|
||||
builder.append(" \"s3:AbortMultipartUpload\",\n");
|
||||
builder.append(" \"s3:DeleteObject\",\n");
|
||||
builder.append(" \"s3:GetObject\",\n");
|
||||
builder.append(" \"s3:ListMultipartUploadParts\",\n");
|
||||
builder.append(" \"s3:PutObject\"\n");
|
||||
builder.append(" ],\n");
|
||||
break;
|
||||
default:
|
||||
builder.append("\"s3:GetObject\",\n");
|
||||
break;
|
||||
}
|
||||
|
||||
builder.append(" \"Effect\": \"Allow\",\n");
|
||||
builder.append(" \"Principal\": \"*\",\n");
|
||||
builder.append(" \"Resource\": \"arn:aws:s3:::");
|
||||
builder.append(bucketName);
|
||||
builder.append("/*\"\n");
|
||||
builder.append(" }\n");
|
||||
builder.append(" ],\n");
|
||||
builder.append(" \"Version\": \"2012-10-17\"\n");
|
||||
builder.append("}\n");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取域名
|
||||
*
|
||||
* @param bucketName 存储桶名称
|
||||
* @return String
|
||||
*/
|
||||
public String getOssHost(String bucketName) {
|
||||
return ossProperties.getEndpoint() + StringPool.SLASH + getBucketName(bucketName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取域名
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public String getOssHost() {
|
||||
return getOssHost(ossProperties.getBucketName());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,202 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
|
||||
* <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 org.springblade.core.oss;
|
||||
|
||||
|
||||
import org.springblade.core.oss.model.BladeFile;
|
||||
import org.springblade.core.oss.model.OssFile;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* OssTemplate抽象API
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public interface OssTemplate {
|
||||
|
||||
/**
|
||||
* 创建 存储桶
|
||||
*
|
||||
* @param bucketName 存储桶名称
|
||||
*/
|
||||
void makeBucket(String bucketName);
|
||||
|
||||
/**
|
||||
* 删除 存储桶
|
||||
*
|
||||
* @param bucketName 存储桶名称
|
||||
*/
|
||||
void removeBucket(String bucketName);
|
||||
|
||||
/**
|
||||
* 存储桶是否存在
|
||||
*
|
||||
* @param bucketName 存储桶名称
|
||||
* @return boolean
|
||||
*/
|
||||
boolean bucketExists(String bucketName);
|
||||
|
||||
/**
|
||||
* 拷贝文件
|
||||
*
|
||||
* @param bucketName 存储桶名称
|
||||
* @param fileName 存储桶文件名称
|
||||
* @param destBucketName 目标存储桶名称
|
||||
*/
|
||||
void copyFile(String bucketName, String fileName, String destBucketName);
|
||||
|
||||
/**
|
||||
* 拷贝文件
|
||||
*
|
||||
* @param bucketName 存储桶名称
|
||||
* @param fileName 存储桶文件名称
|
||||
* @param destBucketName 目标存储桶名称
|
||||
* @param destFileName 目标存储桶文件名称
|
||||
*/
|
||||
void copyFile(String bucketName, String fileName, String destBucketName, String destFileName);
|
||||
|
||||
/**
|
||||
* 获取文件信息
|
||||
*
|
||||
* @param fileName 存储桶文件名称
|
||||
* @return InputStream
|
||||
*/
|
||||
OssFile statFile(String fileName);
|
||||
|
||||
/**
|
||||
* 获取文件信息
|
||||
*
|
||||
* @param bucketName 存储桶名称
|
||||
* @param fileName 存储桶文件名称
|
||||
* @return InputStream
|
||||
*/
|
||||
OssFile statFile(String bucketName, String fileName);
|
||||
|
||||
/**
|
||||
* 获取文件相对路径
|
||||
*
|
||||
* @param fileName 存储桶对象名称
|
||||
* @return String
|
||||
*/
|
||||
String filePath(String fileName);
|
||||
|
||||
/**
|
||||
* 获取文件相对路径
|
||||
*
|
||||
* @param bucketName 存储桶名称
|
||||
* @param fileName 存储桶对象名称
|
||||
* @return String
|
||||
*/
|
||||
String filePath(String bucketName, String fileName);
|
||||
|
||||
/**
|
||||
* 获取文件地址
|
||||
*
|
||||
* @param fileName 存储桶对象名称
|
||||
* @return String
|
||||
*/
|
||||
String fileLink(String fileName);
|
||||
|
||||
/**
|
||||
* 获取文件地址
|
||||
*
|
||||
* @param bucketName 存储桶名称
|
||||
* @param fileName 存储桶对象名称
|
||||
* @return String
|
||||
*/
|
||||
String fileLink(String bucketName, String fileName);
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
*
|
||||
* @param file 上传文件类
|
||||
* @return BladeFile
|
||||
*/
|
||||
BladeFile putFile(MultipartFile file);
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
*
|
||||
* @param file 上传文件类
|
||||
* @param fileName 上传文件名
|
||||
* @return BladeFile
|
||||
*/
|
||||
BladeFile putFile(String fileName, MultipartFile file);
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
*
|
||||
* @param bucketName 存储桶名称
|
||||
* @param fileName 上传文件名
|
||||
* @param file 上传文件类
|
||||
* @return BladeFile
|
||||
*/
|
||||
BladeFile putFile(String bucketName, String fileName, MultipartFile file);
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
*
|
||||
* @param fileName 存储桶对象名称
|
||||
* @param stream 文件流
|
||||
* @return BladeFile
|
||||
*/
|
||||
BladeFile putFile(String fileName, InputStream stream);
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
*
|
||||
* @param bucketName 存储桶名称
|
||||
* @param fileName 存储桶对象名称
|
||||
* @param stream 文件流
|
||||
* @return BladeFile
|
||||
*/
|
||||
BladeFile putFile(String bucketName, String fileName, InputStream stream);
|
||||
|
||||
/**
|
||||
* 删除文件
|
||||
*
|
||||
* @param fileName 存储桶对象名称
|
||||
*/
|
||||
void removeFile(String fileName);
|
||||
|
||||
/**
|
||||
* 删除文件
|
||||
*
|
||||
* @param bucketName 存储桶名称
|
||||
* @param fileName 存储桶对象名称
|
||||
*/
|
||||
void removeFile(String bucketName, String fileName);
|
||||
|
||||
/**
|
||||
* 批量删除文件
|
||||
*
|
||||
* @param fileNames 存储桶对象名称集合
|
||||
*/
|
||||
void removeFiles(List<String> fileNames);
|
||||
|
||||
/**
|
||||
* 批量删除文件
|
||||
*
|
||||
* @param bucketName 存储桶名称
|
||||
* @param fileNames 存储桶对象名称集合
|
||||
*/
|
||||
void removeFiles(String bucketName, List<String> fileNames);
|
||||
|
||||
|
||||
}
|
@ -33,8 +33,6 @@ import org.springblade.core.tool.utils.StringPool;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
@ -44,57 +42,56 @@ import java.util.List;
|
||||
* @author Chill
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public class QiniuTemplate {
|
||||
private Auth auth;
|
||||
private UploadManager uploadManager;
|
||||
private BucketManager bucketManager;
|
||||
private OssProperties ossProperties;
|
||||
private OssRule ossRule;
|
||||
|
||||
public class QiniuTemplate implements OssTemplate {
|
||||
private final Auth auth;
|
||||
private final UploadManager uploadManager;
|
||||
private final BucketManager bucketManager;
|
||||
private final OssProperties ossProperties;
|
||||
private final OssRule ossRule;
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void makeBucket(String bucketName) {
|
||||
if (!CollectionUtil.contains(bucketManager.buckets(), getBucketName(bucketName))) {
|
||||
bucketManager.createBucket(getBucketName(bucketName), Zone.zone0().getRegion());
|
||||
bucketManager.createBucket(getBucketName(bucketName), Zone.autoZone().getRegion());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void removeBucket(String bucketName) {
|
||||
bucketManager.deleteBucket(getBucketName(bucketName));
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public boolean bucketExists(String bucketName) {
|
||||
return CollectionUtil.contains(bucketManager.buckets(), getBucketName(bucketName));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void copyFile(String bucketName, String fileName, String destBucketName) {
|
||||
bucketManager.copy(getBucketName(bucketName), fileName, getBucketName(destBucketName), fileName);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void copyFile(String bucketName, String fileName, String destBucketName, String destFileName) {
|
||||
bucketManager.copy(getBucketName(bucketName), fileName, getBucketName(destBucketName), destFileName);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public OssFile statFile(String fileName) {
|
||||
return statFile(ossProperties.getBucketName(), fileName);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public OssFile statFile(String bucketName, String fileName) {
|
||||
FileInfo stat = bucketManager.stat(getBucketName(bucketName), fileName);
|
||||
OssFile ossFile = new OssFile();
|
||||
ossFile.setName(stat.key);
|
||||
ossFile.setName(Func.isEmpty(stat.key) ? fileName : stat.key);
|
||||
ossFile.setLink(fileLink(ossFile.getName()));
|
||||
ossFile.setHash(stat.hash);
|
||||
@ -104,80 +101,55 @@ public class QiniuTemplate {
|
||||
return ossFile;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public String filePath(String fileName) {
|
||||
return getBucketName().concat(StringPool.SLASH).concat(fileName);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public String filePath(String bucketName, String fileName) {
|
||||
return getBucketName(bucketName).concat(StringPool.SLASH).concat(fileName);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public String fileLink(String fileName) {
|
||||
return ossProperties.getEndpoint().concat(StringPool.SLASH).concat(fileName);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public String fileLink(String bucketName, String fileName) {
|
||||
return ossProperties.getEndpoint().concat(StringPool.SLASH).concat(fileName);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取文件公开链接
|
||||
*
|
||||
* @param fileName 文件名
|
||||
* @return 文件公开链接
|
||||
*/
|
||||
public String publicFileLink(String fileName) {
|
||||
return String.format("%s/%s", ossProperties.getEndpoint(), fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件私有链接
|
||||
*
|
||||
* @param fileName 文件名
|
||||
* @param expireTime 超时时间
|
||||
* @return 私有文件链接
|
||||
*/
|
||||
@SneakyThrows
|
||||
public String privateFileLink(String fileName, Long expireTime) {
|
||||
String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.name()).replace("+", "%20");
|
||||
String publicUrl = String.format("%s/%s", ossProperties.getEndpoint(), encodedFileName);
|
||||
return auth.privateDownloadUrl(publicUrl, expireTime);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public BladeFile putFile(MultipartFile file) {
|
||||
return putFile(ossProperties.getBucketName(), file.getOriginalFilename(), file);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public BladeFile putFile(String fileName, MultipartFile file) {
|
||||
return putFile(ossProperties.getBucketName(), fileName, file);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public BladeFile putFile(String bucketName, String fileName, MultipartFile file) {
|
||||
return putFile(bucketName, fileName, file);
|
||||
return putFile(bucketName, fileName, file.getInputStream());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public BladeFile putFile(String fileName, InputStream stream) {
|
||||
return putFile(ossProperties.getBucketName(), fileName, stream);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public BladeFile putFile(String bucketName, String fileName, InputStream stream) {
|
||||
return put(bucketName, stream, fileName, false);
|
||||
@ -185,9 +157,8 @@ public class QiniuTemplate {
|
||||
|
||||
@SneakyThrows
|
||||
public BladeFile put(String bucketName, InputStream stream, String key, boolean cover) {
|
||||
BladeFile file = new BladeFile();
|
||||
file.setOriginalName(key);
|
||||
makeBucket(bucketName);
|
||||
String originalName = key;
|
||||
key = getFileName(key);
|
||||
// 覆盖上传
|
||||
if (cover) {
|
||||
@ -201,30 +172,33 @@ public class QiniuTemplate {
|
||||
retry++;
|
||||
}
|
||||
}
|
||||
BladeFile file = new BladeFile();
|
||||
file.setOriginalName(originalName);
|
||||
file.setName(key);
|
||||
file.setDomain(getOssHost());
|
||||
file.setLink(fileLink(bucketName, key));
|
||||
return file;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void removeFile(String fileName) {
|
||||
bucketManager.delete(getBucketName(), fileName);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void removeFile(String bucketName, String fileName) {
|
||||
bucketManager.delete(getBucketName(bucketName), fileName);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void removeFiles(List<String> fileNames) {
|
||||
fileNames.forEach(this::removeFile);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void removeFiles(String bucketName, List<String> fileNames) {
|
||||
fileNames.forEach(fileName -> removeFile(getBucketName(bucketName), fileName));
|
||||
@ -261,6 +235,9 @@ public class QiniuTemplate {
|
||||
|
||||
/**
|
||||
* 获取上传凭证,普通上传
|
||||
*
|
||||
* @param bucketName 存储桶名称
|
||||
* @return string
|
||||
*/
|
||||
public String getUploadToken(String bucketName) {
|
||||
return auth.uploadToken(getBucketName(bucketName));
|
||||
@ -268,9 +245,22 @@ public class QiniuTemplate {
|
||||
|
||||
/**
|
||||
* 获取上传凭证,覆盖上传
|
||||
*
|
||||
* @param bucketName 存储桶名称
|
||||
* @param key key
|
||||
* @return string
|
||||
*/
|
||||
private String getUploadToken(String bucketName, String key) {
|
||||
return auth.uploadToken(getBucketName(bucketName), key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取域名
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public String getOssHost() {
|
||||
return ossProperties.getEndpoint();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -22,11 +22,10 @@ import com.aliyun.oss.common.auth.DefaultCredentialProvider;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springblade.core.oss.AliossTemplate;
|
||||
import org.springblade.core.oss.props.OssProperties;
|
||||
import org.springblade.core.oss.rule.BladeOssRule;
|
||||
import org.springblade.core.oss.rule.OssRule;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
@ -37,20 +36,15 @@ import org.springframework.context.annotation.Bean;
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@AutoConfiguration
|
||||
@AllArgsConstructor
|
||||
@AutoConfigureAfter(QiniuConfiguration.class)
|
||||
@AutoConfiguration(after = OssConfiguration.class)
|
||||
@EnableConfigurationProperties(OssProperties.class)
|
||||
@ConditionalOnClass({OSSClient.class})
|
||||
@ConditionalOnProperty(value = "oss.name", havingValue = "alioss")
|
||||
public class AliossConfiguration {
|
||||
|
||||
private OssProperties ossProperties;
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(OssRule.class)
|
||||
public OssRule ossRule() {
|
||||
return new BladeOssRule();
|
||||
}
|
||||
private final OssProperties ossProperties;
|
||||
private final OssRule ossRule;
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(OSSClient.class)
|
||||
@ -74,9 +68,9 @@ public class AliossConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean({OSSClient.class})
|
||||
@ConditionalOnMissingBean(AliossTemplate.class)
|
||||
@ConditionalOnBean({OSSClient.class, OssRule.class})
|
||||
public AliossTemplate aliossTemplate(OSSClient ossClient, OssRule ossRule) {
|
||||
public AliossTemplate aliossTemplate(OSSClient ossClient) {
|
||||
return new AliossTemplate(ossClient, ossProperties, ossRule);
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
|
||||
* <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 org.springblade.core.oss.config;
|
||||
|
||||
import io.minio.MinioClient;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import org.springblade.core.oss.MinioTemplate;
|
||||
import org.springblade.core.oss.props.OssProperties;
|
||||
import org.springblade.core.oss.rule.OssRule;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Minio配置类
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@AutoConfiguration(after = OssConfiguration.class)
|
||||
@ConditionalOnClass({MinioClient.class})
|
||||
@EnableConfigurationProperties(OssProperties.class)
|
||||
@ConditionalOnProperty(value = "oss.name", havingValue = "minio")
|
||||
public class MinioConfiguration {
|
||||
|
||||
private final OssProperties ossProperties;
|
||||
private final OssRule ossRule;
|
||||
|
||||
|
||||
@Bean
|
||||
@SneakyThrows
|
||||
@ConditionalOnMissingBean(MinioClient.class)
|
||||
public MinioClient minioClient() {
|
||||
return MinioClient.builder()
|
||||
.endpoint(ossProperties.getEndpoint())
|
||||
.credentials(ossProperties.getAccessKey(), ossProperties.getSecretKey())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean({MinioClient.class})
|
||||
@ConditionalOnMissingBean(MinioTemplate.class)
|
||||
public MinioTemplate minioTemplate(MinioClient minioClient) {
|
||||
return new MinioTemplate(minioClient, ossRule, ossProperties);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
|
||||
* <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 org.springblade.core.oss.config;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springblade.core.oss.props.OssProperties;
|
||||
import org.springblade.core.oss.rule.BladeOssRule;
|
||||
import org.springblade.core.oss.rule.OssRule;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
/**
|
||||
* Oss配置类
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@AutoConfiguration
|
||||
@AllArgsConstructor
|
||||
@EnableConfigurationProperties(OssProperties.class)
|
||||
public class OssConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(OssRule.class)
|
||||
public OssRule ossRule() {
|
||||
return new BladeOssRule();
|
||||
}
|
||||
|
||||
}
|
@ -15,47 +15,45 @@
|
||||
*/
|
||||
package org.springblade.core.oss.config;
|
||||
|
||||
import com.qiniu.common.Zone;
|
||||
import com.qiniu.storage.BucketManager;
|
||||
import com.qiniu.storage.Region;
|
||||
import com.qiniu.storage.UploadManager;
|
||||
import com.qiniu.util.Auth;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springblade.core.oss.QiniuTemplate;
|
||||
import org.springblade.core.oss.props.OssProperties;
|
||||
import org.springblade.core.oss.rule.BladeOssRule;
|
||||
import org.springblade.core.oss.rule.OssRule;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Oss配置类
|
||||
* Qiniu配置类
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@AutoConfiguration
|
||||
@AllArgsConstructor
|
||||
@AutoConfiguration(after = OssConfiguration.class)
|
||||
@ConditionalOnClass({Auth.class, UploadManager.class, BucketManager.class})
|
||||
@EnableConfigurationProperties(OssProperties.class)
|
||||
@ConditionalOnProperty(value = "oss.name", havingValue = "qiniu")
|
||||
public class QiniuConfiguration {
|
||||
|
||||
private OssProperties ossProperties;
|
||||
private final OssProperties ossProperties;
|
||||
private final OssRule ossRule;
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(OssRule.class)
|
||||
public OssRule ossRule() {
|
||||
return new BladeOssRule();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public com.qiniu.storage.Configuration qiniuConfiguration() {
|
||||
return new com.qiniu.storage.Configuration(Zone.autoZone());
|
||||
@ConditionalOnMissingBean(com.qiniu.storage.Configuration.class)
|
||||
public com.qiniu.storage.Configuration qnConfiguration() {
|
||||
return new com.qiniu.storage.Configuration(Region.autoRegion());
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(Auth.class)
|
||||
public Auth auth() {
|
||||
return Auth.create(ossProperties.getAccessKey(), ossProperties.getSecretKey());
|
||||
}
|
||||
@ -73,11 +71,10 @@ public class QiniuConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean({Auth.class, UploadManager.class, BucketManager.class})
|
||||
@ConditionalOnMissingBean(QiniuTemplate.class)
|
||||
@ConditionalOnBean({Auth.class, UploadManager.class, BucketManager.class, OssRule.class})
|
||||
public QiniuTemplate qiniuTemplate(Auth auth, UploadManager uploadManager, BucketManager bucketManager, OssRule ossRule) {
|
||||
public QiniuTemplate qiniuTemplate(Auth auth, UploadManager uploadManager, BucketManager bucketManager) {
|
||||
return new QiniuTemplate(auth, uploadManager, bucketManager, ossProperties, ossRule);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
|
||||
* <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 org.springblade.core.oss.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* minio策略配置
|
||||
*
|
||||
* @author SCMOX
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum PolicyType {
|
||||
|
||||
/**
|
||||
* 只读
|
||||
*/
|
||||
READ("read", "只读"),
|
||||
|
||||
/**
|
||||
* 只写
|
||||
*/
|
||||
WRITE("write", "只写"),
|
||||
|
||||
/**
|
||||
* 读写
|
||||
*/
|
||||
READ_WRITE("read_write", "读写");
|
||||
|
||||
/**
|
||||
* 类型
|
||||
*/
|
||||
private final String type;
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
private final String policy;
|
||||
|
||||
}
|
@ -28,6 +28,10 @@ public class BladeFile {
|
||||
* 文件地址
|
||||
*/
|
||||
private String link;
|
||||
/**
|
||||
* 域名地址
|
||||
*/
|
||||
private String domain;
|
||||
/**
|
||||
* 文件名
|
||||
*/
|
||||
|
@ -61,7 +61,7 @@ public class OssProperties {
|
||||
/**
|
||||
* 默认的存储桶名称
|
||||
*/
|
||||
private String bucketName = "bladex";
|
||||
private String bucketName = "blade";
|
||||
|
||||
/**
|
||||
* 自定义属性
|
||||
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<groupId>org.springblade</groupId>
|
||||
<version>3.4.1</version>
|
||||
<version>3.5.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<groupId>org.springblade</groupId>
|
||||
<version>3.4.1</version>
|
||||
<version>3.5.0</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -21,6 +21,7 @@ import org.springblade.core.secure.aspect.AuthAspect;
|
||||
import org.springblade.core.secure.interceptor.ClientInterceptor;
|
||||
import org.springblade.core.secure.interceptor.SecureInterceptor;
|
||||
import org.springblade.core.secure.props.BladeSecureProperties;
|
||||
import org.springblade.core.secure.props.BladeTokenProperties;
|
||||
import org.springblade.core.secure.provider.ClientDetailsServiceImpl;
|
||||
import org.springblade.core.secure.provider.IClientDetailsService;
|
||||
import org.springblade.core.secure.registry.SecureRegistry;
|
||||
@ -41,7 +42,7 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
@Order
|
||||
@AutoConfiguration
|
||||
@AllArgsConstructor
|
||||
@EnableConfigurationProperties({BladeSecureProperties.class})
|
||||
@EnableConfigurationProperties({BladeSecureProperties.class, BladeTokenProperties.class})
|
||||
public class SecureConfiguration implements WebMvcConfigurer {
|
||||
|
||||
private final SecureRegistry secureRegistry;
|
||||
|
@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
|
||||
* <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 org.springblade.core.secure.props;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.core.launch.constant.TokenConstant;
|
||||
import org.springblade.core.tool.utils.StringPool;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* secure放行额外配置
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Slf4j
|
||||
@Data
|
||||
@ConfigurationProperties("blade.token")
|
||||
public class BladeTokenProperties {
|
||||
|
||||
/**
|
||||
* token签名
|
||||
*/
|
||||
private String signKey = StringPool.EMPTY;
|
||||
|
||||
/**
|
||||
* 获取签名规则
|
||||
*/
|
||||
public String getSignKey() {
|
||||
if (this.signKey.length() < TokenConstant.SIGN_KEY_LENGTH) {
|
||||
log.warn("Token已启用默认签名,请前往blade.token.sign-key设置32位的key");
|
||||
return TokenConstant.SIGN_KEY;
|
||||
}
|
||||
return this.signKey;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
|
||||
* <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 org.springblade.core.secure.utils;
|
||||
|
||||
/**
|
||||
* Secure工具类
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public class AuthUtil extends SecureUtil{
|
||||
|
||||
}
|
@ -25,6 +25,7 @@ import org.springblade.core.secure.BladeUser;
|
||||
import org.springblade.core.secure.TokenInfo;
|
||||
import org.springblade.core.secure.constant.SecureConstant;
|
||||
import org.springblade.core.secure.exception.SecureException;
|
||||
import org.springblade.core.secure.props.BladeTokenProperties;
|
||||
import org.springblade.core.secure.provider.IClientDetails;
|
||||
import org.springblade.core.secure.provider.IClientDetailsService;
|
||||
import org.springblade.core.tool.constant.RoleConstant;
|
||||
@ -54,12 +55,45 @@ public class SecureUtil {
|
||||
private final static String TENANT_ID = TokenConstant.TENANT_ID;
|
||||
private final static String CLIENT_ID = TokenConstant.CLIENT_ID;
|
||||
private final static Integer AUTH_LENGTH = TokenConstant.AUTH_LENGTH;
|
||||
private static final String BASE64_SECURITY = Base64.getEncoder().encodeToString(TokenConstant.SIGN_KEY.getBytes(Charsets.UTF_8));
|
||||
private static IClientDetailsService CLIENT_DETAILS_SERVICE;
|
||||
private static BladeTokenProperties TOKEN_PROPERTIES;
|
||||
private static String BASE64_SECURITY;
|
||||
|
||||
private static final IClientDetailsService clientDetailsService;
|
||||
|
||||
static {
|
||||
clientDetailsService = SpringUtil.getBean(IClientDetailsService.class);
|
||||
/**
|
||||
* 获取客户端服务类
|
||||
*
|
||||
* @return clientDetailsService
|
||||
*/
|
||||
private static IClientDetailsService getClientDetailsService() {
|
||||
if (CLIENT_DETAILS_SERVICE == null) {
|
||||
CLIENT_DETAILS_SERVICE = SpringUtil.getBean(IClientDetailsService.class);
|
||||
}
|
||||
return CLIENT_DETAILS_SERVICE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取配置类
|
||||
*
|
||||
* @return jwtProperties
|
||||
*/
|
||||
private static BladeTokenProperties getTokenProperties() {
|
||||
if (TOKEN_PROPERTIES == null) {
|
||||
TOKEN_PROPERTIES = SpringUtil.getBean(BladeTokenProperties.class);
|
||||
}
|
||||
return TOKEN_PROPERTIES;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Token签名
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
private static String getBase64Security() {
|
||||
if (BASE64_SECURITY == null) {
|
||||
BASE64_SECURITY = Base64.getEncoder().encodeToString(getTokenProperties().getSignKey().getBytes(Charsets.UTF_8));
|
||||
}
|
||||
return BASE64_SECURITY;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -301,7 +335,7 @@ public class SecureUtil {
|
||||
public static Claims parseJWT(String jsonWebToken) {
|
||||
try {
|
||||
return Jwts.parserBuilder()
|
||||
.setSigningKey(Base64.getDecoder().decode(BASE64_SECURITY)).build()
|
||||
.setSigningKey(Base64.getDecoder().decode(getBase64Security())).build()
|
||||
.parseClaimsJws(jsonWebToken).getBody();
|
||||
} catch (Exception ex) {
|
||||
return null;
|
||||
@ -338,7 +372,7 @@ public class SecureUtil {
|
||||
Date now = new Date(nowMillis);
|
||||
|
||||
//生成签名密钥
|
||||
byte[] apiKeySecretBytes = Base64.getDecoder().decode(BASE64_SECURITY);
|
||||
byte[] apiKeySecretBytes = Base64.getDecoder().decode(getBase64Security());
|
||||
Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
|
||||
|
||||
//添加构成JWT的类
|
||||
@ -434,7 +468,7 @@ public class SecureUtil {
|
||||
* @return clientDetails
|
||||
*/
|
||||
private static IClientDetails clientDetails(String clientId) {
|
||||
return clientDetailsService.loadClientByClientId(clientId);
|
||||
return getClientDetailsService().loadClientByClientId(clientId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<groupId>org.springblade</groupId>
|
||||
<version>3.4.1</version>
|
||||
<version>3.5.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@ -32,5 +32,4 @@
|
||||
<artifactId>httpclient</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<groupId>org.springblade</groupId>
|
||||
<version>3.4.1</version>
|
||||
<version>3.5.0</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -55,7 +55,7 @@ public class SwaggerProperties {
|
||||
/**
|
||||
* 版本
|
||||
**/
|
||||
private String version = "3.4.1";
|
||||
private String version = "3.5.0";
|
||||
/**
|
||||
* 许可证
|
||||
**/
|
||||
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springblade</groupId>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<version>3.4.1</version>
|
||||
<version>3.5.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@ -27,5 +27,4 @@
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>org.springblade</groupId>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<version>3.4.1</version>
|
||||
<version>3.5.0</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -21,12 +21,14 @@ import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springblade.core.tool.jackson.BladeJacksonProperties;
|
||||
import org.springblade.core.tool.jackson.BladeJavaTimeModule;
|
||||
import org.springblade.core.tool.utils.DateUtil;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
|
||||
@ -45,6 +47,7 @@ import java.util.TimeZone;
|
||||
@AllArgsConstructor
|
||||
@ConditionalOnClass(ObjectMapper.class)
|
||||
@AutoConfigureBefore(JacksonAutoConfiguration.class)
|
||||
@EnableConfigurationProperties(BladeJacksonProperties.class)
|
||||
public class JacksonConfiguration {
|
||||
|
||||
@Primary
|
||||
|
@ -18,6 +18,7 @@ package org.springblade.core.tool.config;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springblade.core.tool.jackson.BladeJacksonProperties;
|
||||
import org.springblade.core.tool.jackson.MappingApiJackson2HttpMessageConverter;
|
||||
import org.springblade.core.tool.utils.Charsets;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
@ -40,6 +41,7 @@ import java.util.List;
|
||||
public class MessageConfiguration implements WebMvcConfigurer {
|
||||
|
||||
private final ObjectMapper objectMapper;
|
||||
private final BladeJacksonProperties properties;
|
||||
|
||||
/**
|
||||
* 使用 JACKSON 作为JSON MessageConverter
|
||||
@ -51,7 +53,7 @@ public class MessageConfiguration implements WebMvcConfigurer {
|
||||
converters.add(new ByteArrayHttpMessageConverter());
|
||||
converters.add(new ResourceHttpMessageConverter());
|
||||
converters.add(new ResourceRegionHttpMessageConverter());
|
||||
converters.add(new MappingApiJackson2HttpMessageConverter(objectMapper));
|
||||
converters.add(new MappingApiJackson2HttpMessageConverter(objectMapper, properties));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -36,8 +36,8 @@ import org.springframework.util.TypeUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
@ -64,9 +64,9 @@ public abstract class AbstractReadWriteJackson2HttpMessageConverter extends Abst
|
||||
initSsePrettyPrinter();
|
||||
}
|
||||
|
||||
public AbstractReadWriteJackson2HttpMessageConverter(ObjectMapper readObjectMapper, ObjectMapper writeObjectMapper, MediaType... supportedMediaTypes) {
|
||||
public AbstractReadWriteJackson2HttpMessageConverter(ObjectMapper readObjectMapper, ObjectMapper writeObjectMapper, List<MediaType> supportedMediaTypes) {
|
||||
this(readObjectMapper, writeObjectMapper);
|
||||
setSupportedMediaTypes(Arrays.asList(supportedMediaTypes));
|
||||
setSupportedMediaTypes(supportedMediaTypes);
|
||||
}
|
||||
|
||||
private void initSsePrettyPrinter() {
|
||||
|
@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2028, DreamLu 卢春梦 (qq596392912@gmail.com).
|
||||
* <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 org.springblade.core.tool.jackson;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* jackson 配置
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@ConfigurationProperties("blade.jackson")
|
||||
public class BladeJacksonProperties {
|
||||
|
||||
/**
|
||||
* 支持 MediaType text/plain,用于和 blade-api-crypto 一起使用
|
||||
*/
|
||||
private Boolean supportTextPlain = Boolean.FALSE;
|
||||
|
||||
}
|
@ -24,10 +24,8 @@ import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.core.tool.utils.DateUtil;
|
||||
import org.springblade.core.tool.utils.Exceptions;
|
||||
import org.springblade.core.tool.utils.StringPool;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
import org.springblade.core.tool.utils.*;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@ -268,6 +266,127 @@ public class JsonUtil {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 将json byte 数组反序列化成对象
|
||||
*
|
||||
* @param content json bytes
|
||||
* @param valueType class
|
||||
* @param <T> T 泛型标记
|
||||
* @return Bean
|
||||
*/
|
||||
@Nullable
|
||||
public static <T> T readValue(@Nullable byte[] content, Class<T> valueType) {
|
||||
if (ObjectUtil.isEmpty(content)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return getInstance().readValue(content, valueType);
|
||||
} catch (IOException e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将json反序列化成对象
|
||||
*
|
||||
* @param jsonString jsonString
|
||||
* @param valueType class
|
||||
* @param <T> T 泛型标记
|
||||
* @return Bean
|
||||
*/
|
||||
@Nullable
|
||||
public static <T> T readValue(@Nullable String jsonString, Class<T> valueType) {
|
||||
if (StringUtil.isBlank(jsonString)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return getInstance().readValue(jsonString, valueType);
|
||||
} catch (IOException e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将json反序列化成对象
|
||||
*
|
||||
* @param in InputStream
|
||||
* @param valueType class
|
||||
* @param <T> T 泛型标记
|
||||
* @return Bean
|
||||
*/
|
||||
@Nullable
|
||||
public static <T> T readValue(@Nullable InputStream in, Class<T> valueType) {
|
||||
if (in == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return getInstance().readValue(in, valueType);
|
||||
} catch (IOException e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将json反序列化成对象
|
||||
*
|
||||
* @param content bytes
|
||||
* @param typeReference 泛型类型
|
||||
* @param <T> T 泛型标记
|
||||
* @return Bean
|
||||
*/
|
||||
@Nullable
|
||||
public static <T> T readValue(@Nullable byte[] content, TypeReference<T> typeReference) {
|
||||
if (ObjectUtil.isEmpty(content)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return getInstance().readValue(content, typeReference);
|
||||
} catch (IOException e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将json反序列化成对象
|
||||
*
|
||||
* @param jsonString jsonString
|
||||
* @param typeReference 泛型类型
|
||||
* @param <T> T 泛型标记
|
||||
* @return Bean
|
||||
*/
|
||||
@Nullable
|
||||
public static <T> T readValue(@Nullable String jsonString, TypeReference<T> typeReference) {
|
||||
if (StringUtil.isBlank(jsonString)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return getInstance().readValue(jsonString, typeReference);
|
||||
} catch (IOException e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将json反序列化成对象
|
||||
*
|
||||
* @param in InputStream
|
||||
* @param typeReference 泛型类型
|
||||
* @param <T> T 泛型标记
|
||||
* @return Bean
|
||||
*/
|
||||
@Nullable
|
||||
public static <T> T readValue(@Nullable InputStream in, TypeReference<T> typeReference) {
|
||||
if (in == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return getInstance().readValue(in, typeReference);
|
||||
} catch (IOException e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将json字符串转成 JsonNode
|
||||
*
|
||||
|
@ -22,6 +22,8 @@ import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 针对 api 服务对 android 和 ios 和 web 处理的 分读写的 jackson 处理
|
||||
@ -38,22 +40,26 @@ public class MappingApiJackson2HttpMessageConverter extends AbstractReadWriteJac
|
||||
@Nullable
|
||||
private String jsonPrefix;
|
||||
|
||||
/**
|
||||
* Construct a new {@link MappingApiJackson2HttpMessageConverter} using default configuration
|
||||
* provided by {@link Jackson2ObjectMapperBuilder}.
|
||||
*/
|
||||
public MappingApiJackson2HttpMessageConverter() {
|
||||
this(Jackson2ObjectMapperBuilder.json().build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new {@link MappingApiJackson2HttpMessageConverter} with a custom {@link ObjectMapper}.
|
||||
* You can use {@link Jackson2ObjectMapperBuilder} to build it easily.
|
||||
*
|
||||
* @param objectMapper ObjectMapper
|
||||
* @see Jackson2ObjectMapperBuilder#json()
|
||||
*/
|
||||
public MappingApiJackson2HttpMessageConverter(ObjectMapper objectMapper) {
|
||||
super(objectMapper, initWriteObjectMapper(objectMapper), MediaType.APPLICATION_JSON, new MediaType("application", "*+json"));
|
||||
public MappingApiJackson2HttpMessageConverter(ObjectMapper objectMapper, BladeJacksonProperties properties) {
|
||||
super(objectMapper, initWriteObjectMapper(objectMapper), initMediaType(properties));
|
||||
}
|
||||
|
||||
private static List<MediaType> initMediaType(BladeJacksonProperties properties) {
|
||||
List<MediaType> supportedMediaTypes = new ArrayList<>();
|
||||
supportedMediaTypes.add(MediaType.APPLICATION_JSON);
|
||||
supportedMediaTypes.add(new MediaType("application", "*+json"));
|
||||
// 支持 text 文本,用于报文签名
|
||||
if (Boolean.TRUE.equals(properties.getSupportTextPlain())) {
|
||||
supportedMediaTypes.add(MediaType.TEXT_PLAIN);
|
||||
}
|
||||
return supportedMediaTypes;
|
||||
}
|
||||
|
||||
private static ObjectMapper initWriteObjectMapper(ObjectMapper readObjectMapper) {
|
||||
|
@ -43,6 +43,15 @@ public class Kv extends LinkedCaseInsensitiveMap<Object> {
|
||||
return new Kv();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建Kv
|
||||
*
|
||||
* @return Kv
|
||||
*/
|
||||
public static Kv create() {
|
||||
return new Kv();
|
||||
}
|
||||
|
||||
public static <K, V> HashMap<K, V> newMap() {
|
||||
return new HashMap<>(16);
|
||||
}
|
||||
|
@ -15,73 +15,257 @@
|
||||
*/
|
||||
package org.springblade.core.tool.utils;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 完全兼容微信所使用的AES加密方式。
|
||||
* 完全兼容微信所使用的AES加密工具类
|
||||
* aes的key必须是256byte长(比如32个字符),可以使用AesKit.genAesKey()来生成一组key
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class AesUtil {
|
||||
|
||||
private AesUtil() {
|
||||
}
|
||||
public static final Charset DEFAULT_CHARSET = Charsets.UTF_8;
|
||||
|
||||
/**
|
||||
* 获取密钥
|
||||
*
|
||||
* @return {String}
|
||||
*/
|
||||
public static String genAesKey() {
|
||||
return StringUtil.random(32);
|
||||
}
|
||||
|
||||
public static byte[] encrypt(byte[] content, String aesTextKey) {
|
||||
return encrypt(content, aesTextKey.getBytes(Charsets.UTF_8));
|
||||
}
|
||||
|
||||
/**
|
||||
* 加密
|
||||
*
|
||||
* @param content 文本内容
|
||||
* @param aesTextKey 文本密钥
|
||||
* @return byte[]
|
||||
*/
|
||||
public static byte[] encrypt(String content, String aesTextKey) {
|
||||
return encrypt(content.getBytes(Charsets.UTF_8), aesTextKey.getBytes(Charsets.UTF_8));
|
||||
return encrypt(content.getBytes(DEFAULT_CHARSET), aesTextKey);
|
||||
}
|
||||
|
||||
public static byte[] encrypt(String content, java.nio.charset.Charset charset, String aesTextKey) {
|
||||
return encrypt(content.getBytes(charset), aesTextKey.getBytes(Charsets.UTF_8));
|
||||
/**
|
||||
* 加密
|
||||
*
|
||||
* @param content 文本内容
|
||||
* @param charset 编码
|
||||
* @param aesTextKey 文本密钥
|
||||
* @return byte[]
|
||||
*/
|
||||
public static byte[] encrypt(String content, Charset charset, String aesTextKey) {
|
||||
return encrypt(content.getBytes(charset), aesTextKey);
|
||||
}
|
||||
|
||||
public static byte[] decrypt(byte[] content, String aesTextKey) {
|
||||
return decrypt(content, aesTextKey.getBytes(Charsets.UTF_8));
|
||||
/**
|
||||
* 加密
|
||||
*
|
||||
* @param content 内容
|
||||
* @param aesTextKey 文本密钥
|
||||
* @return byte[]
|
||||
*/
|
||||
public static byte[] encrypt(byte[] content, String aesTextKey) {
|
||||
return encrypt(content, Objects.requireNonNull(aesTextKey).getBytes(DEFAULT_CHARSET));
|
||||
}
|
||||
|
||||
public static String decryptToStr(byte[] content, String aesTextKey) {
|
||||
return new String(decrypt(content, aesTextKey.getBytes(Charsets.UTF_8)), Charsets.UTF_8);
|
||||
/**
|
||||
* hex加密
|
||||
*
|
||||
* @param content 文本内容
|
||||
* @param aesTextKey 文本密钥
|
||||
* @return {String}
|
||||
*/
|
||||
public static String encryptToHex(String content, String aesTextKey) {
|
||||
return HexUtil.encodeToString(encrypt(content, aesTextKey));
|
||||
}
|
||||
|
||||
public static String decryptToStr(byte[] content, String aesTextKey, java.nio.charset.Charset charset) {
|
||||
return new String(decrypt(content, aesTextKey.getBytes(Charsets.UTF_8)), charset);
|
||||
/**
|
||||
* hex加密
|
||||
*
|
||||
* @param content 内容
|
||||
* @param aesTextKey 文本密钥
|
||||
* @return {String}
|
||||
*/
|
||||
public static String encryptToHex(byte[] content, String aesTextKey) {
|
||||
return HexUtil.encodeToString(encrypt(content, aesTextKey));
|
||||
}
|
||||
|
||||
public static byte[] encrypt(byte[] content, byte[] aesKey) {
|
||||
Assert.isTrue(aesKey.length == 32, "IllegalAesKey, aesKey's length must be 32");
|
||||
try {
|
||||
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
|
||||
SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES");
|
||||
IvParameterSpec iv = new IvParameterSpec(aesKey, 0, 16);
|
||||
cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);
|
||||
return cipher.doFinal(Pkcs7Encoder.encode(content));
|
||||
} catch (Exception e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
/**
|
||||
* Base64加密
|
||||
*
|
||||
* @param content 文本内容
|
||||
* @param aesTextKey 文本密钥
|
||||
* @return {String}
|
||||
*/
|
||||
public static String encryptToBase64(String content, String aesTextKey) {
|
||||
return Base64Util.encodeToString(encrypt(content, aesTextKey));
|
||||
}
|
||||
|
||||
/**
|
||||
* Base64加密
|
||||
*
|
||||
* @param content 内容
|
||||
* @param aesTextKey 文本密钥
|
||||
* @return {String}
|
||||
*/
|
||||
public static String encryptToBase64(byte[] content, String aesTextKey) {
|
||||
return Base64Util.encodeToString(encrypt(content, aesTextKey));
|
||||
}
|
||||
|
||||
/**
|
||||
* hex解密
|
||||
*
|
||||
* @param content 文本内容
|
||||
* @param aesTextKey 文本密钥
|
||||
* @return {String}
|
||||
*/
|
||||
@Nullable
|
||||
public static String decryptFormHexToString(@Nullable String content, String aesTextKey) {
|
||||
byte[] hexBytes = decryptFormHex(content, aesTextKey);
|
||||
if (hexBytes == null) {
|
||||
return null;
|
||||
}
|
||||
return new String(hexBytes, DEFAULT_CHARSET);
|
||||
}
|
||||
|
||||
/**
|
||||
* hex解密
|
||||
*
|
||||
* @param content 文本内容
|
||||
* @param aesTextKey 文本密钥
|
||||
* @return byte[]
|
||||
*/
|
||||
@Nullable
|
||||
public static byte[] decryptFormHex(@Nullable String content, String aesTextKey) {
|
||||
if (StringUtil.isBlank(content)) {
|
||||
return null;
|
||||
}
|
||||
return decryptFormHex(content.getBytes(DEFAULT_CHARSET), aesTextKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* hex解密
|
||||
*
|
||||
* @param content 内容
|
||||
* @param aesTextKey 文本密钥
|
||||
* @return byte[]
|
||||
*/
|
||||
public static byte[] decryptFormHex(byte[] content, String aesTextKey) {
|
||||
return decrypt(HexUtil.decode(content), aesTextKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Base64解密
|
||||
*
|
||||
* @param content 文本内容
|
||||
* @param aesTextKey 文本密钥
|
||||
* @return {String}
|
||||
*/
|
||||
@Nullable
|
||||
public static String decryptFormBase64ToString(@Nullable String content, String aesTextKey) {
|
||||
byte[] hexBytes = decryptFormBase64(content, aesTextKey);
|
||||
if (hexBytes == null) {
|
||||
return null;
|
||||
}
|
||||
return new String(hexBytes, DEFAULT_CHARSET);
|
||||
}
|
||||
|
||||
/**
|
||||
* Base64解密
|
||||
*
|
||||
* @param content 文本内容
|
||||
* @param aesTextKey 文本密钥
|
||||
* @return byte[]
|
||||
*/
|
||||
@Nullable
|
||||
public static byte[] decryptFormBase64(@Nullable String content, String aesTextKey) {
|
||||
if (StringUtil.isBlank(content)) {
|
||||
return null;
|
||||
}
|
||||
return decryptFormBase64(content.getBytes(DEFAULT_CHARSET), aesTextKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Base64解密
|
||||
*
|
||||
* @param content 内容
|
||||
* @param aesTextKey 文本密钥
|
||||
* @return byte[]
|
||||
*/
|
||||
public static byte[] decryptFormBase64(byte[] content, String aesTextKey) {
|
||||
return decrypt(Base64Util.decode(content), aesTextKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密
|
||||
*
|
||||
* @param content 内容
|
||||
* @param aesTextKey 文本密钥
|
||||
* @return {String}
|
||||
*/
|
||||
public static String decryptToString(byte[] content, String aesTextKey) {
|
||||
return new String(decrypt(content, aesTextKey), DEFAULT_CHARSET);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密
|
||||
*
|
||||
* @param content 内容
|
||||
* @param aesTextKey 文本密钥
|
||||
* @return byte[]
|
||||
*/
|
||||
public static byte[] decrypt(byte[] content, String aesTextKey) {
|
||||
return decrypt(content, Objects.requireNonNull(aesTextKey).getBytes(DEFAULT_CHARSET));
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密
|
||||
*
|
||||
* @param content 内容
|
||||
* @param aesKey 密钥
|
||||
* @return byte[]
|
||||
*/
|
||||
public static byte[] encrypt(byte[] content, byte[] aesKey) {
|
||||
return aes(Pkcs7Encoder.encode(content), aesKey, Cipher.ENCRYPT_MODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加密
|
||||
*
|
||||
* @param encrypted 内容
|
||||
* @param aesKey 密钥
|
||||
* @return byte[]
|
||||
*/
|
||||
public static byte[] decrypt(byte[] encrypted, byte[] aesKey) {
|
||||
return Pkcs7Encoder.decode(aes(encrypted, aesKey, Cipher.DECRYPT_MODE));
|
||||
}
|
||||
|
||||
/**
|
||||
* ase加密
|
||||
*
|
||||
* @param encrypted 内容
|
||||
* @param aesKey 密钥
|
||||
* @param mode 模式
|
||||
* @return byte[]
|
||||
*/
|
||||
private static byte[] aes(byte[] encrypted, byte[] aesKey, int mode) {
|
||||
Assert.isTrue(aesKey.length == 32, "IllegalAesKey, aesKey's length must be 32");
|
||||
try {
|
||||
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
|
||||
SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES");
|
||||
IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey, 0, 16));
|
||||
cipher.init(Cipher.DECRYPT_MODE, keySpec, iv);
|
||||
return Pkcs7Encoder.decode(cipher.doFinal(encrypted));
|
||||
cipher.init(mode, keySpec, iv);
|
||||
return cipher.doFinal(encrypted);
|
||||
} catch (Exception e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
@ -90,16 +274,13 @@ public class AesUtil {
|
||||
/**
|
||||
* 提供基于PKCS7算法的加解密接口.
|
||||
*/
|
||||
static class Pkcs7Encoder {
|
||||
static int BLOCK_SIZE = 32;
|
||||
private static class Pkcs7Encoder {
|
||||
private static final int BLOCK_SIZE = 32;
|
||||
|
||||
static byte[] encode(byte[] src) {
|
||||
private static byte[] encode(byte[] src) {
|
||||
int count = src.length;
|
||||
// 计算需要填充的位数
|
||||
int amountToPad = BLOCK_SIZE - (count % BLOCK_SIZE);
|
||||
if (amountToPad == 0) {
|
||||
amountToPad = BLOCK_SIZE;
|
||||
}
|
||||
// 获得补位所用的字符
|
||||
byte pad = (byte) (amountToPad & 0xFF);
|
||||
byte[] pads = new byte[amountToPad];
|
||||
@ -113,8 +294,8 @@ public class AesUtil {
|
||||
return dest;
|
||||
}
|
||||
|
||||
static byte[] decode(byte[] decrypted) {
|
||||
int pad = (int) decrypted[decrypted.length - 1];
|
||||
private static byte[] decode(byte[] decrypted) {
|
||||
int pad = decrypted[decrypted.length - 1];
|
||||
if (pad < 1 || pad > BLOCK_SIZE) {
|
||||
pad = 0;
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* 类工具类
|
||||
* 类操作工具
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@ -106,4 +106,24 @@ public class ClassUtil extends org.springframework.util.ClassUtils {
|
||||
return AnnotatedElementUtils.findMergedAnnotation(beanType, annotationType);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 判断是否有注解 Annotation
|
||||
*
|
||||
* @param method Method
|
||||
* @param annotationType 注解类
|
||||
* @param <A> 泛型标记
|
||||
* @return {boolean}
|
||||
*/
|
||||
public static <A extends Annotation> boolean isAnnotated(Method method, Class<A> annotationType) {
|
||||
// 先找方法,再找方法上的类
|
||||
boolean isMethodAnnotated = AnnotatedElementUtils.isAnnotated(method, annotationType);
|
||||
if (isMethodAnnotated) {
|
||||
return true;
|
||||
}
|
||||
// 获取类上面的Annotation,可能包含组合注解,故采用spring的工具类
|
||||
Class<?> targetClass = method.getDeclaringClass();
|
||||
return AnnotatedElementUtils.isAnnotated(targetClass, annotationType);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,207 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2028, DreamLu 卢春梦 (qq596392912@gmail.com).
|
||||
* <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 org.springblade.core.tool.utils;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.SecretKeyFactory;
|
||||
import javax.crypto.spec.DESKeySpec;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* DES加解密处理工具
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class DesUtil {
|
||||
/**
|
||||
* 数字签名,密钥算法
|
||||
*/
|
||||
public static final String DES_ALGORITHM = "DES";
|
||||
|
||||
/**
|
||||
* 生成 des 密钥
|
||||
*
|
||||
* @return 密钥
|
||||
*/
|
||||
public static String genDesKey() {
|
||||
return StringUtil.random(16);
|
||||
}
|
||||
|
||||
/**
|
||||
* DES加密
|
||||
*
|
||||
* @param data byte array
|
||||
* @param password 密钥
|
||||
* @return des hex
|
||||
*/
|
||||
public static String encryptToHex(byte[] data, String password) {
|
||||
return HexUtil.encodeToString(encrypt(data, password));
|
||||
}
|
||||
|
||||
/**
|
||||
* DES加密
|
||||
*
|
||||
* @param data 字符串内容
|
||||
* @param password 密钥
|
||||
* @return des hex
|
||||
*/
|
||||
@Nullable
|
||||
public static String encryptToHex(@Nullable String data, String password) {
|
||||
if (StringUtil.isBlank(data)) {
|
||||
return null;
|
||||
}
|
||||
byte[] dataBytes = data.getBytes(Charsets.UTF_8);
|
||||
return encryptToHex(dataBytes, password);
|
||||
}
|
||||
|
||||
/**
|
||||
* DES解密
|
||||
*
|
||||
* @param data 字符串内容
|
||||
* @param password 密钥
|
||||
* @return des context
|
||||
*/
|
||||
@Nullable
|
||||
public static String decryptFormHex(@Nullable String data, String password) {
|
||||
if (StringUtil.isBlank(data)) {
|
||||
return null;
|
||||
}
|
||||
byte[] hexBytes = HexUtil.decode(data);
|
||||
return new String(decrypt(hexBytes, password), Charsets.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* DES加密
|
||||
*
|
||||
* @param data byte array
|
||||
* @param password 密钥
|
||||
* @return des hex
|
||||
*/
|
||||
public static String encryptToBase64(byte[] data, String password) {
|
||||
return Base64Util.encodeToString(encrypt(data, password));
|
||||
}
|
||||
|
||||
/**
|
||||
* DES加密
|
||||
*
|
||||
* @param data 字符串内容
|
||||
* @param password 密钥
|
||||
* @return des hex
|
||||
*/
|
||||
@Nullable
|
||||
public static String encryptToBase64(@Nullable String data, String password) {
|
||||
if (StringUtil.isBlank(data)) {
|
||||
return null;
|
||||
}
|
||||
byte[] dataBytes = data.getBytes(Charsets.UTF_8);
|
||||
return encryptToBase64(dataBytes, password);
|
||||
}
|
||||
|
||||
/**
|
||||
* DES解密
|
||||
*
|
||||
* @param data 字符串内容
|
||||
* @param password 密钥
|
||||
* @return des context
|
||||
*/
|
||||
public static byte[] decryptFormBase64(byte[] data, String password) {
|
||||
byte[] dataBytes = Base64Util.decode(data);
|
||||
return decrypt(dataBytes, password);
|
||||
}
|
||||
|
||||
/**
|
||||
* DES解密
|
||||
*
|
||||
* @param data 字符串内容
|
||||
* @param password 密钥
|
||||
* @return des context
|
||||
*/
|
||||
@Nullable
|
||||
public static String decryptFormBase64(@Nullable String data, String password) {
|
||||
if (StringUtil.isBlank(data)) {
|
||||
return null;
|
||||
}
|
||||
byte[] dataBytes = Base64Util.decodeFromString(data);
|
||||
return new String(decrypt(dataBytes, password), Charsets.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* DES加密
|
||||
*
|
||||
* @param data 内容
|
||||
* @param desKey 密钥
|
||||
* @return byte array
|
||||
*/
|
||||
public static byte[] encrypt(byte[] data, byte[] desKey) {
|
||||
return des(data, desKey, Cipher.ENCRYPT_MODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* DES加密
|
||||
*
|
||||
* @param data 内容
|
||||
* @param desKey 密钥
|
||||
* @return byte array
|
||||
*/
|
||||
public static byte[] encrypt(byte[] data, String desKey) {
|
||||
return encrypt(data, Objects.requireNonNull(desKey).getBytes(Charsets.UTF_8));
|
||||
}
|
||||
|
||||
/**
|
||||
* DES解密
|
||||
*
|
||||
* @param data 内容
|
||||
* @param desKey 密钥
|
||||
* @return byte array
|
||||
*/
|
||||
public static byte[] decrypt(byte[] data, byte[] desKey) {
|
||||
return des(data, desKey, Cipher.DECRYPT_MODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* DES解密
|
||||
*
|
||||
* @param data 内容
|
||||
* @param desKey 密钥
|
||||
* @return byte array
|
||||
*/
|
||||
public static byte[] decrypt(byte[] data, String desKey) {
|
||||
return decrypt(data, Objects.requireNonNull(desKey).getBytes(Charsets.UTF_8));
|
||||
}
|
||||
|
||||
/**
|
||||
* DES加密/解密公共方法
|
||||
*
|
||||
* @param data byte数组
|
||||
* @param desKey 密钥
|
||||
* @param mode 加密:{@link Cipher#ENCRYPT_MODE},解密:{@link Cipher#DECRYPT_MODE}
|
||||
* @return des
|
||||
*/
|
||||
private static byte[] des(byte[] data, byte[] desKey, int mode) {
|
||||
try {
|
||||
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES_ALGORITHM);
|
||||
Cipher cipher = Cipher.getInstance(DES_ALGORITHM);
|
||||
DESKeySpec desKeySpec = new DESKeySpec(desKey);
|
||||
cipher.init(mode, keyFactory.generateSecret(desKeySpec), Holder.SECURE_RANDOM);
|
||||
return cipher.doFinal(data);
|
||||
} catch (Exception e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,163 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2028, DreamLu 卢春梦 (qq596392912@gmail.com).
|
||||
* <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 org.springblade.core.tool.utils;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
/**
|
||||
* hex 工具,编解码全用 byte
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class HexUtil {
|
||||
public static final Charset DEFAULT_CHARSET = Charsets.UTF_8;
|
||||
private static final byte[] DIGITS_LOWER = new byte[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
|
||||
private static final byte[] DIGITS_UPPER = new byte[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
|
||||
|
||||
/**
|
||||
* encode Hex
|
||||
*
|
||||
* @param data data to hex
|
||||
* @return hex bytes
|
||||
*/
|
||||
public static byte[] encode(byte[] data) {
|
||||
return encode(data, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* encode Hex
|
||||
*
|
||||
* @param data data to hex
|
||||
* @param toLowerCase 是否小写
|
||||
* @return hex bytes
|
||||
*/
|
||||
public static byte[] encode(byte[] data, boolean toLowerCase) {
|
||||
return encode(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER);
|
||||
}
|
||||
|
||||
/**
|
||||
* encode Hex
|
||||
*
|
||||
* @param data Data to Hex
|
||||
* @return bytes as a hex string
|
||||
*/
|
||||
private static byte[] encode(byte[] data, byte[] digits) {
|
||||
int len = data.length;
|
||||
byte[] out = new byte[len << 1];
|
||||
for (int i = 0, j = 0; i < len; i++) {
|
||||
out[j++] = digits[(0xF0 & data[i]) >>> 4];
|
||||
out[j++] = digits[0xF & data[i]];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* encode Hex
|
||||
*
|
||||
* @param data Data to Hex
|
||||
* @param toLowerCase 是否小写
|
||||
* @return bytes as a hex string
|
||||
*/
|
||||
public static String encodeToString(byte[] data, boolean toLowerCase) {
|
||||
return new String(encode(data, toLowerCase), DEFAULT_CHARSET);
|
||||
}
|
||||
|
||||
/**
|
||||
* encode Hex
|
||||
*
|
||||
* @param data Data to Hex
|
||||
* @return bytes as a hex string
|
||||
*/
|
||||
public static String encodeToString(byte[] data) {
|
||||
return new String(encode(data), DEFAULT_CHARSET);
|
||||
}
|
||||
|
||||
/**
|
||||
* encode Hex
|
||||
*
|
||||
* @param data Data to Hex
|
||||
* @return bytes as a hex string
|
||||
*/
|
||||
@Nullable
|
||||
public static String encodeToString(@Nullable String data) {
|
||||
if (StringUtil.isBlank(data)) {
|
||||
return null;
|
||||
}
|
||||
return encodeToString(data.getBytes(DEFAULT_CHARSET));
|
||||
}
|
||||
|
||||
/**
|
||||
* decode Hex
|
||||
*
|
||||
* @param data Hex data
|
||||
* @return decode hex to bytes
|
||||
*/
|
||||
@Nullable
|
||||
public static byte[] decode(@Nullable String data) {
|
||||
if (StringUtil.isBlank(data)) {
|
||||
return null;
|
||||
}
|
||||
return decode(data.getBytes(DEFAULT_CHARSET));
|
||||
}
|
||||
|
||||
/**
|
||||
* encode Hex
|
||||
*
|
||||
* @param data Data to Hex
|
||||
* @return bytes as a hex string
|
||||
*/
|
||||
@Nullable
|
||||
public static String decodeToString(@Nullable String data) {
|
||||
byte[] decodeBytes = decode(data);
|
||||
if (decodeBytes == null) {
|
||||
return null;
|
||||
}
|
||||
return new String(decodeBytes, DEFAULT_CHARSET);
|
||||
}
|
||||
|
||||
/**
|
||||
* decode Hex
|
||||
*
|
||||
* @param data Hex data
|
||||
* @return decode hex to bytes
|
||||
*/
|
||||
public static byte[] decode(byte[] data) {
|
||||
int len = data.length;
|
||||
if ((len & 0x01) != 0) {
|
||||
throw new IllegalArgumentException("hexBinary needs to be even-length: " + len);
|
||||
}
|
||||
byte[] out = new byte[len >> 1];
|
||||
for (int i = 0, j = 0; j < len; i++) {
|
||||
int f = toDigit(data[j], j) << 4;
|
||||
j++;
|
||||
f |= toDigit(data[j], j);
|
||||
j++;
|
||||
out[i] = (byte) (f & 0xFF);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
private static int toDigit(byte b, int index) {
|
||||
int digit = Character.digit(b, 16);
|
||||
if (digit == -1) {
|
||||
throw new IllegalArgumentException("Illegal hexadecimal byte " + b + " at index " + index);
|
||||
}
|
||||
return digit;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2028, DreamLu 卢春梦 (qq596392912@gmail.com).
|
||||
* <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 org.springblade.core.tool.utils;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* 一些常用的单利对象
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class Holder {
|
||||
|
||||
/**
|
||||
* RANDOM
|
||||
*/
|
||||
public final static Random RANDOM = new Random();
|
||||
|
||||
/**
|
||||
* SECURE_RANDOM
|
||||
*/
|
||||
public final static SecureRandom SECURE_RANDOM = new SecureRandom();
|
||||
}
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<groupId>org.springblade</groupId>
|
||||
<version>3.4.1</version>
|
||||
<version>3.5.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
5
pom.xml
5
pom.xml
@ -5,7 +5,7 @@
|
||||
|
||||
<groupId>org.springblade</groupId>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<version>3.4.1</version>
|
||||
<version>3.5.0</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>blade-tool</name>
|
||||
<description>
|
||||
@ -36,7 +36,7 @@
|
||||
</scm>
|
||||
|
||||
<properties>
|
||||
<blade.tool.version>3.4.1</blade.tool.version>
|
||||
<blade.tool.version>3.5.0</blade.tool.version>
|
||||
|
||||
<java.version>1.8</java.version>
|
||||
<maven.plugin.version>3.8.1</maven.plugin.version>
|
||||
@ -80,6 +80,7 @@
|
||||
<module>blade-core-transaction</module>
|
||||
<module>blade-core-report</module>
|
||||
<module>blade-core-datascope</module>
|
||||
<module>blade-core-crypto</module>
|
||||
</modules>
|
||||
|
||||
<dependencyManagement>
|
||||
|
Loading…
Reference in New Issue
Block a user