fix bugs
This commit is contained in:
59
blade-core-boot/pom.xml
Normal file
59
blade-core-boot/pom.xml
Normal 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>
|
||||
@@ -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,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));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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,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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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,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,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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<>();
|
||||
|
||||
}
|
||||
@@ -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, "&", result);
|
||||
result = regexReplace(P_QUOTE, """, result);
|
||||
result = regexReplace(P_LEFT_ARROW, "<", result);
|
||||
result = regexReplace(P_RIGHT_ARROW, ">", 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, "<$1", s);
|
||||
s = regexReplace(P_STRAY_RIGHT_ARROW, "$1$2><", 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, """, 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
|
||||
: "&" + 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));
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<>();
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
8
blade-core-boot/src/main/resources/banner.txt
Normal file
8
blade-core-boot/src/main/resources/banner.txt
Normal 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}
|
||||
35
blade-core-boot/src/main/resources/blade-boot.yml
Normal file
35
blade-core-boot/src/main/resources/blade-boot.yml
Normal 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
|
||||
|
||||
BIN
blade-core-boot/src/main/resources/static/favicon.ico
Normal file
BIN
blade-core-boot/src/main/resources/static/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 66 KiB |
Reference in New Issue
Block a user