6 changed files with 263 additions and 0 deletions
@ -0,0 +1,18 @@ |
|||||
|
package com.zdxt.common.annotation; |
||||
|
|
||||
|
|
||||
|
import java.lang.annotation.ElementType; |
||||
|
import java.lang.annotation.Retention; |
||||
|
import java.lang.annotation.RetentionPolicy; |
||||
|
import java.lang.annotation.Target; |
||||
|
|
||||
|
/** |
||||
|
* @author |
||||
|
*/ |
||||
|
@Target({ElementType.METHOD}) |
||||
|
@Retention(RetentionPolicy.RUNTIME) |
||||
|
public @interface AdditionAllowUploadFileTypes { |
||||
|
String[] value() default ""; |
||||
|
} |
||||
|
|
||||
|
|
||||
@ -0,0 +1,115 @@ |
|||||
|
package com.zdxt.auth.framework.config; |
||||
|
|
||||
|
import cn.hutool.core.collection.CollUtil; |
||||
|
import cn.hutool.core.io.FileTypeUtil; |
||||
|
import cn.hutool.core.io.FileUtil; |
||||
|
import cn.hutool.extra.servlet.ServletUtil; |
||||
|
import com.alibaba.fastjson.JSONObject; |
||||
|
import com.google.common.collect.Lists; |
||||
|
import com.zdxt.common.ZDResponse; |
||||
|
import com.zdxt.common.annotation.AdditionAllowUploadFileTypes; |
||||
|
import org.apache.commons.collections4.CollectionUtils; |
||||
|
import org.apache.commons.lang3.StringUtils; |
||||
|
import org.springframework.http.MediaType; |
||||
|
import org.springframework.lang.NonNull; |
||||
|
import org.springframework.messaging.handler.HandlerMethod; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
import org.springframework.web.multipart.MultipartFile; |
||||
|
import org.springframework.web.multipart.MultipartHttpServletRequest; |
||||
|
import org.springframework.web.servlet.HandlerInterceptor; |
||||
|
|
||||
|
import javax.servlet.http.HttpServletRequest; |
||||
|
import javax.servlet.http.HttpServletResponse; |
||||
|
import java.io.IOException; |
||||
|
import java.io.InputStream; |
||||
|
import java.util.*; |
||||
|
|
||||
|
@Component |
||||
|
public class FileTypeFilter implements HandlerInterceptor { |
||||
|
private final FileTypeProperties fileTypeProperties; |
||||
|
|
||||
|
public FileTypeFilter(FileTypeProperties fileTypeProperties) { |
||||
|
this.fileTypeProperties = fileTypeProperties; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public boolean preHandle(@NonNull HttpServletRequest request, |
||||
|
@NonNull HttpServletResponse response, |
||||
|
@NonNull Object handler) throws Exception { |
||||
|
if (!(request instanceof MultipartHttpServletRequest)) { |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
Map<String, MultipartFile> allFiles = ((MultipartHttpServletRequest) request).getFileMap(); |
||||
|
List<String> notAllowedFiles = getNotAllowedFiles(handler, allFiles); |
||||
|
|
||||
|
if (CollUtil.isNotEmpty(notAllowedFiles)) { |
||||
|
String message = "不被允许的文件类型!不被允许的文件列表如下:" + notAllowedFiles; |
||||
|
ServletUtil.write(response, JSONObject.toJSONString(ZDResponse.error(message)), MediaType.APPLICATION_JSON_UTF8_VALUE); |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
private List<String> getNotAllowedFiles(Object handler, Map<String, MultipartFile> allFiles) throws IOException { |
||||
|
List<String> notAllowedFilenames = null; |
||||
|
for (MultipartFile file : allFiles.values()) { |
||||
|
if (!isAllowedFile(file, handler)) { |
||||
|
if (notAllowedFilenames == null) { |
||||
|
notAllowedFilenames = new ArrayList<>(); |
||||
|
} |
||||
|
notAllowedFilenames.add(file.getOriginalFilename()); |
||||
|
} |
||||
|
} |
||||
|
return notAllowedFilenames; |
||||
|
} |
||||
|
|
||||
|
boolean isAllowedFile(MultipartFile file, Object handler) throws IOException { |
||||
|
String fileName = file.getOriginalFilename(); |
||||
|
try (InputStream in = file.getInputStream()) { |
||||
|
String type = FileTypeUtil.getType(in); |
||||
|
if (StringUtils.isBlank(type)) { |
||||
|
// 无法通过文件头或者扩展名识别文件类型。
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
String extType = FileUtil.extName(fileName); |
||||
|
if (StringUtils.isBlank(extType)) { |
||||
|
// 后缀读不到类型,直接拒绝
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
// 文件头类型和后缀类型都要满足白名单
|
||||
|
if (CollectionUtils.containsAll(fileTypeProperties.getAllowedTypes(), |
||||
|
Lists.newArrayList(type.toLowerCase(Locale.ROOT), extType.toLowerCase(Locale.ROOT)))) { |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
String[] additionAllowUploadFileTypes = getAdditionAllowUploadFileTypes(handler); |
||||
|
return Arrays.stream(additionAllowUploadFileTypes) |
||||
|
.anyMatch(type::equalsIgnoreCase) && |
||||
|
Arrays.stream(additionAllowUploadFileTypes) |
||||
|
.anyMatch(extType::equalsIgnoreCase); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private String[] getAdditionAllowUploadFileTypes(Object handle) { |
||||
|
String[] res = new String[0]; |
||||
|
if (!(handle instanceof HandlerMethod)) { |
||||
|
return res; |
||||
|
} |
||||
|
|
||||
|
HandlerMethod handlerMethod = (HandlerMethod) handle; |
||||
|
if (handlerMethod.hasMethodAnnotation(AdditionAllowUploadFileTypes.class)) { |
||||
|
AdditionAllowUploadFileTypes types = handlerMethod.getMethodAnnotation(AdditionAllowUploadFileTypes.class); |
||||
|
if (types != null) { |
||||
|
String[] allowedTypes = types.value(); |
||||
|
if (allowedTypes != null) { |
||||
|
res = allowedTypes; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return res; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,88 @@ |
|||||
|
package com.zdxt.auth.framework.config; |
||||
|
|
||||
|
|
||||
|
import cn.hutool.core.collection.CollectionUtil; |
||||
|
import cn.hutool.core.util.StrUtil; |
||||
|
import com.google.common.collect.Lists; |
||||
|
import lombok.Data; |
||||
|
import org.slf4j.Logger; |
||||
|
import org.slf4j.LoggerFactory; |
||||
|
import org.springframework.boot.context.properties.ConfigurationProperties; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
|
||||
|
import javax.annotation.PostConstruct; |
||||
|
import java.util.HashSet; |
||||
|
import java.util.List; |
||||
|
import java.util.Locale; |
||||
|
import java.util.Set; |
||||
|
|
||||
|
/** |
||||
|
* 默认支持上传: |
||||
|
* 1. 文档类: "xlsx", "xls", "docx", "doc", "pdf", "txt" |
||||
|
* 2. 图片/视频类: "jpg", "jpeg", "png", "bmp", "mp4" |
||||
|
* 3. 压缩包:zip |
||||
|
* |
||||
|
* @author |
||||
|
*/ |
||||
|
@Component |
||||
|
@ConfigurationProperties(value = "ruoyi.filetype") |
||||
|
public class FileTypeProperties { |
||||
|
private static final Logger LOGGER = LoggerFactory.getLogger(FileTypeProperties.class); |
||||
|
|
||||
|
/** |
||||
|
* 默认允许的文件类型 |
||||
|
* |
||||
|
* @see cn.hutool.core.io.FileTypeUtil |
||||
|
*/ |
||||
|
private List<String> defaultAllowTypes = Lists.newArrayList( |
||||
|
"xlsx", "xls", "docx", "doc", "pdf", "txt", |
||||
|
"jpg", "jpeg", "bmp", "mp4", |
||||
|
"zip"); |
||||
|
|
||||
|
/** |
||||
|
* 自定义支持的扩展类型 |
||||
|
* |
||||
|
* @see cn.hutool.core.io.FileTypeUtil |
||||
|
*/ |
||||
|
private List<String> additionalAllowTypes; |
||||
|
|
||||
|
private Set<String> allowedTypes; |
||||
|
|
||||
|
public Set<String> getAllowedTypes() { |
||||
|
return allowedTypes; |
||||
|
} |
||||
|
|
||||
|
public void setAllowedTypes(Set<String> allowedTypes) { |
||||
|
this.allowedTypes = allowedTypes; |
||||
|
} |
||||
|
|
||||
|
public List<String> getAdditionalAllowTypes() { |
||||
|
return additionalAllowTypes; |
||||
|
} |
||||
|
|
||||
|
public void setAdditionalAllowTypes(List<String> additionalAllowTypes) { |
||||
|
this.additionalAllowTypes = additionalAllowTypes; |
||||
|
} |
||||
|
|
||||
|
@PostConstruct |
||||
|
public void preSetAllowedTypes() { |
||||
|
allowedTypes = new HashSet<>(); |
||||
|
|
||||
|
if (CollectionUtil.isNotEmpty(defaultAllowTypes)) { |
||||
|
for (String type : defaultAllowTypes) { |
||||
|
if (StrUtil.isNotBlank(type)) { |
||||
|
allowedTypes.add(type.toLowerCase(Locale.ROOT)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
if (CollectionUtil.isNotEmpty(additionalAllowTypes)) { |
||||
|
for (String type : additionalAllowTypes) { |
||||
|
if (StrUtil.isNotBlank(type)) { |
||||
|
allowedTypes.add(type.toLowerCase(Locale.ROOT)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
LOGGER.info("File Allowed Types 初始化完成!"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
@ -0,0 +1,31 @@ |
|||||
|
package com.zdxt.auth.framework.config; |
||||
|
|
||||
|
import org.springframework.context.annotation.Bean; |
||||
|
import org.springframework.context.annotation.Configuration; |
||||
|
import org.springframework.http.MediaType; |
||||
|
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer; |
||||
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; |
||||
|
|
||||
|
//@Configuration
|
||||
|
public class FileUploadConfiguration implements WebMvcConfigurer { |
||||
|
|
||||
|
@Bean |
||||
|
public WebMvcConfigurer multipartConfigurer() { |
||||
|
return new WebMvcConfigurer() { |
||||
|
@Override |
||||
|
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) { |
||||
|
// 设置媒体类型
|
||||
|
configurer.mediaType("json", MediaType.APPLICATION_JSON); |
||||
|
// 你可以继续添加更多的媒体类型
|
||||
|
//图片
|
||||
|
configurer.mediaType("png", MediaType.IMAGE_PNG); |
||||
|
configurer.mediaType("gif", MediaType.IMAGE_GIF); |
||||
|
configurer.mediaType("jpeg", MediaType.IMAGE_JPEG); |
||||
|
//TODO 视频
|
||||
|
|
||||
|
//文档类型 TODO doc
|
||||
|
configurer.mediaType("pdf", MediaType.APPLICATION_PDF); |
||||
|
} |
||||
|
}; |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue