mirror of
https://gitee.com/lab1024/smart-admin.git
synced 2025-09-17 10:56:39 +08:00
更新:文件类型检测白名单
This commit is contained in:
parent
bb68f050bf
commit
55fc58da3d
@ -57,6 +57,7 @@
|
|||||||
<snakeyaml.version>2.2</snakeyaml.version>
|
<snakeyaml.version>2.2</snakeyaml.version>
|
||||||
<freemarker.version>2.3.33</freemarker.version>
|
<freemarker.version>2.3.33</freemarker.version>
|
||||||
<jsoup.version>1.18.1</jsoup.version>
|
<jsoup.version>1.18.1</jsoup.version>
|
||||||
|
<tika.version>3.1.0</tika.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
@ -325,6 +326,12 @@
|
|||||||
<version>${freemarker.version}</version>
|
<version>${freemarker.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.tika</groupId>
|
||||||
|
<artifactId>tika-core</artifactId>
|
||||||
|
<version>${tika.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</dependencyManagement>
|
</dependencyManagement>
|
||||||
|
@ -273,6 +273,11 @@
|
|||||||
<artifactId>freemarker</artifactId>
|
<artifactId>freemarker</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.tika</groupId>
|
||||||
|
<artifactId>tika-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,10 +2,19 @@ package net.lab1024.sa.base.module.support.securityprotect.service;
|
|||||||
|
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import net.lab1024.sa.base.common.domain.ResponseDTO;
|
import net.lab1024.sa.base.common.domain.ResponseDTO;
|
||||||
|
import org.apache.tika.config.TikaConfig;
|
||||||
|
import org.apache.tika.exception.TikaException;
|
||||||
|
import org.apache.tika.io.TikaInputStream;
|
||||||
|
import org.apache.tika.metadata.Metadata;
|
||||||
|
import org.apache.tika.metadata.TikaCoreProperties;
|
||||||
|
import org.apache.tika.mime.MediaType;
|
||||||
|
import org.apache.tika.mime.MimeTypes;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 三级等保 文件上传 相关
|
* 三级等保 文件上传 相关
|
||||||
@ -23,6 +32,28 @@ public class SecurityFileService {
|
|||||||
@Resource
|
@Resource
|
||||||
private Level3ProtectConfigService level3ProtectConfigService;
|
private Level3ProtectConfigService level3ProtectConfigService;
|
||||||
|
|
||||||
|
// 定义白名单MIME类型
|
||||||
|
private static final List<String> ALLOWED_MIME_TYPES = Arrays.asList(
|
||||||
|
"application/json",
|
||||||
|
"application/zip",
|
||||||
|
"application/x-7z-compressed",
|
||||||
|
"application/pdf",
|
||||||
|
"application/vnd.ms-excel",
|
||||||
|
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||||
|
"application/vnd.ms-powerpoint",
|
||||||
|
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
||||||
|
"application/msword",
|
||||||
|
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||||
|
"application/vnd.ms-works",
|
||||||
|
"text/csv",
|
||||||
|
"audio/*",
|
||||||
|
"video/*",
|
||||||
|
// 图片类型 svg有安全隐患,所以不使用"image/*"
|
||||||
|
"image/jpeg",
|
||||||
|
"image/png",
|
||||||
|
"image/gif",
|
||||||
|
"image/bmp"
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检测文件安全类型
|
* 检测文件安全类型
|
||||||
@ -38,15 +69,50 @@ public class SecurityFileService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 文件类型安全检测
|
// 文件类型安全检测
|
||||||
if (!level3ProtectConfigService.isFileDetectFlag()) {
|
if (level3ProtectConfigService.isFileDetectFlag()) {
|
||||||
return ResponseDTO.ok();
|
String fileType = getFileMimeType(file);
|
||||||
|
if(ALLOWED_MIME_TYPES.stream()
|
||||||
|
.noneMatch(allowedType -> matchesMimeType(fileType, allowedType))){
|
||||||
|
return ResponseDTO.userErrorParam("禁止上传此文件类型");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检测文件类型
|
|
||||||
// .....
|
|
||||||
|
|
||||||
return ResponseDTO.ok();
|
return ResponseDTO.ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取文件的 MIME 类型
|
||||||
|
*
|
||||||
|
* @param file 要检查的文件
|
||||||
|
* @return 文件的 MIME 类型
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public static String getFileMimeType(MultipartFile file) {
|
||||||
|
try {
|
||||||
|
TikaConfig tika = new TikaConfig();
|
||||||
|
Metadata metadata = new Metadata();
|
||||||
|
metadata.set(TikaCoreProperties.RESOURCE_NAME_KEY, file.getOriginalFilename());
|
||||||
|
TikaInputStream stream = TikaInputStream.get(file.getInputStream());
|
||||||
|
MediaType mimetype = tika.getDetector().detect(stream, metadata);
|
||||||
|
return mimetype.toString();
|
||||||
|
} catch (IOException | TikaException e) {
|
||||||
|
return MimeTypes.OCTET_STREAM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查文件的 MIME 类型是否与指定的MIME 类型匹配(支持通配符)
|
||||||
|
*
|
||||||
|
* @param fileType 文件的 MIME 类型
|
||||||
|
* @param mimetype MIME 类型(支持通配符)
|
||||||
|
* @return 是否匹配
|
||||||
|
*/
|
||||||
|
private static boolean matchesMimeType(String fileType, String mimetype) {
|
||||||
|
if (mimetype.endsWith("/*")) {
|
||||||
|
String prefix = mimetype.substring(0, mimetype.length() - 1);
|
||||||
|
return fileType.startsWith(prefix);
|
||||||
|
} else {
|
||||||
|
return fileType.equalsIgnoreCase(mimetype);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
;
|
|
@ -59,6 +59,7 @@
|
|||||||
<snakeyaml.version>2.2</snakeyaml.version>
|
<snakeyaml.version>2.2</snakeyaml.version>
|
||||||
<freemarker.version>2.3.33</freemarker.version>
|
<freemarker.version>2.3.33</freemarker.version>
|
||||||
<jsoup.version>1.18.1</jsoup.version>
|
<jsoup.version>1.18.1</jsoup.version>
|
||||||
|
<tika.version>2.9.3</tika.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
@ -370,6 +371,12 @@
|
|||||||
<version>${freemarker.version}</version>
|
<version>${freemarker.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.tika</groupId>
|
||||||
|
<artifactId>tika-core</artifactId>
|
||||||
|
<version>${tika.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</dependencyManagement>
|
</dependencyManagement>
|
||||||
|
@ -303,6 +303,11 @@
|
|||||||
<artifactId>freemarker</artifactId>
|
<artifactId>freemarker</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.tika</groupId>
|
||||||
|
<artifactId>tika-core</artifactId>
|
||||||
|
<version>${tika.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
@ -1,11 +1,23 @@
|
|||||||
package net.lab1024.sa.base.module.support.securityprotect.service;
|
package net.lab1024.sa.base.module.support.securityprotect.service;
|
||||||
|
|
||||||
import net.lab1024.sa.base.common.domain.ResponseDTO;
|
import net.lab1024.sa.base.common.domain.ResponseDTO;
|
||||||
|
import org.apache.tika.config.TikaConfig;
|
||||||
|
import org.apache.tika.detect.Detector;
|
||||||
|
import org.apache.tika.exception.TikaException;
|
||||||
|
import org.apache.tika.io.TikaInputStream;
|
||||||
|
import org.apache.tika.metadata.Metadata;
|
||||||
|
import org.apache.tika.metadata.TikaCoreProperties;
|
||||||
|
import org.apache.tika.mime.MediaType;
|
||||||
|
import org.apache.tika.mime.MimeTypes;
|
||||||
|
import org.apache.tika.parser.AutoDetectParser;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 三级等保 文件上传 相关
|
* 三级等保 文件上传 相关
|
||||||
@ -23,6 +35,28 @@ public class SecurityFileService {
|
|||||||
@Resource
|
@Resource
|
||||||
private Level3ProtectConfigService level3ProtectConfigService;
|
private Level3ProtectConfigService level3ProtectConfigService;
|
||||||
|
|
||||||
|
// 定义白名单MIME类型
|
||||||
|
private static final List<String> ALLOWED_MIME_TYPES = Arrays.asList(
|
||||||
|
"application/json",
|
||||||
|
"application/zip",
|
||||||
|
"application/x-7z-compressed",
|
||||||
|
"application/pdf",
|
||||||
|
"application/vnd.ms-excel",
|
||||||
|
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||||
|
"application/vnd.ms-powerpoint",
|
||||||
|
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
||||||
|
"application/msword",
|
||||||
|
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||||
|
"application/vnd.ms-works",
|
||||||
|
"text/csv",
|
||||||
|
"audio/*",
|
||||||
|
"video/*",
|
||||||
|
// 图片类型 svg有安全隐患,所以不使用"image/*"
|
||||||
|
"image/jpeg",
|
||||||
|
"image/png",
|
||||||
|
"image/gif",
|
||||||
|
"image/bmp"
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检测文件安全类型
|
* 检测文件安全类型
|
||||||
@ -38,15 +72,50 @@ public class SecurityFileService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 文件类型安全检测
|
// 文件类型安全检测
|
||||||
if (!level3ProtectConfigService.isFileDetectFlag()) {
|
if (level3ProtectConfigService.isFileDetectFlag()) {
|
||||||
return ResponseDTO.ok();
|
String fileType = getFileMimeType(file);
|
||||||
|
if(ALLOWED_MIME_TYPES.stream()
|
||||||
|
.noneMatch(allowedType -> matchesMimeType(fileType, allowedType))){
|
||||||
|
return ResponseDTO.userErrorParam("禁止上传此文件类型");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检测文件类型
|
|
||||||
// .....
|
|
||||||
|
|
||||||
return ResponseDTO.ok();
|
return ResponseDTO.ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取文件的 MIME 类型
|
||||||
|
*
|
||||||
|
* @param file 要检查的文件
|
||||||
|
* @return 文件的 MIME 类型
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public static String getFileMimeType(MultipartFile file) {
|
||||||
|
try {
|
||||||
|
TikaConfig tika = new TikaConfig();
|
||||||
|
Metadata metadata = new Metadata();
|
||||||
|
metadata.set(TikaCoreProperties.RESOURCE_NAME_KEY, file.getOriginalFilename());
|
||||||
|
TikaInputStream stream = TikaInputStream.get(file.getInputStream());
|
||||||
|
MediaType mimetype = tika.getDetector().detect(stream, metadata);
|
||||||
|
return mimetype.toString();
|
||||||
|
} catch (IOException | TikaException e) {
|
||||||
|
return MimeTypes.OCTET_STREAM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查文件的 MIME 类型是否与指定的MIME 类型匹配(支持通配符)
|
||||||
|
*
|
||||||
|
* @param fileType 文件的 MIME 类型
|
||||||
|
* @param mimetype MIME 类型(支持通配符)
|
||||||
|
* @return 是否匹配
|
||||||
|
*/
|
||||||
|
private static boolean matchesMimeType(String fileType, String mimetype) {
|
||||||
|
if (mimetype.endsWith("/*")) {
|
||||||
|
String prefix = mimetype.substring(0, mimetype.length() - 1);
|
||||||
|
return fileType.startsWith(prefix);
|
||||||
|
} else {
|
||||||
|
return fileType.equalsIgnoreCase(mimetype);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
;
|
|
Loading…
Reference in New Issue
Block a user