This commit is contained in:
kk
2026-07-07 18:01:21 +08:00
commit b259f94d4b
1088 changed files with 121778 additions and 0 deletions
+67
View File
@@ -0,0 +1,67 @@
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>BladeX-Tool</artifactId>
<groupId>org.springblade</groupId>
<version>${revision}</version>
</parent>
<artifactId>blade-core-boot</artifactId>
<name>${project.artifactId}</name>
<version>${project.parent.version}</version>
<packaging>jar</packaging>
<properties>
<module.name>org.springblade.blade.core.boot</module.name>
</properties>
<dependencies>
<!-- Blade -->
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-context</artifactId>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-db</artifactId>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-secure</artifactId>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-cloud</artifactId>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-starter-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-starter-log</artifactId>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-starter-mybatis</artifactId>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-starter-xss</artifactId>
</dependency>
<!-- Auto -->
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-auto</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,46 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.boot.config;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.launch.props.BladePropertySource;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
/**
* blade自动配置类
*
* @author Chill
*/
@Slf4j
@AutoConfiguration
@AllArgsConstructor
@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)
@BladePropertySource(value = "classpath:/blade-boot.yml")
public class BladeBootAutoConfiguration {
}
@@ -0,0 +1,120 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.boot.config;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.boot.handler.BladeAsyncUncaughtExceptionHandler;
import org.springblade.core.context.BladeContext;
import org.springblade.core.context.BladeRunnableWrapper;
import org.springblade.core.launch.props.BladeProperties;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.task.ThreadPoolTaskExecutorCustomizer;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.lang.NonNull;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 异步处理
*
* @author Chill
*/
@Slf4j
@EnableAsync
@EnableScheduling
@AllArgsConstructor
@Configuration(proxyBeanMethods = false)
public class BladeExecutorConfiguration {
private static final String ASYNC_TASK_THREAD_NAME_PREFIX = "async-task-";
private static final String ASYNC_SCHEDULER_THREAD_NAME_PREFIX = "async-scheduler";
private static final String BOOT_ASYNC_CONFIGURER_BEAN_NAME = "applicationTaskExecutorAsyncConfigurer";
@Bean
public ThreadPoolTaskExecutorCustomizer taskExecutorCustomizer() {
return taskExecutor -> {
taskExecutor.setThreadNamePrefix(ASYNC_TASK_THREAD_NAME_PREFIX);
taskExecutor.setTaskDecorator(BladeRunnableWrapper::new);
taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
};
}
@Bean
public ThreadPoolTaskExecutorCustomizer taskSchedulerCustomizer() {
return taskExecutor -> {
taskExecutor.setThreadNamePrefix(ASYNC_SCHEDULER_THREAD_NAME_PREFIX);
taskExecutor.setTaskDecorator(BladeRunnableWrapper::new);
taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
};
}
@Bean
public BladeAsyncUncaughtExceptionHandler bladeAsyncUncaughtExceptionHandler(BladeContext bladeContext,
BladeProperties bladeProperties,
ApplicationEventPublisher eventPublisher) {
return new BladeAsyncUncaughtExceptionHandler(bladeContext, bladeProperties, eventPublisher);
}
/**
* 装饰 Spring Boot 自动注册的 applicationTaskExecutorAsyncConfigurer
* getAsyncExecutor() 透传原 Bean 的实现(即 applicationTaskExecutor
* getAsyncUncaughtExceptionHandler() 接入 BladeAsyncUncaughtExceptionHandler 实现统一异步异常处理
*/
@Bean
public static BeanPostProcessor bladeAsyncConfigurerDecorator(ObjectProvider<BladeAsyncUncaughtExceptionHandler> handlerProvider) {
return new BeanPostProcessor() {
@Override
public Object postProcessAfterInitialization(@NonNull Object bean, @NonNull String beanName) {
if (!BOOT_ASYNC_CONFIGURER_BEAN_NAME.equals(beanName) || !(bean instanceof AsyncConfigurer original)) {
return bean;
}
return new AsyncConfigurer() {
@Override
public Executor getAsyncExecutor() {
return original.getAsyncExecutor();
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return handlerProvider.getIfAvailable();
}
};
}
};
}
}
@@ -0,0 +1,63 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.boot.config;
import jakarta.servlet.DispatcherType;
import lombok.AllArgsConstructor;
import org.springblade.core.boot.request.BladeRequestFilter;
import org.springblade.core.boot.request.RequestProperties;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.core.Ordered;
/**
* 过滤器配置类
*
* @author Chill
*/
@AutoConfiguration
@AllArgsConstructor
@EnableConfigurationProperties({RequestProperties.class})
public class BladeRequestConfiguration {
private final RequestProperties requestProperties;
/**
* 全局过滤器
*/
@Bean
public FilterRegistrationBean<BladeRequestFilter> bladeFilterRegistration() {
FilterRegistrationBean<BladeRequestFilter> registration = new FilterRegistrationBean<>();
registration.setDispatcherTypes(DispatcherType.REQUEST);
registration.setFilter(new BladeRequestFilter(requestProperties));
registration.addUrlPatterns("/*");
registration.setName("bladeRequestFilter");
registration.setOrder(Ordered.HIGHEST_PRECEDENCE);
return registration;
}
}
@@ -0,0 +1,58 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.boot.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.retry.interceptor.RetryInterceptorBuilder;
import org.springframework.retry.interceptor.RetryOperationsInterceptor;
/**
* 重试机制
*
* @author Chill
*/
@Slf4j
@AutoConfiguration
public class BladeRetryConfiguration {
@Bean
@ConditionalOnMissingBean(name = "configServerRetryInterceptor")
public RetryOperationsInterceptor configServerRetryInterceptor() {
log.info(String.format(
"configServerRetryInterceptor: Changing backOffOptions " +
"to initial: %s, multiplier: %s, maxInterval: %s",
1000, 1.2, 5000));
return RetryInterceptorBuilder
.stateless()
.backOffOptions(1000, 1.2, 5000)
.maxAttempts(10)
.build();
}
}
@@ -0,0 +1,62 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.boot.config;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.boot.props.BladeFileProperties;
import org.springblade.core.boot.props.BladeUploadProperties;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* WEB配置
*
* @author Chill
*/
@Slf4j
@AutoConfiguration
@Order(Ordered.HIGHEST_PRECEDENCE)
@AllArgsConstructor
@EnableConfigurationProperties({
BladeUploadProperties.class, BladeFileProperties.class
})
public class BladeWebMvcConfiguration implements WebMvcConfigurer {
private final BladeUploadProperties bladeUploadProperties;
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
String path = bladeUploadProperties.getSavePath();
registry.addResourceHandler("/upload/**")
.addResourceLocations("file:" + path + "/upload/");
}
}
@@ -0,0 +1,288 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.boot.ctrl;
import org.springblade.core.boot.file.LocalFile;
import org.springblade.core.boot.file.BladeFileUtil;
import org.springblade.core.secure.BladeUser;
import org.springblade.core.secure.utils.AuthUtil;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.utils.Charsets;
import org.springblade.core.tool.utils.StringPool;
import org.springblade.core.tool.utils.WebUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.ResourceRegion;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.util.UriUtils;
import jakarta.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.util.List;
/**
* Blade控制器封装类
*
* @author Chill
*/
public class BladeController {
/**
* ============================ REQUEST =================================================
*/
@Autowired
private HttpServletRequest request;
/**
* 获取request
*/
public HttpServletRequest getRequest() {
return this.request;
}
/**
* 获取当前用户
*
* @return
*/
public BladeUser getUser() {
return AuthUtil.getUser();
}
/** ============================ API_RESULT ================================================= */
/**
* 返回ApiResult
*
* @param data
* @return R
*/
public <T> R<T> data(T data) {
return R.data(data);
}
/**
* 返回ApiResult
*
* @param data
* @param message
* @return R
*/
public <T> R<T> data(T data, String message) {
return R.data(data, message);
}
/**
* 返回ApiResult
*
* @param data
* @param message
* @param code
* @return R
*/
public <T> R<T> data(T data, String message, int code) {
return R.data(code, data, message);
}
/**
* 返回ApiResult
*
* @param message
* @return R
*/
public R success(String message) {
return R.success(message);
}
/**
* 返回ApiResult
*
* @param message
* @return R
*/
public R fail(String message) {
return R.fail(message);
}
/**
* 返回ApiResult
*
* @param flag
* @return R
*/
public R status(boolean flag) {
return R.status(flag);
}
/**============================ FILE ================================================= */
/**
* 获取BladeFile封装类
*
* @param file
* @return
*/
public LocalFile getFile(MultipartFile file) {
return BladeFileUtil.getFile(file);
}
/**
* 获取BladeFile封装类
*
* @param file
* @param dir
* @return
*/
public LocalFile getFile(MultipartFile file, String dir) {
return BladeFileUtil.getFile(file, dir);
}
/**
* 获取BladeFile封装类
*
* @param file
* @param dir
* @param path
* @param virtualPath
* @return
*/
public LocalFile getFile(MultipartFile file, String dir, String path, String virtualPath) {
return BladeFileUtil.getFile(file, dir, path, virtualPath);
}
/**
* 获取BladeFile封装类
*
* @param files
* @return
*/
public List<LocalFile> getFiles(List<MultipartFile> files) {
return BladeFileUtil.getFiles(files);
}
/**
* 获取BladeFile封装类
*
* @param files
* @param dir
* @return
*/
public List<LocalFile> getFiles(List<MultipartFile> files, String dir) {
return BladeFileUtil.getFiles(files, dir);
}
/**
* 获取BladeFile封装类
*
* @param files
* @param path
* @param virtualPath
* @return
*/
public List<LocalFile> getFiles(List<MultipartFile> files, String dir, String path, String virtualPath) {
return BladeFileUtil.getFiles(files, dir, path, virtualPath);
}
/**
* 下载文件
*
* @param file 文件
* @return {ResponseEntity}
* @throws IOException io异常
*/
protected ResponseEntity<ResourceRegion> download(File file) throws IOException {
String fileName = file.getName();
return download(file, fileName);
}
/**
* 下载
*
* @param file 文件
* @param fileName 生成的文件名
* @return {ResponseEntity}
* @throws IOException io异常
*/
protected ResponseEntity<ResourceRegion> download(File file, String fileName) throws IOException {
Resource resource = new FileSystemResource(file);
return download(resource, fileName);
}
/**
* 下载
*
* @param resource 资源
* @param fileName 生成的文件名
* @return {ResponseEntity}
* @throws IOException io异常
*/
protected ResponseEntity<ResourceRegion> download(Resource resource, String fileName) throws IOException {
HttpServletRequest request = WebUtil.getRequest();
String header = request.getHeader(HttpHeaders.USER_AGENT);
// 避免空指针
header = header == null ? StringPool.EMPTY : header.toUpperCase();
HttpStatus status;
String msie= "MSIE";
String trident= "TRIDENT";
String edge= "EDGE";
if (header.contains(msie) || header.contains(trident) || header.contains(edge)) {
status = HttpStatus.OK;
} else {
status = HttpStatus.CREATED;
}
// 断点续传
long position = 0;
long count = resource.contentLength();
String range = request.getHeader(HttpHeaders.RANGE);
if (null != range) {
status = HttpStatus.PARTIAL_CONTENT;
String[] rangeRange = range.replace("bytes=", StringPool.EMPTY).split(StringPool.DASH);
position = Long.parseLong(rangeRange[0]);
if (rangeRange.length > 1) {
long end = Long.parseLong(rangeRange[1]);
count = end - position + 1;
}
}
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
String encodeFileName = UriUtils.encode(fileName, Charsets.UTF_8);
// 兼容各种浏览器下载:
// https://blog.robotshell.org/2012/deal-with-http-header-encoding-for-file-download/
String disposition = "attachment;" +
"filename=\"" + encodeFileName + "\";" +
"filename*=utf-8''" + encodeFileName;
headers.set(HttpHeaders.CONTENT_DISPOSITION, disposition);
return new ResponseEntity<>(new ResourceRegion(resource, position, count), headers, status);
}
}
@@ -0,0 +1,66 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.boot.error;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.lang.Nullable;
/**
* 异常类型
*
* @author L.cm
*/
@Getter
@RequiredArgsConstructor
public enum ErrorType {
/**
* 异常类型
*/
REQUEST("request"),
ASYNC("async"),
SCHEDULER("scheduler"),
WEB_SOCKET("websocket"),
OTHER("other");
@JsonValue
private final String type;
@Nullable
@JsonCreator
public static ErrorType of(String type) {
ErrorType[] values = ErrorType.values();
for (ErrorType errorType : values) {
if (errorType.type.equals(type)) {
return errorType;
}
}
return null;
}
}
@@ -0,0 +1,62 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.boot.error;
import org.springblade.core.log.model.LogError;
import org.springblade.core.tool.utils.DateUtil;
import org.springblade.core.tool.utils.Exceptions;
import org.springblade.core.tool.utils.ObjectUtil;
/**
* 异常工具类
*
* @author L.cm
*/
public class ErrorUtil {
/**
* 初始化异常信息
*
* @param error 异常
* @param event 异常事件封装
*/
public static void initErrorInfo(Throwable error, LogError event) {
// 堆栈信息
event.setStackTrace(Exceptions.getStackTraceAsString(error));
event.setExceptionName(error.getClass().getName());
event.setMessage(error.getMessage());
event.setCreateTime(DateUtil.now());
StackTraceElement[] elements = error.getStackTrace();
if (ObjectUtil.isNotEmpty(elements)) {
// 报错的类信息
StackTraceElement element = elements[0];
event.setMethodClass(element.getClassName());
event.setFileName(element.getFileName());
event.setMethodName(element.getMethodName());
event.setLineNumber(element.getLineNumber());
}
}
}
@@ -0,0 +1,249 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.boot.file;
import org.springblade.core.tool.utils.StringPool;
import org.springblade.core.tool.utils.StringUtil;
import org.springframework.web.multipart.MultipartFile;
import java.util.*;
/**
* 文件工具类
*
* @author Chill
*/
public class BladeFileUtil {
/**
* 定义允许上传的文件扩展名
*/
private static final HashMap<String, String> EXT_MAP = new HashMap<>();
private static final String IS_DIR = "is_dir";
private static final String FILE_NAME = "filename";
private static final String FILE_SIZE = "filesize";
/**
* 图片扩展名
*/
private static final String[] FILE_TYPES = new String[]{"gif", "jpg", "jpeg", "png", "bmp"};
static {
EXT_MAP.put("image", ".gif,.jpg,.jpeg,.png,.bmp,.JPG,.JPEG,.PNG");
EXT_MAP.put("flash", ".swf,.flv");
EXT_MAP.put("media", ".swf,.flv,.mp3,.mp4,.wav,.wma,.wmv,.mid,.avi,.mpg,.asf,.rm,.rmvb");
EXT_MAP.put("file", ".doc,.docx,.xls,.xlsx,.ppt,.htm,.html,.txt,.zip,.rar,.gz,.bz2");
EXT_MAP.put("allfile", ".gif,.jpg,.jpeg,.png,.bmp,.swf,.flv,.mp3,.mp4,.wav,.wma,.wmv,.mid,.avi,.mpg,.asf,.rm,.rmvb,.doc,.docx,.xls,.xlsx,.ppt,.htm,.html,.txt,.zip,.rar,.gz,.bz2");
}
/**
* 获取文件后缀
*
* @param fileName 文件名
* @return String 返回后缀
*/
public static String getFileExt(String fileName) {
return fileName.substring(fileName.lastIndexOf(StringPool.DOT));
}
/**
* 测试文件后缀 只让指定后缀的文件上传,像jsp,war,sh等危险的后缀禁止
*
* @param dir 目录
* @param fileName 文件名
* @return 返回成功与否
*/
public static boolean testExt(String dir, String fileName) {
String fileExt = getFileExt(fileName);
String ext = EXT_MAP.get(dir);
return StringUtil.isNotBlank(ext) && (ext.contains(fileExt.toLowerCase()) || ext.contains(fileExt.toUpperCase()));
}
/**
* 文件管理排序
*/
public enum FileSort {
/**
* 大小
*/
size,
/**
* 类型
*/
type,
/**
* 名称
*/
name;
/**
* 文本排序转换成枚举
*
* @param sort
* @return
*/
public static FileSort of(String sort) {
try {
return FileSort.valueOf(sort);
} catch (Exception e) {
return FileSort.name;
}
}
}
public static class NameComparator implements Comparator {
@Override
public int compare(Object a, Object b) {
Hashtable hashA = (Hashtable) a;
Hashtable hashB = (Hashtable) b;
if (((Boolean) hashA.get(IS_DIR)) && !((Boolean) hashB.get(IS_DIR))) {
return -1;
} else if (!((Boolean) hashA.get(IS_DIR)) && ((Boolean) hashB.get(IS_DIR))) {
return 1;
} else {
return ((String) hashA.get(FILE_NAME)).compareTo((String) hashB.get(FILE_NAME));
}
}
}
public static class SizeComparator implements Comparator {
@Override
public int compare(Object a, Object b) {
Hashtable hashA = (Hashtable) a;
Hashtable hashB = (Hashtable) b;
if (((Boolean) hashA.get(IS_DIR)) && !((Boolean) hashB.get(IS_DIR))) {
return -1;
} else if (!((Boolean) hashA.get(IS_DIR)) && ((Boolean) hashB.get(IS_DIR))) {
return 1;
} else {
if (((Long) hashA.get(FILE_SIZE)) > ((Long) hashB.get(FILE_SIZE))) {
return 1;
} else if (((Long) hashA.get(FILE_SIZE)) < ((Long) hashB.get(FILE_SIZE))) {
return -1;
} else {
return 0;
}
}
}
}
public static class TypeComparator implements Comparator {
@Override
public int compare(Object a, Object b) {
Hashtable hashA = (Hashtable) a;
Hashtable hashB = (Hashtable) b;
if (((Boolean) hashA.get(IS_DIR)) && !((Boolean) hashB.get(IS_DIR))) {
return -1;
} else if (!((Boolean) hashA.get(IS_DIR)) && ((Boolean) hashB.get(IS_DIR))) {
return 1;
} else {
return ((String) hashA.get("filetype")).compareTo((String) hashB.get("filetype"));
}
}
}
public static String formatUrl(String url) {
return url.replaceAll("\\\\", "/");
}
/********************************BladeFile封装********************************************************/
/**
* 获取BladeFile封装类
*
* @param file 文件
* @return BladeFile
*/
public static LocalFile getFile(MultipartFile file) {
return getFile(file, "image", null, null);
}
/**
* 获取BladeFile封装类
*
* @param file 文件
* @param dir 目录
* @return BladeFile
*/
public static LocalFile getFile(MultipartFile file, String dir) {
return getFile(file, dir, null, null);
}
/**
* 获取BladeFile封装类
*
* @param file 文件
* @param dir 目录
* @param path 路径
* @param virtualPath 虚拟路径
* @return BladeFile
*/
public static LocalFile getFile(MultipartFile file, String dir, String path, String virtualPath) {
return new LocalFile(file, dir, path, virtualPath);
}
/**
* 获取BladeFile封装类
*
* @param files 文件集合
* @return BladeFile
*/
public static List<LocalFile> getFiles(List<MultipartFile> files) {
return getFiles(files, "image", null, null);
}
/**
* 获取BladeFile封装类
*
* @param files 文件集合
* @param dir 目录
* @return BladeFile
*/
public static List<LocalFile> getFiles(List<MultipartFile> files, String dir) {
return getFiles(files, dir, null, null);
}
/**
* 获取BladeFile封装类
*
* @param files 文件集合
* @param path 路径
* @param virtualPath 虚拟路径
* @return BladeFile
*/
public static List<LocalFile> getFiles(List<MultipartFile> files, String dir, String path, String virtualPath) {
List<LocalFile> list = new ArrayList<>();
for (MultipartFile file : files) {
list.add(new LocalFile(file, dir, path, virtualPath));
}
return list;
}
}
@@ -0,0 +1,60 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.boot.file;
import java.io.File;
/**
* 文件管理类
*
* @author Chill
*/
public class FileProxyManager {
private IFileProxy defaultFileProxyFactory = new LocalFileProxyFactory();
private static final FileProxyManager ME = new FileProxyManager();
public static FileProxyManager me() {
return ME;
}
public IFileProxy getDefaultFileProxyFactory() {
return defaultFileProxyFactory;
}
public void setDefaultFileProxyFactory(IFileProxy defaultFileProxyFactory) {
this.defaultFileProxyFactory = defaultFileProxyFactory;
}
public String[] path(File file, String dir) {
return defaultFileProxyFactory.path(file, dir);
}
public File rename(File file, String path) {
return defaultFileProxyFactory.rename(file, path);
}
}
@@ -0,0 +1,62 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.boot.file;
import java.io.File;
/**
* 文件代理接口
*
* @author Chill
*/
public interface IFileProxy {
/**
* 返回路径[物理路径][虚拟路径]
*
* @param file 文件
* @param dir 目录
* @return
*/
String[] path(File file, String dir);
/**
* 文件重命名策略
*
* @param file 文件
* @param path 路径
* @return
*/
File rename(File file, String path);
/**
* 图片压缩
*
* @param path 路径
*/
void compress(String path);
}
@@ -0,0 +1,166 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.boot.file;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import org.springblade.core.boot.props.BladeFileProperties;
import org.springblade.core.tool.utils.DateUtil;
import org.springblade.core.tool.utils.SpringUtil;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
/**
* 上传文件封装
*
* @author Chill
*/
@Data
public class LocalFile {
/**
* 上传文件在附件表中的id
*/
private Object fileId;
/**
* 上传文件
*/
@JsonIgnore
private MultipartFile file;
/**
* 文件外网地址
*/
private String domain;
/**
* 上传分类文件夹
*/
private String dir;
/**
* 上传物理路径
*/
private String uploadPath;
/**
* 上传虚拟路径
*/
private String uploadVirtualPath;
/**
* 文件名
*/
private String fileName;
/**
* 真实文件名
*/
private String originalFileName;
/**
* 文件配置
*/
private static BladeFileProperties fileProperties;
private static BladeFileProperties getBladeFileProperties() {
if (fileProperties == null) {
fileProperties = SpringUtil.getBean(BladeFileProperties.class);
}
return fileProperties;
}
public LocalFile(MultipartFile file, String dir) {
this.dir = dir;
this.file = file;
this.fileName = file.getName();
this.originalFileName = file.getOriginalFilename();
this.domain = getBladeFileProperties().getUploadDomain();
this.uploadPath = BladeFileUtil.formatUrl(File.separator + getBladeFileProperties().getUploadRealPath() + File.separator + dir + File.separator + DateUtil.format(DateUtil.now(), "yyyyMMdd") + File.separator + this.originalFileName);
this.uploadVirtualPath = BladeFileUtil.formatUrl(getBladeFileProperties().getUploadCtxPath().replace(getBladeFileProperties().getContextPath(), "") + File.separator + dir + File.separator + DateUtil.format(DateUtil.now(), "yyyyMMdd") + File.separator + this.originalFileName);
}
public LocalFile(MultipartFile file, String dir, String uploadPath, String uploadVirtualPath) {
this(file, dir);
if (null != uploadPath) {
this.uploadPath = BladeFileUtil.formatUrl(uploadPath);
this.uploadVirtualPath = BladeFileUtil.formatUrl(uploadVirtualPath);
}
}
/**
* 图片上传
*/
public void transfer() {
transfer(getBladeFileProperties().getCompress());
}
/**
* 图片上传
*
* @param compress 是否压缩
*/
public void transfer(boolean compress) {
IFileProxy fileFactory = FileProxyManager.me().getDefaultFileProxyFactory();
this.transfer(fileFactory, compress);
}
/**
* 图片上传
*
* @param fileFactory 文件上传工厂类
* @param compress 是否压缩
*/
public void transfer(IFileProxy fileFactory, boolean compress) {
try {
File file = new File(uploadPath);
if (null != fileFactory) {
String[] path = fileFactory.path(file, dir);
this.uploadPath = path[0];
this.uploadVirtualPath = path[1];
file = fileFactory.rename(file, path[0]);
}
File pfile = file.getParentFile();
if (!pfile.exists()) {
pfile.mkdirs();
}
this.file.transferTo(file);
if (compress) {
fileFactory.compress(this.uploadPath);
}
} catch (IllegalStateException | IOException e) {
e.printStackTrace();
}
}
}
@@ -0,0 +1,126 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.boot.file;
import org.springblade.core.boot.props.BladeFileProperties;
import org.springblade.core.tool.utils.DateUtil;
import org.springblade.core.tool.utils.ImageUtil;
import org.springblade.core.tool.utils.SpringUtil;
import org.springblade.core.tool.utils.StringPool;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
/**
* 文件代理类
*
* @author Chill
*/
public class LocalFileProxyFactory implements IFileProxy {
/**
* 文件配置
*/
private static BladeFileProperties fileProperties;
private static BladeFileProperties getBladeFileProperties() {
if (fileProperties == null) {
fileProperties = SpringUtil.getBean(BladeFileProperties.class);
}
return fileProperties;
}
@Override
public File rename(File f, String path) {
File dest = new File(path);
f.renameTo(dest);
return dest;
}
@Override
public String[] path(File f, String dir) {
//避免网络延迟导致时间不同步
long time = System.nanoTime();
StringBuilder uploadPath = new StringBuilder()
.append(getFileDir(dir, getBladeFileProperties().getUploadRealPath()))
.append(time)
.append(getFileExt(f.getName()));
StringBuilder virtualPath = new StringBuilder()
.append(getFileDir(dir, getBladeFileProperties().getUploadCtxPath()))
.append(time)
.append(getFileExt(f.getName()));
return new String[]{BladeFileUtil.formatUrl(uploadPath.toString()), BladeFileUtil.formatUrl(virtualPath.toString())};
}
/**
* 获取文件后缀
*
* @param fileName 文件名
* @return 文件后缀
*/
public static String getFileExt(String fileName) {
if (!fileName.contains(StringPool.DOT)) {
return ".jpg";
} else {
return fileName.substring(fileName.lastIndexOf(StringPool.DOT));
}
}
/**
* 获取文件保存地址
*
* @param dir 目录
* @param saveDir 保存目录
* @return 地址
*/
public static String getFileDir(String dir, String saveDir) {
StringBuilder newFileDir = new StringBuilder();
newFileDir.append(saveDir)
.append(File.separator).append(dir).append(File.separator).append(DateUtil.format(DateUtil.now(), "yyyyMMdd"))
.append(File.separator);
return newFileDir.toString();
}
/**
* 图片压缩
*
* @param path 文件地址
*/
@Override
public void compress(String path) {
try {
ImageUtil.zoomScale(ImageUtil.readImage(path), new FileOutputStream(new File(path)), null, getBladeFileProperties().getCompressScale(), getBladeFileProperties().getCompressFlag());
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
@@ -0,0 +1,73 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.boot.handler;
import jakarta.annotation.Nonnull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.boot.error.ErrorType;
import org.springblade.core.boot.error.ErrorUtil;
import org.springblade.core.context.BladeContext;
import org.springblade.core.launch.props.BladeProperties;
import org.springblade.core.log.constant.EventConstant;
import org.springblade.core.log.event.ErrorLogEvent;
import org.springblade.core.log.model.LogError;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.ApplicationEventPublisher;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
/**
* 异步任务异常处理器
*
* @author BladeX
*/
@Slf4j
@RequiredArgsConstructor
public class BladeAsyncUncaughtExceptionHandler implements AsyncUncaughtExceptionHandler {
private final BladeContext bladeContext;
private final BladeProperties bladeProperties;
private final ApplicationEventPublisher eventPublisher;
@Override
public void handleUncaughtException(@Nonnull Throwable error, @Nonnull Method method, @Nonnull Object... params) {
log.error("Unexpected exception occurred invoking async method: {}", method, error);
LogError logError = new LogError();
// 服务信息、环境、异常类型
logError.setParams(ErrorType.ASYNC.getType());
logError.setEnv(bladeProperties.getEnv());
logError.setServiceId(bladeProperties.getName());
logError.setRequestUri(bladeContext.getRequestId());
// 堆栈信息
ErrorUtil.initErrorInfo(error, logError);
Map<String, Object> event = new HashMap<>(16);
event.put(EventConstant.EVENT_LOG, logError);
eventPublisher.publishEvent(new ErrorLogEvent(event));
}
}
@@ -0,0 +1,101 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.boot.props;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* BladeFileProperties
*
* @author Chill
*/
@Getter
@Setter
@ConfigurationProperties("blade.file")
public class BladeFileProperties {
/**
* 远程上传模式
*/
private boolean remoteMode = false;
/**
* 外网地址
*/
private String uploadDomain = "http://127.0.0.1:8999";
/**
* 上传下载路径(物理路径)
*/
private String remotePath = System.getProperty("user.dir") + "/target/blade";
/**
* 上传路径(相对路径)
*/
private String uploadPath = "/upload";
/**
* 下载路径
*/
private String downloadPath = "/download";
/**
* 图片压缩
*/
private Boolean compress = false;
/**
* 图片压缩比例
*/
private Double compressScale = 2.00;
/**
* 图片缩放选择:true放大;false缩小
*/
private Boolean compressFlag = false;
/**
* 项目物理路径
*/
private String realPath = System.getProperty("user.dir");
/**
* 项目相对路径
*/
private String contextPath = "/";
public String getUploadRealPath() {
return (remoteMode ? remotePath : realPath) + uploadPath;
}
public String getUploadCtxPath() {
return contextPath + uploadPath;
}
}
@@ -0,0 +1,52 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.boot.props;
import lombok.Getter;
import lombok.Setter;
import org.springblade.core.tool.utils.PathUtil;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.lang.Nullable;
/**
* 文件上传配置
*
* @author Chill
*/
@Getter
@Setter
@RefreshScope
@ConfigurationProperties("blade.upload")
public class BladeUploadProperties {
/**
* 文件保存目录,默认:jar 包同级目录
*/
@Nullable
private String savePath = PathUtil.getJarPath();
}
@@ -0,0 +1,125 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.boot.request;
import lombok.Getter;
import org.springblade.core.tool.utils.WebUtil;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import jakarta.servlet.ReadListener;
import jakarta.servlet.ServletInputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
/**
* 全局Request包装
*
* @author Chill
*/
public class BladeHttpServletRequestWrapper extends HttpServletRequestWrapper {
/**
* 没被包装过的HttpServletRequest(特殊场景,需要自己过滤)
* 获取初始request
*/
@Getter
private final HttpServletRequest orgRequest;
/**
* 缓存报文,支持多次读取流
*/
private byte[] body;
public BladeHttpServletRequestWrapper(HttpServletRequest request) {
super(request);
orgRequest = request;
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
@Override
public ServletInputStream getInputStream() throws IOException {
if (super.getHeader(HttpHeaders.CONTENT_TYPE) == null) {
return super.getInputStream();
}
if (super.getHeader(HttpHeaders.CONTENT_TYPE).startsWith(MediaType.MULTIPART_FORM_DATA_VALUE)) {
return super.getInputStream();
}
if (body == null) {
body = WebUtil.getRequestBody(super.getInputStream()).getBytes();
}
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body);
return new ServletInputStream() {
@Override
public int read() {
return byteArrayInputStream.read();
}
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
};
}
/**
* 获取初始request
*
* @param request request
* @return HttpServletRequest
*/
public static HttpServletRequest getOrgRequest(HttpServletRequest request) {
if (request instanceof BladeHttpServletRequestWrapper) {
return ((BladeHttpServletRequestWrapper) request).getOrgRequest();
}
return request;
}
}
@@ -0,0 +1,216 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.boot.request;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import lombok.AllArgsConstructor;
import org.springblade.core.tool.utils.WebUtil;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PatternMatchUtils;
import java.io.IOException;
import java.util.List;
/**
* Request全局过滤
*
* @author Chill
*/
@AllArgsConstructor
public class BladeRequestFilter implements Filter {
/**
* 请求配置
*/
private final RequestProperties requestProperties;
/**
* 路径匹配
*/
private final AntPathMatcher antPathMatcher = new AntPathMatcher();
/**
* 默认拦截路径
*/
private final List<String> defaultBlockUrl = List.of("/**/actuator/**", "/health/**");
/**
* 默认白名单
*/
private final List<String> defaultWhiteList = List.of("127.0.0.1", "172.30.*.*", "192.168.*.*", "10.*.*.*", "0:0:0:0:0:0:0:1");
/**
* 默认提示信息
*/
private static final String DEFAULT_MESSAGE = "当前请求被拒绝,请联系管理员!";
/**
* 方法不允许提示信息
*/
private static final String METHOD_NOT_ALLOWED_MESSAGE = "当前请求方法不被允许,请联系管理员!";
@Override
public void init(FilterConfig config) {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// 获取请求
HttpServletRequest httpRequest = (HttpServletRequest) request;
// 判断 拦截请求 与 白名单
if (requestProperties.getEnabled()) {
// 获取请求路径
String path = httpRequest.getServletPath();
// 获取请求IP
String ip = WebUtil.getIP(httpRequest);
// 获取请求方法
String method = httpRequest.getMethod();
// 判断是否拦截请求
if (isRequestBlock(path, ip)) {
throw new ServletException(DEFAULT_MESSAGE);
}
// 判断请求方法是否允许
if (!isMethodAllowed(path, method)) {
throw new ServletException(METHOD_NOT_ALLOWED_MESSAGE);
}
// 判断是否跳过Request包装
if (isRequestSkip(path)) {
chain.doFilter(request, response);
}
// 采用Request包装
else {
BladeHttpServletRequestWrapper bladeRequest = new BladeHttpServletRequestWrapper(httpRequest);
chain.doFilter(bladeRequest, response);
}
}
// 采用原版Request请求
else {
chain.doFilter(request, response);
}
}
/**
* 是否白名单
*
* @param ip ip地址
* @return boolean
*/
private boolean isWhiteList(String ip) {
List<String> whiteList = requestProperties.getWhiteList();
String[] defaultWhiteIps = defaultWhiteList.toArray(new String[0]);
String[] whiteIps = whiteList.toArray(new String[0]);
return PatternMatchUtils.simpleMatch(defaultWhiteIps, ip) || PatternMatchUtils.simpleMatch(whiteIps, ip);
}
/**
* 是否黑名单
*
* @param ip ip地址
* @return boolean
*/
private boolean isBlackList(String ip) {
List<String> blackList = requestProperties.getBlackList();
String[] blackIps = blackList.toArray(new String[0]);
return PatternMatchUtils.simpleMatch(blackIps, ip);
}
/**
* 是否禁用请求访问
*
* @param path 请求路径
* @return boolean
*/
private boolean isRequestBlock(String path) {
List<String> blockUrl = requestProperties.getBlockUrl();
return defaultBlockUrl.stream().anyMatch(pattern -> antPathMatcher.match(pattern, path)) ||
blockUrl.stream().anyMatch(pattern -> antPathMatcher.match(pattern, path));
}
/**
* 是否拦截请求
*
* @param path 请求路径
* @param ip ip地址
* @return boolean
*/
private boolean isRequestBlock(String path, String ip) {
return (isRequestBlock(path) && !isWhiteList(ip)) || isBlackList(ip);
}
/**
* 是否跳过请求包装
*
* @param path 请求路径
* @return boolean
*/
private boolean isRequestSkip(String path) {
return requestProperties.getSkipUrl().stream().anyMatch(pattern -> antPathMatcher.match(pattern, path));
}
/**
* 判断请求方法是否允许
*
* @param path 请求路径
* @param method 请求方法
* @return boolean
*/
private boolean isMethodAllowed(String path, String method) {
// 优先匹配路径级别规则
List<HttpMethod> allowMethods = findAllowMethodsByPath(path);
// 如果没有匹配的路径规则,使用全局配置
if (allowMethods == null) {
allowMethods = requestProperties.getAllowMethods();
}
// 如果配置为空,允许所有方法
if (allowMethods == null || allowMethods.isEmpty()) {
return true;
}
// 检查请求方法是否在允许列表中
return allowMethods.stream().anyMatch(httpMethod -> httpMethod.matches(method));
}
/**
* 根据路径查找匹配的方法规则
*
* @param path 请求路径
* @return 允许的方法列表,未匹配返回null
*/
private List<HttpMethod> findAllowMethodsByPath(String path) {
List<MethodRule> methodRules = requestProperties.getMethodRules();
if (methodRules == null || methodRules.isEmpty()) {
return null;
}
return methodRules.stream()
.filter(rule -> rule.getPattern() != null && antPathMatcher.match(rule.getPattern(), path))
.findFirst()
.map(MethodRule::getAllowMethods)
.orElse(null);
}
@Override
public void destroy() {
}
}
@@ -0,0 +1,113 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.boot.request;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
/**
* HTTP请求方法枚举
*
* @author Chill
*/
@Getter
@RequiredArgsConstructor
public enum HttpMethod {
/**
* GET请求
*/
GET("GET"),
/**
* POST请求
*/
POST("POST"),
/**
* PUT请求
*/
PUT("PUT"),
/**
* DELETE请求
*/
DELETE("DELETE"),
/**
* PATCH请求
*/
PATCH("PATCH"),
/**
* HEAD请求
*/
HEAD("HEAD"),
/**
* OPTIONS请求
*/
OPTIONS("OPTIONS"),
/**
* TRACE请求
*/
TRACE("TRACE");
/**
* 方法名称
*/
private final String name;
/**
* 根据方法名称解析枚举
*
* @param method 方法名称
* @return HttpMethod
*/
public static HttpMethod resolve(String method) {
if (method == null) {
return null;
}
for (HttpMethod httpMethod : values()) {
if (httpMethod.name.equalsIgnoreCase(method)) {
return httpMethod;
}
}
return null;
}
/**
* 判断是否匹配
*
* @param method 方法名称
* @return boolean
*/
public boolean matches(String method) {
return this.name.equalsIgnoreCase(method);
}
}
@@ -0,0 +1,51 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.boot.request;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
/**
* 请求方法规则配置
*
* @author Chill
*/
@Data
public class MethodRule {
/**
* 匹配路径模式,支持Ant风格,例如:/api/**, /admin/*
*/
private String pattern;
/**
* 允许的请求方法列表
*/
private List<HttpMethod> allowMethods = new ArrayList<>();
}
@@ -0,0 +1,78 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.boot.request;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.ArrayList;
import java.util.List;
/**
* Request配置类
*
* @author Chill
*/
@Data
@ConfigurationProperties("blade.request")
public class RequestProperties {
/**
* 开启自定义request
*/
private Boolean enabled = true;
/**
* 放行url
*/
private List<String> skipUrl = new ArrayList<>();
/**
* 禁用url
*/
private List<String> blockUrl = new ArrayList<>();
/**
* 白名单,支持通配符,例如:10.20.0.8*、10.20.0.*
*/
private List<String> whiteList = new ArrayList<>();
/**
* 黑名单,支持通配符,例如:10.20.0.8*、10.20.0.*
*/
private List<String> blackList = new ArrayList<>();
/**
* 全局允许的请求方法列表,为空则允许所有方法
*/
private List<HttpMethod> allowMethods = new ArrayList<>();
/**
* 路径级别的请求方法规则,优先级高于全局配置
*/
private List<MethodRule> methodRules = new ArrayList<>();
}
@@ -0,0 +1,8 @@
${AnsiColor.BLUE} ______ _ _ ___ ___
${AnsiColor.BLUE} | ___ \| | | | \ \ / /
${AnsiColor.BLUE} | |_/ /| | __ _ __| | ___ \ V /
${AnsiColor.BLUE} | ___ \| | / _` | / _` | / _ \ > <
${AnsiColor.BLUE} | |_/ /| || (_| || (_| || __/ / . \
${AnsiColor.BLUE} \____/ |_| \__,_| \__,_| \___|/__/ \__\
${AnsiColor.BLUE}:: BladeX ${blade.service.version} :: ${spring.application.name}:${AnsiColor.RED}${blade.env}${AnsiColor.BLUE} :: Running SpringBoot ${spring-boot.version} :: ${AnsiColor.BRIGHT_BLACK}
@@ -0,0 +1,63 @@
#服务器配置
server:
undertow:
# 线程配置
threads:
# 设置IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接, 默认设置每个CPU核心一个线程
io: 16
# 阻塞任务线程池, 当执行类似servlet请求阻塞操作, undertow会从这个线程池中取得线程,它的值设置取决于系统的负载
worker: 400
# 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作,有点类似netty的池化内存管理
buffer-size: 1024
# 是否分配的直接内存
direct-buffers: true
servlet:
# 编码配置
encoding:
charset: UTF-8
force: true
#spring配置
spring:
task:
execution:
# 线程名前缀
thread-name-prefix: BladeAsync-
# 线程池配置
pool:
# 核心线程数
core-size: 5
# 最大线程数
max-size: 10
# 队列容量
queue-capacity: 25
# 允许核心线程超时
allow-core-thread-timeout: true
# 线程保持存活时间
keep-alive: 60s
servlet:
multipart:
enabled: true
max-file-size: 1024MB
max-request-size: 1024MB
mvc:
throw-exception-if-no-handler-found: true
web:
resources:
add-mappings: false
devtools:
restart:
log-condition-evaluation-delta: false
blade:
request:
enabled: true
# 全局允许的请求方法
allow-methods:
- GET
- POST
- PUT
- DELETE
- PATCH
- HEAD
- OPTIONS
Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB