Merge remote-tracking branch 'origin/springboot3' into springboot3_sas

This commit is contained in:
JEECG
2025-10-09 11:48:54 +08:00
12 changed files with 227 additions and 145 deletions

View File

@ -153,9 +153,9 @@ public class CommonUtils {
*/
public static String uploadLocal(MultipartFile mf,String bizPath,String uploadpath){
try {
//update-begin-author:liusq date:20210809 for: 过滤上传文件类型
SsrfFileTypeFilter.checkUploadFileType(mf);
//update-end-author:liusq date:20210809 for: 过滤上传文件类型
// 文件安全校验,防止上传漏洞文件
SsrfFileTypeFilter.checkUploadFileType(mf, bizPath);
String fileName = null;
File file = new File(uploadpath + File.separator + bizPath + File.separator );
if (!file.exists()) {

View File

@ -55,13 +55,11 @@ public class MinioUtil {
*/
public static String upload(MultipartFile file, String bizPath, String customBucket) throws Exception {
String fileUrl = "";
//update-begin-author:wangshuai date:20201012 for: 过滤上传文件夹名特殊字符,防止攻击
// 业务路径过滤,防止攻击
bizPath = StrAttackFilter.filter(bizPath);
//update-end-author:wangshuai date:20201012 for: 过滤上传文件夹名特殊字符,防止攻击
//update-begin-author:liusq date:20210809 for: 过滤上传文件类型
SsrfFileTypeFilter.checkUploadFileType(file);
//update-end-author:liusq date:20210809 for: 过滤上传文件类型
// 文件安全校验,防止上传漏洞文件
SsrfFileTypeFilter.checkUploadFileType(file, bizPath);
String newBucket = bucketName;
if(oConvertUtils.isNotEmpty(customBucket)){

View File

@ -2,6 +2,7 @@ package org.jeecg.common.util.filter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jeecg.common.exception.JeecgBootException;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
@ -149,29 +150,38 @@ public class SsrfFileTypeFilter {
public static void checkDownloadFileType(String filePath) throws IOException {
//文件后缀
String suffix = getFileTypeBySuffix(filePath);
log.info("suffix:{}", suffix);
log.debug(" 【文件下载校验】文件后缀 suffix: {}", suffix);
boolean isAllowExtension = FILE_TYPE_WHITE_LIST.contains(suffix.toLowerCase());
//是否允许下载的文件
if (!isAllowExtension) {
throw new IOException("下载失败,存在非法文件类型:" + suffix);
throw new JeecgBootException("下载失败,存在非法文件类型:" + suffix);
}
}
/**
* 上传文件类型过滤
*
* @param file
*/
public static void checkUploadFileType(MultipartFile file) throws Exception {
//获取文件真是后缀
String suffix = getFileType(file);
log.info("suffix:{}", suffix);
checkUploadFileType(file, null);
}
/**
* 上传文件类型过滤
*
* @param file
*/
public static void checkUploadFileType(MultipartFile file, String customPath) throws Exception {
//1. 路径安全校验
validatePathSecurity(customPath);
//2. 校验文件后缀和头
String suffix = getFileType(file, customPath);
log.info("【文件上传校验】文件后缀 suffix: {}customPath{}", suffix, customPath);
boolean isAllowExtension = FILE_TYPE_WHITE_LIST.contains(suffix.toLowerCase());
//是否允许下载的文件
if (!isAllowExtension) {
throw new Exception("上传失败,存在非法文件类型:" + suffix);
throw new JeecgBootException("上传失败,存在非法文件类型:" + suffix);
}
}
@ -183,7 +193,7 @@ public class SsrfFileTypeFilter {
* @throws Exception
*/
private static String getFileType(MultipartFile file) throws Exception {
private static String getFileType(MultipartFile file, String customPath) throws Exception {
//update-begin-author:liusq date:20230404 for: [issue/4672]方法造成的文件被占用注释掉此方法tomcat就能自动清理掉临时文件
String fileExtendName = null;
InputStream is = null;
@ -203,7 +213,7 @@ public class SsrfFileTypeFilter {
break;
}
}
log.info("-----获取到的指定文件类型------"+fileExtendName);
log.debug("-----获取到的指定文件类型------"+fileExtendName);
// 如果不是上述类型,则判断扩展名
if (StringUtils.isBlank(fileExtendName)) {
String fileName = file.getOriginalFilename();
@ -214,7 +224,6 @@ public class SsrfFileTypeFilter {
// 如果有扩展名,则返回扩展名
return getFileTypeBySuffix(fileName);
}
log.info("-----最終的文件类型------"+fileExtendName);
is.close();
return fileExtendName;
} catch (Exception e) {
@ -249,4 +258,34 @@ public class SsrfFileTypeFilter {
}
return stringBuilder.toString();
}
/**
* 路径安全校验
*/
private static void validatePathSecurity(String customPath) throws JeecgBootException {
if (customPath == null || customPath.trim().isEmpty()) {
return;
}
// 统一分隔符为 /
String normalized = customPath.replace("\\", "/");
// 1. 防止路径遍历攻击
if (normalized.contains("..") || normalized.contains("~")) {
throw new JeecgBootException("上传业务路径包含非法字符!");
}
// 2. 限制路径深度
int depth = normalized.split("/").length;
if (depth > 5) {
throw new JeecgBootException("上传业务路径深度超出限制!");
}
// 3. 限制字符集(只允许字母、数字、下划线、横线、斜杠)
if (!normalized.matches("^[a-zA-Z0-9/_-]+$")) {
throw new JeecgBootException("上传业务路径包含非法字符!");
}
}
}

View File

@ -97,9 +97,8 @@ public class OssBootUtil {
* @return oss 中的相对文件路径
*/
public static String upload(MultipartFile file, String fileDir,String customBucket) throws Exception {
//update-begin-author:liusq date:20210809 for: 过滤上传文件类型
// 文件安全校验,防止上传漏洞文件
SsrfFileTypeFilter.checkUploadFileType(file);
//update-end-author:liusq date:20210809 for: 过滤上传文件类型
String filePath = null;
initOss(endPoint, accessKeyId, accessKeySecret);

View File

@ -11,22 +11,19 @@ import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import io.micrometer.prometheusmetrics.PrometheusMeterRegistry;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.ObjectProvider;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.event.EventListener;
import org.springframework.http.CacheControl;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
@ -50,6 +47,7 @@ import java.util.concurrent.TimeUnit;
* @Author qinfeng
*
*/
@Slf4j
@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {
@ -157,16 +155,17 @@ public class WebMvcConfiguration implements WebMvcConfigurer {
/**
* 监听应用启动完成事件,确保 PrometheusMeterRegistry 已经初始化
* 在Bean初始化完成后立即配置PrometheusMeterRegistry避免在Meter注册后才配置MeterFilter
* for [QQYUN-12558]【监控】系统监控的头两个tab不好使接口404
* @param event
* @author chenrui
* @date 2025/5/26 16:46
*/
@EventListener
public void onApplicationReady(ApplicationReadyEvent event) {
if(null != meterRegistryPostProcessor){
meterRegistryPostProcessor.postProcessAfterInitialization(prometheusMeterRegistry, "");
@PostConstruct
public void initPrometheusMeterRegistry() {
// 确保在应用启动早期就配置MeterFilter避免警告
if (null != meterRegistryPostProcessor && null != prometheusMeterRegistry) {
meterRegistryPostProcessor.postProcessAfterInitialization(prometheusMeterRegistry, "prometheusMeterRegistry");
log.info("PrometheusMeterRegistry配置完成");
}
}