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

59
blade-core-boot/pom.xml Normal file
View File

@@ -0,0 +1,59 @@
<?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>
<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>
<!-- Auto -->
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-auto</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@@ -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 {
}

View File

@@ -0,0 +1,116 @@
/**
* 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.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.context.BladeRunnableWrapper;
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.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.AsyncConfigurerSupport;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 异步处理
*
* @author Chill
*/
@Slf4j
@Configuration
@EnableAsync
@EnableScheduling
@AllArgsConstructor
public class BladeExecutorConfiguration extends AsyncConfigurerSupport {
private final BladeContext bladeContext;
private final BladeProperties bladeProperties;
private final ApplicationEventPublisher publisher;
@Bean
public ThreadPoolTaskExecutorCustomizer taskExecutorCustomizer() {
return taskExecutor -> {
taskExecutor.setThreadNamePrefix("async-task-");
taskExecutor.setTaskDecorator(BladeRunnableWrapper::new);
taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
};
}
@Bean
public ThreadPoolTaskExecutorCustomizer taskSchedulerCustomizer() {
return taskExecutor -> {
taskExecutor.setThreadNamePrefix("async-scheduler");
taskExecutor.setTaskDecorator(BladeRunnableWrapper::new);
taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
};
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new BladeAsyncUncaughtExceptionHandler(bladeContext, bladeProperties, publisher);
}
@RequiredArgsConstructor
private static 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));
}
}
}

View File

@@ -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();
}
}

View File

@@ -0,0 +1,71 @@
/**
* 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.springblade.core.boot.resolver.TokenArgumentResolver;
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.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.List;
/**
* 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/");
}
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new TokenArgumentResolver());
}
}

View File

@@ -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: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.boot.config;
import lombok.AllArgsConstructor;
import org.springblade.core.boot.request.BladeRequestFilter;
import org.springblade.core.boot.request.RequestProperties;
import org.springblade.core.boot.request.XssProperties;
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;
import jakarta.servlet.DispatcherType;
/**
* 过滤器配置类
*
* @author Chill
*/
@AutoConfiguration
@AllArgsConstructor
@EnableConfigurationProperties({RequestProperties.class, XssProperties.class})
public class RequestConfiguration {
private final RequestProperties requestProperties;
private final XssProperties xssProperties;
/**
* 全局过滤器
*/
@Bean
public FilterRegistrationBean<BladeRequestFilter> bladeFilterRegistration() {
FilterRegistrationBean<BladeRequestFilter> registration = new FilterRegistrationBean<>();
registration.setDispatcherTypes(DispatcherType.REQUEST);
registration.setFilter(new BladeRequestFilter(requestProperties, xssProperties));
registration.addUrlPatterns("/*");
registration.setName("bladeRequestFilter");
registration.setOrder(Ordered.LOWEST_PRECEDENCE);
return registration;
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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());
}
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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();
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}

View File

@@ -0,0 +1,129 @@
/**
* 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 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特殊场景,需要自己过滤)
*/
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
*
* @return HttpServletRequest
*/
public HttpServletRequest getOrgRequest() {
return orgRequest;
}
/**
* 获取初始request
*
* @param request request
* @return HttpServletRequest
*/
public static HttpServletRequest getOrgRequest(HttpServletRequest request) {
if (request instanceof BladeHttpServletRequestWrapper) {
return ((BladeHttpServletRequestWrapper) request).getOrgRequest();
}
return request;
}
}

View File

@@ -0,0 +1,84 @@
/**
* 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.AllArgsConstructor;
import org.springframework.util.AntPathMatcher;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* Request全局过滤
*
* @author Chill
*/
@AllArgsConstructor
public class BladeRequestFilter implements Filter {
private final RequestProperties requestProperties;
private final XssProperties xssProperties;
private final AntPathMatcher antPathMatcher = new AntPathMatcher();
@Override
public void init(FilterConfig config) {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
String path = ((HttpServletRequest) request).getServletPath();
// 跳过 Request 包装
if (!requestProperties.getEnabled() || isRequestSkip(path)) {
chain.doFilter(request, response);
}
// 默认 Request 包装
else if (!xssProperties.getEnabled() || isXssSkip(path)) {
BladeHttpServletRequestWrapper bladeRequest = new BladeHttpServletRequestWrapper((HttpServletRequest) request);
chain.doFilter(bladeRequest, response);
}
// Xss Request 包装
else {
XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) request);
chain.doFilter(xssRequest, response);
}
}
private boolean isRequestSkip(String path) {
return requestProperties.getSkipUrl().stream().anyMatch(pattern -> antPathMatcher.match(pattern, path));
}
private boolean isXssSkip(String path) {
return xssProperties.getSkipUrl().stream().anyMatch(pattern -> antPathMatcher.match(pattern, path));
}
@Override
public void destroy() {
}
}

View File

@@ -0,0 +1,53 @@
/**
* 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<>();
}

View File

@@ -0,0 +1,536 @@
package org.springblade.core.boot.request;
import org.springblade.core.tool.utils.StringPool;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* HTML filtering utility for protecting against XSS (Cross Site Scripting).
* <p>
* This code is licensed LGPLv3
* <p>
* This code is a Java port of the original work in PHP by Cal Hendersen.
* http://code.iamcal.com/php/lib_filter/
* <p>
* The trickiest part of the translation was handling the differences in regex handling
* between PHP and Java. These resources were helpful in the process:
* <p>
* http://java.sun.com/j2se/1.4.2/docs/api/java/util/regex/Pattern.html
* http://us2.php.net/manual/en/reference.pcre.pattern.modifiers.php
* http://www.regular-expressions.info/modifiers.html
* <p>
* A note on naming conventions: instance variables are prefixed with a "v"; global
* constants are in all caps.
* <p>
* Sample use:
* String input = ...
* String clean = new HtmlFilter().filter( input );
* <p>
* The class is not thread safe. Create a new instance if in doubt.
* <p>
* If you find bugs or have suggestions on improvement (especially regarding
* performance), please contact us. The latest version of this
* source, and our contact details, can be found at http://xss-html-filter.sf.net
*
* @author Joseph O'Connell
* @author Cal Hendersen
* @author Michael Semb Wever
*/
public final class XssHtmlFilter {
/**
* regex flag union representing /si modifiers in php
**/
private static final int REGEX_FLAGS_SI = Pattern.CASE_INSENSITIVE | Pattern.DOTALL;
private static final Pattern P_COMMENTS = Pattern.compile("<!--(.*?)-->", Pattern.DOTALL);
private static final Pattern P_COMMENT = Pattern.compile("^!--(.*)--$", REGEX_FLAGS_SI);
private static final Pattern P_TAGS = Pattern.compile("<(.*?)>", Pattern.DOTALL);
private static final Pattern P_END_TAG = Pattern.compile("^/([a-z0-9]+)", REGEX_FLAGS_SI);
private static final Pattern P_START_TAG = Pattern.compile("^([a-z0-9]+)(.*?)(/?)$", REGEX_FLAGS_SI);
private static final Pattern P_QUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)=([\"'])(.*?)\\2", REGEX_FLAGS_SI);
private static final Pattern P_UNQUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)(=)([^\"\\s']+)", REGEX_FLAGS_SI);
private static final Pattern P_PROTOCOL = Pattern.compile("^([^:]+):", REGEX_FLAGS_SI);
private static final Pattern P_ENTITY = Pattern.compile("&#(\\d+);?");
private static final Pattern P_ENTITY_UNICODE = Pattern.compile("&#x([0-9a-f]+);?");
private static final Pattern P_ENCODE = Pattern.compile("%([0-9a-f]{2});?");
private static final Pattern P_VALID_ENTITIES = Pattern.compile("&([^&;]*)(?=(;|&|$))");
private static final Pattern P_VALID_QUOTES = Pattern.compile("(>|^)([^<]+?)(<|$)", Pattern.DOTALL);
private static final Pattern P_END_ARROW = Pattern.compile("^>");
private static final Pattern P_BODY_TO_END = Pattern.compile("<([^>]*?)(?=<|$)");
private static final Pattern P_XML_CONTENT = Pattern.compile("(^|>)([^<]*?)(?=>)");
private static final Pattern P_STRAY_LEFT_ARROW = Pattern.compile("<([^>]*?)(?=<|$)");
private static final Pattern P_STRAY_RIGHT_ARROW = Pattern.compile("(^|>)([^<]*?)(?=>)");
private static final Pattern P_AMP = Pattern.compile("&");
private static final Pattern P_QUOTE = Pattern.compile("");
private static final Pattern P_LEFT_ARROW = Pattern.compile("<");
private static final Pattern P_RIGHT_ARROW = Pattern.compile(">");
private static final Pattern P_BOTH_ARROWS = Pattern.compile("<>");
private static final ConcurrentMap<String, Pattern> P_REMOVE_PAIR_BLANKS = new ConcurrentHashMap<String, Pattern>();
private static final ConcurrentMap<String, Pattern> P_REMOVE_SELF_BLANKS = new ConcurrentHashMap<String, Pattern>();
/**
* set of allowed html elements, along with allowed attributes for each element
**/
private final Map<String, List<String>> vAllowed;
/**
* counts of open tags for each (allowable) html element
**/
private final Map<String, Integer> vTagCounts = new HashMap<String, Integer>();
/**
* html elements which must always be self-closing (e.g. "<img />")
**/
private final String[] vSelfClosingTags;
/**
* html elements which must always have separate opening and closing tags (e.g. "<b></b>")
**/
private final String[] vNeedClosingTags;
/**
* set of disallowed html elements
**/
private final String[] vDisallowed;
/**
* attributes which should be checked for valid protocols
**/
private final String[] vProtocolAtts;
/**
* allowed protocols
**/
private final String[] vAllowedProtocols;
/**
* tags which should be removed if they contain no content (e.g. "<b></b>" or "<b />")
**/
private final String[] vRemoveBlanks;
/**
* entities allowed within html markup
**/
private final String[] vAllowedEntities;
/**
* flag determining whether comments are allowed in input String.
*/
private final boolean stripComment;
private final boolean encodeQuotes;
private boolean vDebug = false;
/**
* flag determining whether to try to make tags when presented with "unbalanced"
* angle brackets (e.g. "<b text </b>" becomes "<b> text </b>"). If set to false,
* unbalanced angle brackets will be html escaped.
*/
private final boolean alwaysMakeTags;
/**
* Default constructor.
*/
public XssHtmlFilter() {
vAllowed = new HashMap<>();
final ArrayList<String> aAtts = new ArrayList<String>();
aAtts.add("href");
aAtts.add("target");
vAllowed.put("a", aAtts);
final ArrayList<String> imgAtts = new ArrayList<String>();
imgAtts.add("src");
imgAtts.add("width");
imgAtts.add("height");
imgAtts.add("alt");
vAllowed.put("img", imgAtts);
final ArrayList<String> noAtts = new ArrayList<String>();
vAllowed.put("b", noAtts);
vAllowed.put("strong", noAtts);
vAllowed.put("i", noAtts);
vAllowed.put("em", noAtts);
vSelfClosingTags = new String[]{"img"};
vNeedClosingTags = new String[]{"a", "b", "strong", "i", "em"};
vDisallowed = new String[]{};
vAllowedProtocols = new String[]{"http", "mailto", "https"};
vProtocolAtts = new String[]{"src", "href"};
vRemoveBlanks = new String[]{"a", "b", "strong", "i", "em"};
vAllowedEntities = new String[]{"amp", "gt", "lt", "quot"};
stripComment = true;
encodeQuotes = true;
alwaysMakeTags = false;
}
/**
* Set debug flag to true. Otherwise use default settings. See the default constructor.
*
* @param debug turn debug on with a true argument
*/
public XssHtmlFilter(final boolean debug) {
this();
vDebug = debug;
}
/**
* Map-parameter configurable constructor.
*
* @param conf map containing configuration. keys match field names.
*/
public XssHtmlFilter(final Map<String, Object> conf) {
assert conf.containsKey("vAllowed") : "configuration requires vAllowed";
assert conf.containsKey("vSelfClosingTags") : "configuration requires vSelfClosingTags";
assert conf.containsKey("vNeedClosingTags") : "configuration requires vNeedClosingTags";
assert conf.containsKey("vDisallowed") : "configuration requires vDisallowed";
assert conf.containsKey("vAllowedProtocols") : "configuration requires vAllowedProtocols";
assert conf.containsKey("vProtocolAtts") : "configuration requires vProtocolAtts";
assert conf.containsKey("vRemoveBlanks") : "configuration requires vRemoveBlanks";
assert conf.containsKey("vAllowedEntities") : "configuration requires vAllowedEntities";
vAllowed = Collections.unmodifiableMap((HashMap<String, List<String>>) conf.get("vAllowed"));
vSelfClosingTags = (String[]) conf.get("vSelfClosingTags");
vNeedClosingTags = (String[]) conf.get("vNeedClosingTags");
vDisallowed = (String[]) conf.get("vDisallowed");
vAllowedProtocols = (String[]) conf.get("vAllowedProtocols");
vProtocolAtts = (String[]) conf.get("vProtocolAtts");
vRemoveBlanks = (String[]) conf.get("vRemoveBlanks");
vAllowedEntities = (String[]) conf.get("vAllowedEntities");
stripComment = conf.containsKey("stripComment") ? (Boolean) conf.get("stripComment") : true;
encodeQuotes = conf.containsKey("encodeQuotes") ? (Boolean) conf.get("encodeQuotes") : true;
alwaysMakeTags = conf.containsKey("alwaysMakeTags") ? (Boolean) conf.get("alwaysMakeTags") : true;
}
private void reset() {
vTagCounts.clear();
}
private void debug(final String msg) {
if (vDebug) {
Logger.getAnonymousLogger().info(msg);
}
}
public static String chr(final int decimal) {
return String.valueOf((char) decimal);
}
public static String htmlSpecialChars(final String s) {
String result = s;
result = regexReplace(P_AMP, "&amp;", result);
result = regexReplace(P_QUOTE, "&quot;", result);
result = regexReplace(P_LEFT_ARROW, "&lt;", result);
result = regexReplace(P_RIGHT_ARROW, "&gt;", result);
return result;
}
//---------------------------------------------------------------
/**
* given a user submitted input String, filter out any invalid or restricted
* html.
*
* @param input text (i.e. submitted by a user) than may contain html
* @return "clean" version of input, with only valid, whitelisted html elements allowed
*/
public String filter(final String input) {
reset();
String s = input;
debug("************************************************");
debug(" INPUT: " + input);
s = escapeComments(s);
debug(" escapeComments: " + s);
s = balanceHtml(s);
debug(" balanceHtml: " + s);
s = checkTags(s);
debug(" checkTags: " + s);
s = processRemoveBlanks(s);
debug("processRemoveBlanks: " + s);
s = validateEntities(s);
debug(" validateEntites: " + s);
debug("************************************************\n\n");
return s;
}
public boolean isAlwaysMakeTags() {
return alwaysMakeTags;
}
public boolean isStripComments() {
return stripComment;
}
private String escapeComments(final String s) {
final Matcher m = P_COMMENTS.matcher(s);
final StringBuffer buf = new StringBuffer();
if (m.find()) {
final String match = m.group(1);
m.appendReplacement(buf, Matcher.quoteReplacement("<!--" + htmlSpecialChars(match) + "-->"));
}
m.appendTail(buf);
return buf.toString();
}
private String balanceHtml(String s) {
if (alwaysMakeTags) {
//
// try and form html
//
s = regexReplace(P_END_ARROW, "", s);
s = regexReplace(P_BODY_TO_END, "<$1>", s);
s = regexReplace(P_XML_CONTENT, "$1<$2", s);
} else {
//
// escape stray brackets
//
s = regexReplace(P_STRAY_LEFT_ARROW, "&lt;$1", s);
s = regexReplace(P_STRAY_RIGHT_ARROW, "$1$2&gt;<", s);
//
// the last regexp causes '<>' entities to appear
// (we need to do a lookahead assertion so that the last bracket can
// be used in the next pass of the regexp)
//
s = regexReplace(P_BOTH_ARROWS, "", s);
}
return s;
}
private String checkTags(String s) {
Matcher m = P_TAGS.matcher(s);
final StringBuffer buf = new StringBuffer();
while (m.find()) {
String replaceStr = m.group(1);
replaceStr = processTag(replaceStr);
m.appendReplacement(buf, Matcher.quoteReplacement(replaceStr));
}
m.appendTail(buf);
s = buf.toString();
// these get tallied in processTag
// (remember to reset before subsequent calls to filter method)
for (String key : vTagCounts.keySet()) {
for (int ii = 0; ii < vTagCounts.get(key); ii++) {
s += "</" + key + ">";
}
}
return s;
}
private String processRemoveBlanks(final String s) {
String result = s;
for (String tag : vRemoveBlanks) {
if (!P_REMOVE_PAIR_BLANKS.containsKey(tag)) {
P_REMOVE_PAIR_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?></" + tag + ">"));
}
result = regexReplace(P_REMOVE_PAIR_BLANKS.get(tag), "", result);
if (!P_REMOVE_SELF_BLANKS.containsKey(tag)) {
P_REMOVE_SELF_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?/>"));
}
result = regexReplace(P_REMOVE_SELF_BLANKS.get(tag), "", result);
}
return result;
}
private static String regexReplace(final Pattern regexPattern, final String replacement, final String s) {
Matcher m = regexPattern.matcher(s);
return m.replaceAll(replacement);
}
private String processTag(final String s) {
Matcher m = P_END_TAG.matcher(s);
if (m.find()) {
final String name = m.group(1).toLowerCase();
if (allowed(name)) {
if (!inArray(name, vSelfClosingTags)) {
if (vTagCounts.containsKey(name)) {
vTagCounts.put(name, vTagCounts.get(name) - 1);
return "</" + name + ">";
}
}
}
}
m = P_START_TAG.matcher(s);
if (m.find()) {
final String name = m.group(1).toLowerCase();
final String body = m.group(2);
String ending = m.group(3);
if (allowed(name)) {
String params = "";
final Matcher m2 = P_QUOTED_ATTRIBUTES.matcher(body);
final Matcher m3 = P_UNQUOTED_ATTRIBUTES.matcher(body);
final List<String> paramNames = new ArrayList<String>();
final List<String> paramValues = new ArrayList<String>();
while (m2.find()) {
paramNames.add(m2.group(1));
paramValues.add(m2.group(3));
}
while (m3.find()) {
paramNames.add(m3.group(1));
paramValues.add(m3.group(3));
}
String paramName, paramValue;
for (int ii = 0; ii < paramNames.size(); ii++) {
paramName = paramNames.get(ii).toLowerCase();
paramValue = paramValues.get(ii);
if (allowedAttribute(name, paramName)) {
if (inArray(paramName, vProtocolAtts)) {
paramValue = processParamProtocol(paramValue);
}
params += " " + paramName + "=\"" + paramValue + "\"";
}
}
if (inArray(name, vSelfClosingTags)) {
ending = " /";
}
if (inArray(name, vNeedClosingTags)) {
ending = "";
}
if (ending == null || ending.length() < 1) {
if (vTagCounts.containsKey(name)) {
vTagCounts.put(name, vTagCounts.get(name) + 1);
} else {
vTagCounts.put(name, 1);
}
} else {
ending = " /";
}
return "<" + name + params + ending + ">";
} else {
return "";
}
}
m = P_COMMENT.matcher(s);
if (!stripComment && m.find()) {
return "<" + m.group() + ">";
}
return "";
}
private String processParamProtocol(String s) {
s = decodeEntities(s);
final Matcher m = P_PROTOCOL.matcher(s);
if (m.find()) {
final String protocol = m.group(1);
if (!inArray(protocol, vAllowedProtocols)) {
// bad protocol, turn into local anchor link instead
s = "#" + s.substring(protocol.length() + 1);
if (s.startsWith(StringPool.DOUBLE_SLASH)) {
s = "#" + s.substring(3);
}
}
}
return s;
}
private String decodeEntities(String s) {
StringBuffer buf = new StringBuffer();
Matcher m = P_ENTITY.matcher(s);
while (m.find()) {
final String match = m.group(1);
final int decimal = Integer.decode(match);
m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
}
m.appendTail(buf);
s = buf.toString();
buf = new StringBuffer();
m = P_ENTITY_UNICODE.matcher(s);
while (m.find()) {
final String match = m.group(1);
final int decimal = Integer.valueOf(match, 16).intValue();
m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
}
m.appendTail(buf);
s = buf.toString();
buf = new StringBuffer();
m = P_ENCODE.matcher(s);
while (m.find()) {
final String match = m.group(1);
final int decimal = Integer.valueOf(match, 16).intValue();
m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
}
m.appendTail(buf);
s = buf.toString();
s = validateEntities(s);
return s;
}
private String validateEntities(final String s) {
StringBuffer buf = new StringBuffer();
// validate entities throughout the string
Matcher m = P_VALID_ENTITIES.matcher(s);
while (m.find()) {
final String one = m.group(1);
final String two = m.group(2);
m.appendReplacement(buf, Matcher.quoteReplacement(checkEntity(one, two)));
}
m.appendTail(buf);
return encodeQuotes(buf.toString());
}
private String encodeQuotes(final String s) {
if (encodeQuotes) {
StringBuffer buf = new StringBuffer();
Matcher m = P_VALID_QUOTES.matcher(s);
while (m.find()) {
final String one = m.group(1);
final String two = m.group(2);
final String three = m.group(3);
m.appendReplacement(buf, Matcher.quoteReplacement(one + regexReplace(P_QUOTE, "&quot;", two) + three));
}
m.appendTail(buf);
return buf.toString();
} else {
return s;
}
}
private String checkEntity(final String preamble, final String term) {
return ";".equals(term) && isValidEntity(preamble)
? '&' + preamble
: "&amp;" + preamble;
}
private boolean isValidEntity(final String entity) {
return inArray(entity, vAllowedEntities);
}
private static boolean inArray(final String s, final String[] array) {
for (String item : array) {
if (item != null && item.equals(s)) {
return true;
}
}
return false;
}
private boolean allowed(final String name) {
return (vAllowed.isEmpty() || vAllowed.containsKey(name)) && !inArray(name, vDisallowed);
}
private boolean allowedAttribute(final String name, final String paramName) {
return allowed(name) && (vAllowed.isEmpty() || vAllowed.get(name).contains(paramName));
}
}

View File

@@ -0,0 +1,184 @@
/**
* 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 org.springblade.core.tool.utils.StringUtil;
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;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* XSS过滤
*
* @author Chill
*/
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
/**
* 没被包装过的HttpServletRequest特殊场景,需要自己过滤)
*/
private final HttpServletRequest orgRequest;
/**
* 缓存报文,支持多次读取流
*/
private byte[] body;
/**
* html过滤
*/
private final static XssHtmlFilter HTML_FILTER = new XssHtmlFilter();
public XssHttpServletRequestWrapper(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 = xssEncode(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) {
}
};
}
@Override
public String getParameter(String name) {
String value = super.getParameter(xssEncode(name));
if (StringUtil.isNotBlank(value)) {
value = xssEncode(value);
}
return value;
}
@Override
public String[] getParameterValues(String name) {
String[] parameters = super.getParameterValues(name);
if (parameters == null || parameters.length == 0) {
return null;
}
for (int i = 0; i < parameters.length; i++) {
parameters[i] = xssEncode(parameters[i]);
}
return parameters;
}
@Override
public Map<String, String[]> getParameterMap() {
Map<String, String[]> map = new LinkedHashMap<>();
Map<String, String[]> parameters = super.getParameterMap();
for (String key : parameters.keySet()) {
String[] values = parameters.get(key);
for (int i = 0; i < values.length; i++) {
values[i] = xssEncode(values[i]);
}
map.put(key, values);
}
return map;
}
@Override
public String getHeader(String name) {
String value = super.getHeader(xssEncode(name));
if (StringUtil.isNotBlank(value)) {
value = xssEncode(value);
}
return value;
}
private String xssEncode(String input) {
return HTML_FILTER.filter(input);
}
/**
* 获取初始request
*
* @return HttpServletRequest
*/
public HttpServletRequest getOrgRequest() {
return orgRequest;
}
/**
* 获取初始request
*
* @param request request
* @return HttpServletRequest
*/
public static HttpServletRequest getOrgRequest(HttpServletRequest request) {
if (request instanceof XssHttpServletRequestWrapper) {
return ((XssHttpServletRequestWrapper) request).getOrgRequest();
}
return request;
}
}

View File

@@ -0,0 +1,53 @@
/**
* 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;
/**
* Xss配置类
*
* @author Chill
*/
@Data
@ConfigurationProperties("blade.xss")
public class XssProperties {
/**
* 开启xss
*/
private Boolean enabled = true;
/**
* 放行url
*/
private List<String> skipUrl = new ArrayList<>();
}

View File

@@ -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.resolver;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.secure.BladeUser;
import org.springblade.core.secure.utils.AuthUtil;
import org.springframework.core.MethodParameter;
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;
/**
* Token转化BladeUser
*
* @author Chill
*/
@Slf4j
public class TokenArgumentResolver implements HandlerMethodArgumentResolver {
/**
* 入参筛选
*
* @param methodParameter 参数集合
* @return 格式化后的参数
*/
@Override
public boolean supportsParameter(MethodParameter methodParameter) {
return methodParameter.getParameterType().equals(BladeUser.class);
}
/**
* 出参设置
*
* @param methodParameter 入参集合
* @param modelAndViewContainer model 和 view
* @param nativeWebRequest web相关
* @param webDataBinderFactory 入参解析
* @return 包装对象
*/
@Override
public Object resolveArgument(MethodParameter methodParameter,
ModelAndViewContainer modelAndViewContainer,
NativeWebRequest nativeWebRequest,
WebDataBinderFactory webDataBinderFactory) {
return AuthUtil.getUser();
}
}

View File

@@ -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}

View File

@@ -0,0 +1,35 @@
#服务器配置
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:
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

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB