diff --git a/smart-admin-api-java17-springboot3/pom.xml b/smart-admin-api-java17-springboot3/pom.xml index 6de337d3..212f55df 100644 --- a/smart-admin-api-java17-springboot3/pom.xml +++ b/smart-admin-api-java17-springboot3/pom.xml @@ -57,6 +57,7 @@ 2.2 2.3.33 1.18.1 + 3.1.0 @@ -325,6 +326,12 @@ ${freemarker.version} + + org.apache.tika + tika-core + ${tika.version} + + @@ -414,4 +421,4 @@ - \ No newline at end of file + diff --git a/smart-admin-api-java17-springboot3/sa-base/pom.xml b/smart-admin-api-java17-springboot3/sa-base/pom.xml index 75c32c3b..97920440 100644 --- a/smart-admin-api-java17-springboot3/sa-base/pom.xml +++ b/smart-admin-api-java17-springboot3/sa-base/pom.xml @@ -273,7 +273,12 @@ freemarker + + org.apache.tika + tika-core + + - \ No newline at end of file + diff --git a/smart-admin-api-java17-springboot3/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/service/SecurityFileService.java b/smart-admin-api-java17-springboot3/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/service/SecurityFileService.java index 6774a43c..5a0117e3 100644 --- a/smart-admin-api-java17-springboot3/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/service/SecurityFileService.java +++ b/smart-admin-api-java17-springboot3/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/service/SecurityFileService.java @@ -2,10 +2,19 @@ package net.lab1024.sa.base.module.support.securityprotect.service; import jakarta.annotation.Resource; 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.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 private Level3ProtectConfigService level3ProtectConfigService; + // 定义白名单MIME类型 + private static final List 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()) { - return ResponseDTO.ok(); + if (level3ProtectConfigService.isFileDetectFlag()) { + String fileType = getFileMimeType(file); + if(ALLOWED_MIME_TYPES.stream() + .noneMatch(allowedType -> matchesMimeType(fileType, allowedType))){ + return ResponseDTO.userErrorParam("禁止上传此文件类型"); + } } - // 检测文件类型 - // ..... - 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); + } + } } -; \ No newline at end of file diff --git a/smart-admin-api-java8-springboot2/pom.xml b/smart-admin-api-java8-springboot2/pom.xml index f861e07e..f0567b0f 100644 --- a/smart-admin-api-java8-springboot2/pom.xml +++ b/smart-admin-api-java8-springboot2/pom.xml @@ -59,6 +59,7 @@ 2.2 2.3.33 1.18.1 + 2.9.3 @@ -370,6 +371,12 @@ ${freemarker.version} + + org.apache.tika + tika-core + ${tika.version} + + diff --git a/smart-admin-api-java8-springboot2/sa-base/pom.xml b/smart-admin-api-java8-springboot2/sa-base/pom.xml index 3c43d462..b85edb17 100644 --- a/smart-admin-api-java8-springboot2/sa-base/pom.xml +++ b/smart-admin-api-java8-springboot2/sa-base/pom.xml @@ -303,6 +303,11 @@ freemarker + + org.apache.tika + tika-core + ${tika.version} + diff --git a/smart-admin-api-java8-springboot2/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/service/SecurityFileService.java b/smart-admin-api-java8-springboot2/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/service/SecurityFileService.java index cdea03a4..cd1190a5 100644 --- a/smart-admin-api-java8-springboot2/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/service/SecurityFileService.java +++ b/smart-admin-api-java8-springboot2/sa-base/src/main/java/net/lab1024/sa/base/module/support/securityprotect/service/SecurityFileService.java @@ -1,11 +1,23 @@ package net.lab1024.sa.base.module.support.securityprotect.service; 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.web.multipart.MultipartFile; import javax.annotation.Resource; import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; /** * 三级等保 文件上传 相关 @@ -23,6 +35,28 @@ public class SecurityFileService { @Resource private Level3ProtectConfigService level3ProtectConfigService; + // 定义白名单MIME类型 + private static final List 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()) { - return ResponseDTO.ok(); + if (level3ProtectConfigService.isFileDetectFlag()) { + String fileType = getFileMimeType(file); + if(ALLOWED_MIME_TYPES.stream() + .noneMatch(allowedType -> matchesMimeType(fileType, allowedType))){ + return ResponseDTO.userErrorParam("禁止上传此文件类型"); + } } - // 检测文件类型 - // ..... - 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); + } + } } -; \ No newline at end of file