Ludc
2025-11-18 4470052c3b6bdeb18e45987f8aa293d1e93d0552
所有文件上传接口增加文件安全校验逻辑。
已添加2个文件
已修改8个文件
706 ■■■■■ 文件已修改
Source/UBCS/ubcs-common/src/main/java/com/vci/ubcs/common/validator/ComprehensiveFileValidator.java 477 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Source/UBCS/ubcs-gateway/src/main/java/com/vci/ubcs/gateway/filter/EssentialSecurityFilter.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Source/UBCS/ubcs-ops/ubcs-flow/src/main/java/com/vci/ubcs/flow/engine/controller/FlowManagerController.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Source/UBCS/ubcs-ops/ubcs-resource/src/main/java/com/vci/ubcs/resource/controller/FileController.java 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Source/UBCS/ubcs-ops/ubcs-resource/src/main/java/com/vci/ubcs/resource/endpoint/OssEndpoint.java 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Source/UBCS/ubcs-service/ubcs-code/src/main/java/com/vci/ubcs/code/controller/CodeClassifyController.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Source/UBCS/ubcs-service/ubcs-code/src/main/java/com/vci/ubcs/code/controller/MdmEngineController.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Source/UBCS/ubcs-service/ubcs-deploy/src/main/java/com/vci/ubcs/deploy/controller/DeployAppsController.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Source/UBCS/ubcs-service/ubcs-system/src/main/java/com/vci/ubcs/system/controller/RegionController.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Source/UBCS/ubcs-service/ubcs-user/src/main/java/com/vci/ubcs/system/user/controller/UserController.java 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Source/UBCS/ubcs-common/src/main/java/com/vci/ubcs/common/validator/ComprehensiveFileValidator.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,477 @@
package com.vci.ubcs.common.validator;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.PostConstruct;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;
/**
 * æ–‡ä»¶å®‰å…¨éªŒè¯å™¨
 */
@Component
@Slf4j
public class ComprehensiveFileValidator {
    /**
     * æ–‡ä»¶ç™½åå•
     */
    @Value("${app.upload.security.allowed-extensions:jpg,jpeg,png,pdf}")
    private String allowedExtensionsConfig;
    /**
     * å¤šé‡æ‰©å±•名文件禁止
     */
    @Value("${app.upload.security.prevent-multiple-extensions:true}")
    private boolean preventMultipleExtensions;
    /**
     * é™åˆ¶çš„危险文件类型
     */
    @Value("${app.upload.security.dangerous-primary-extensions:jsp,jspx,php,asp,aspx,war,exe,sh,bat}")
    private String dangerousExtensionsConfig;
    /**
     * æ–‡ä»¶å†…容类型是否匹配校验
     */
    @Value("${app.upload.security.validate-content-type:true}")
    private boolean validateContentType;
    /**
     * æ–‡ä»¶å¤´éªŒè¯
     */
    @Value("${app.upload.security.validate-file-header:true}")
    private boolean validateFileHeader;
    /**
     * ä¸¥æ ¼æ¨¡å¼
     */
    @Value("${app.upload.security.strict-mode:false}")
    private boolean strictMode;
    /**
     * å…è®¸ä¸Šä¼ çš„后缀
     */
    private Set<String> allowedExtensions;
    /**
     * å±é™©çš„æ–‡ä»¶åŽç¼€
     */
    private Set<String> dangerousPrimaryExtensions;
    @PostConstruct
    public void init() {
        // è§£æžé€—号分隔的配置
        this.allowedExtensions = parseCommaSeparatedConfig(allowedExtensionsConfig);
        this.dangerousPrimaryExtensions = parseCommaSeparatedConfig(dangerousExtensionsConfig);
        log.info("文件上传验证器初始化完成");
        log.info("允许的扩展名: {}", allowedExtensions);
        log.info("危险扩展名: {}", dangerousPrimaryExtensions);
    }
    private Set<String> parseCommaSeparatedConfig(String config) {
        if (config == null || config.trim().isEmpty()) {
            return new HashSet<>();
        }
        return Arrays.stream(config.split(","))
            .map(String::trim)
            .map(String::toLowerCase)
            .collect(Collectors.toSet());
    }
    /**
     * éªŒè¯å•个文件
     */
    public UploadValidationResult validateFile(MultipartFile file) {
        UploadValidationResult result = new UploadValidationResult();
        try {
            // åŸºç¡€æ£€æŸ¥
            if (!basicValidation(file, result)) {
                return result;
            }
            String filename = file.getOriginalFilename();
            // æ–‡ä»¶åå®‰å…¨éªŒè¯
            if (!filenameSecurityValidation(filename, result)) {
                return result;
            }
            // å†…容安全验证
            if (!contentSecurityValidation(file, result)) {
                return result;
            }
            result.setValid(true);
            result.setMessage("文件验证通过");
        } catch (Exception e) {
            log.error("文件验证异常", e);
            result.setValid(false);
            result.setMessage("验证过程发生异常");
        }
        return result;
    }
    /**
     * éªŒè¯å¤šä¸ªæ–‡ä»¶
     * @param files æ–‡ä»¶åˆ—表
     * @return å¤šä¸ªæ–‡ä»¶çš„验证结果
     */
    public MultiUploadValidationResult validateFiles(List<MultipartFile> files) {
        return validateFiles(files, false);
    }
    /**
     * éªŒè¯å¤šä¸ªæ–‡ä»¶
     * @param files æ–‡ä»¶åˆ—表
     * @param stopOnFirstError é‡åˆ°ç¬¬ä¸€ä¸ªé”™è¯¯æ˜¯å¦åœæ­¢éªŒè¯
     * @return å¤šä¸ªæ–‡ä»¶çš„验证结果
     */
    public MultiUploadValidationResult validateFiles(List<MultipartFile> files, boolean stopOnFirstError) {
        MultiUploadValidationResult result = new MultiUploadValidationResult();
        if (files == null || files.isEmpty()) {
            result.setValid(false);
            result.setMessage("文件列表为空");
            return result;
        }
        List<FileValidationDetail> details = new ArrayList<>();
        boolean allValid = true;
        for (int i = 0; i < files.size(); i++) {
            MultipartFile file = files.get(i);
            FileValidationDetail detail = new FileValidationDetail();
            detail.setFileName(file.getOriginalFilename());
            detail.setFileIndex(i);
            detail.setFileSize(file.getSize());
            // éªŒè¯å•个文件
            UploadValidationResult singleResult = validateFile(file);
            detail.setValid(singleResult.isValid());
            detail.setMessage(singleResult.getMessage());
            detail.setDetectedType(singleResult.getDetectedType());
            details.add(detail);
            if (!singleResult.isValid()) {
                allValid = false;
                if (stopOnFirstError) {
                    // é‡åˆ°é”™è¯¯ä¸”设置为快速失败,立即返回
                    result.setValid(false);
                    result.setMessage("第" + (i + 1) + "个文件验证失败: " + file.getOriginalFilename());
                    result.setDetails(details);
                    result.setFailedIndex(i);
                    return result;
                }
            }
        }
        result.setValid(allValid);
        result.setMessage(allValid ? "所有文件验证通过" : "部分文件验证失败");
        result.setDetails(details);
        result.setTotalFiles(files.size());
        result.setValidFiles((int) details.stream().filter(FileValidationDetail::isValid).count());
        result.setInvalidFiles((int) details.stream().filter(d -> !d.isValid()).count());
        return result;
    }
    /**
     * éªŒè¯å¤šä¸ªæ–‡ä»¶ï¼ˆæ•°ç»„版本)
     */
    public MultiUploadValidationResult validateFiles(MultipartFile[] files) {
        return validateFiles(Arrays.asList(files));
    }
    /**
     * éªŒè¯å¤šä¸ªæ–‡ä»¶ï¼ˆæ•°ç»„版本,可设置是否快速失败)
     */
    public MultiUploadValidationResult validateFiles(MultipartFile[] files, boolean stopOnFirstError) {
        return validateFiles(Arrays.asList(files), stopOnFirstError);
    }
    /**
     * æ‰¹é‡éªŒè¯æ–‡ä»¶å¹¶è¿”回有效的文件列表
     */
    public List<MultipartFile> getValidFiles(List<MultipartFile> files) {
        MultiUploadValidationResult result = validateFiles(files);
        List<MultipartFile> validFiles = new ArrayList<>();
        for (int i = 0; i < files.size(); i++) {
            if (result.getDetails().get(i).isValid()) {
                validFiles.add(files.get(i));
            }
        }
        return validFiles;
    }
    /**
     * æ£€æŸ¥æ˜¯å¦æ‰€æœ‰æ–‡ä»¶éƒ½æœ‰æ•ˆ
     */
    public boolean areAllFilesValid(List<MultipartFile> files) {
        MultiUploadValidationResult result = validateFiles(files);
        return result.isValid();
    }
    // åŽŸæœ‰çš„ç§æœ‰æ–¹æ³•ä¿æŒä¸å˜
    private boolean basicValidation(MultipartFile file, UploadValidationResult result) {
        if (file == null || file.isEmpty()) {
            result.setMessage("文件为空");
            return false;
        }
        String filename = file.getOriginalFilename();
        if (filename == null || filename.trim().isEmpty()) {
            result.setMessage("文件名为空");
            return false;
        }
        return true;
    }
    private boolean filenameSecurityValidation(String filename, UploadValidationResult result) {
        // è·¯å¾„遍历检查
        if (filename.contains("..") || filename.contains("/") || filename.contains("\\")) {
            result.setMessage("文件名包含危险字符");
            return false;
        }
        // æ‰©å±•名检查
        String finalExtension = getFinalExtension(filename);
        if (finalExtension.isEmpty() || !allowedExtensions.contains(finalExtension.toLowerCase())) {
            result.setMessage("不支持的文件类型: " + finalExtension);
            return false;
        }
        // å¤šé‡æ‰©å±•名检查
        if (preventMultipleExtensions && hasMultipleExtensions(filename)) {
            if (strictMode) {
                // ä¸¥æ ¼æ¨¡å¼ï¼šæ‹¦æˆªæ‰€æœ‰å¤šé‡æ‰©å±•名
                result.setMessage("多重扩展名文件被禁止");
                return false;
            } else {
                // æ™®é€šæ¨¡å¼ï¼šåªæ‹¦æˆªåŒ…含危险扩展名的多重扩展名
                if (containsDangerousExtension(filename)) {
                    result.setMessage("检测到伪装Webshell文件: " + filename);
                    return false;
                }
            }
        }
        return true;
    }
    private boolean contentSecurityValidation(MultipartFile file, UploadValidationResult result) {
        // å†…容类型验证
        if (validateContentType && !validateContentType(file)) {
            result.setMessage("文件内容类型不匹配");
            return false;
        }
        // æ–‡ä»¶å¤´éªŒè¯
        if (validateFileHeader && !validateFileHeader(file)) {
            result.setMessage("文件头验证失败");
            return false;
        }
        return true;
    }
    private boolean hasMultipleExtensions(String filename) {
        String name = getFileNameWithoutPath(filename);
        return name.chars().filter(ch -> ch == '.').count() > 1;
    }
    private boolean containsDangerousExtension(String filename) {
        String name = getFileNameWithoutPath(filename);
        String[] parts = name.split("\\.");
        // æ£€æŸ¥é™¤æœ€åŽä¸€ä¸ªæ‰©å±•名之外的所有部分
        for (int i = 0; i < parts.length - 1; i++) {
            String part = parts[i].toLowerCase();
            if (dangerousPrimaryExtensions.contains(part)) {
                return true;
            }
        }
        return false;
    }
    private boolean validateContentType(MultipartFile file) {
        try {
            String declaredType = file.getContentType();
            if (declaredType == null) {
                return true; // æ²¡æœ‰å£°æ˜Žç±»åž‹ï¼Œæ”¾è¿‡
            }
            // ç®€å•的类型匹配检查
            String finalExtension = getFinalExtension(file.getOriginalFilename()).toLowerCase();
            return isContentTypeConsistent(declaredType, finalExtension);
        } catch (Exception e) {
            log.error("内容类型验证失败", e);
            return false;
        }
    }
    /**
     * éªŒè¯æ–‡ä»¶çš„内容类型(Content-Type)是否与文件扩展名一致
     * @param contentType
     * @param extension
     * @return
     */
    private boolean isContentTypeConsistent(String contentType, String extension) {
        // æ‰©å±•更全面的类型映射
        Map<String, String> expectedTypes = new HashMap<>();
        // å›¾ç‰‡ç±»åž‹
        expectedTypes.put("jpg", "image/jpeg");
        expectedTypes.put("jpeg", "image/jpeg");
        expectedTypes.put("png", "image/png");
        expectedTypes.put("gif", "image/gif");
        expectedTypes.put("bmp", "image/bmp");
        expectedTypes.put("webp", "image/webp");
        expectedTypes.put("svg", "image/svg+xml");
        // æ–‡æ¡£ç±»åž‹
        expectedTypes.put("pdf", "application/pdf");
        expectedTypes.put("doc", "application/msword");
        expectedTypes.put("docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document");
        expectedTypes.put("xls", "application/vnd.ms-excel");
        expectedTypes.put("xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        expectedTypes.put("ppt", "application/vnd.ms-powerpoint");
        expectedTypes.put("pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation");
        expectedTypes.put("txt", "text/plain");
        // åŽ‹ç¼©æ–‡ä»¶
        expectedTypes.put("zip", "application/zip");
        expectedTypes.put("rar", "application/x-rar-compressed");
        expectedTypes.put("7z", "application/x-7z-compressed");
        String expectedType = expectedTypes.get(extension);
        return expectedType == null || expectedType.equalsIgnoreCase(contentType);
    }
    private boolean validateFileHeader(MultipartFile file) {
        try {
            byte[] header = new byte[8];
            int bytesRead = file.getInputStream().read(header);
            if (bytesRead < 4) {
                return false;
            }
            String finalExtension = getFinalExtension(file.getOriginalFilename()).toLowerCase();
            // åŸºç¡€çš„æ–‡ä»¶å¤´éªŒè¯
            switch (finalExtension) {
                case "jpg":
                case "jpeg":
                    return isJpeg(header);
                case "png":
                    return isPng(header);
                case "pdf":
                    return isPdf(header);
                case "gif":
                    return isGif(header);
                default:
                    return true; // å…¶ä»–类型不验证文件头
            }
        } catch (IOException e) {
            log.error("文件头验证失败", e);
            return false;
        }
    }
    /**
     * æ–‡ä»¶å¤´éªŒè¯æ–¹æ³•
     * @param header
     * @return
     */
    private boolean isJpeg(byte[] header) {
        return (header[0] & 0xFF) == 0xFF && (header[1] & 0xFF) == 0xD8;
    }
    private boolean isPng(byte[] header) {
        return header[0] == (byte) 0x89 && header[1] == 0x50 &&
            header[2] == 0x4E && header[3] == 0x47;
    }
    private boolean isPdf(byte[] header) {
        return header[0] == 0x25 && header[1] == 0x50 &&
            header[2] == 0x44 && header[3] == 0x46;
    }
    private boolean isGif(byte[] header) {
        return header[0] == 'G' && header[1] == 'I' &&
            header[2] == 'F' && header[3] == '8';
    }
    // è¾…助方法
    private String getFinalExtension(String filename) {
        if (filename == null || !filename.contains(".")) return "";
        String[] parts = filename.split("\\.");
        return parts[parts.length - 1];
    }
    private String getFileNameWithoutPath(String filename) {
        if (filename == null) return "";
        filename = filename.replace('\\', '/');
        int lastSlash = filename.lastIndexOf('/');
        return lastSlash >= 0 ? filename.substring(lastSlash + 1) : filename;
    }
    @Data
    public static class UploadValidationResult {
        private boolean valid;
        private String message;
        private String detectedType;
        public UploadValidationResult() {
            this.valid = false;
            this.message = "";
        }
    }
    /**
     * å¤šæ–‡ä»¶éªŒè¯ç»“æžœ
     */
    @Data
    public static class MultiUploadValidationResult {
        private boolean valid;
        private String message;
        private int totalFiles;
        private int validFiles;
        private int invalidFiles;
        private int failedIndex = -1; // ç¬¬ä¸€ä¸ªå¤±è´¥çš„æ–‡ä»¶ç´¢å¼•
        private List<FileValidationDetail> details;
        public MultiUploadValidationResult() {
            this.valid = false;
            this.message = "";
            this.details = new ArrayList<>();
        }
    }
    /**
     * å•个文件验证详情
     */
    @Data
    public static class FileValidationDetail {
        private String fileName;
        private int fileIndex;
        private long fileSize;
        private boolean valid;
        private String message;
        private String detectedType;
    }
}
Source/UBCS/ubcs-gateway/src/main/java/com/vci/ubcs/gateway/filter/EssentialSecurityFilter.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,42 @@
package com.vci.ubcs.gateway.filter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
 * ç½‘关过滤器,拦截明确的攻击特征
 */
public class EssentialSecurityFilter implements GlobalFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        String path = request.getPath().value();
        // åªæ‹¦æˆªæœ€å±é™©çš„请求
        if (isDefinitelyDangerous(path)) {
            return blockRequest(exchange, "危险请求被拦截!");
        }
        return chain.filter(exchange);
    }
    private boolean isDefinitelyDangerous(String path) {
        // åªæ‹¦æˆªæ˜Žç¡®çš„æ”»å‡»ç‰¹å¾
        return path.contains("../") ||
            path.contains("/WEB-INF/") ||
            path.matches(".*\\.(jsp|war|sh|bat|exe)$") ||
            path.contains("cmd.exe") ||
            path.contains("/bin/");
    }
    private Mono<Void> blockRequest(ServerWebExchange exchange, String message) {
        exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
        return exchange.getResponse().setComplete();
    }
}
Source/UBCS/ubcs-ops/ubcs-flow/src/main/java/com/vci/ubcs/flow/engine/controller/FlowManagerController.java
@@ -18,6 +18,7 @@
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.vci.ubcs.common.validator.ComprehensiveFileValidator;
import com.vci.ubcs.flow.engine.entity.FlowProcess;
import com.vci.ubcs.flow.engine.service.FlowEngineService;
import io.swagger.annotations.Api;
@@ -31,6 +32,7 @@
import org.springblade.core.tool.support.Kv;
import org.springblade.core.tool.utils.Func;
import com.vci.ubcs.flow.engine.constant.FlowEngineConstant;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
@@ -47,10 +49,15 @@
@RequestMapping("manager")
@AllArgsConstructor
@Api(value = "流程管理接口", tags = "流程管理接口")
//@PreAuth(RoleConstant.HAS_ROLE_ADMINISTRATOR)
public class FlowManagerController {
    private final FlowEngineService flowEngineService;
    /**
     * æ–‡ä»¶å®‰å…¨æ£€æŸ¥
     */
    @Autowired
    private ComprehensiveFileValidator fileValidator;
    /**
     * åˆ†é¡µ
@@ -98,6 +105,11 @@
    @ApiOperationSupport(order = 4)
    @ApiOperation(value = "上传部署流程文件", notes = "传入文件")
    public R checkUpload(@RequestParam MultipartFile file) {
        // ä½¿ç”¨æ–‡ä»¶å®‰å…¨éªŒè¯å™¨
        ComprehensiveFileValidator.UploadValidationResult result = fileValidator.validateFile(file);
        if (!result.isValid()) {
            return R.fail(result.getMessage());
        }
        boolean temp = Objects.requireNonNull(file.getOriginalFilename()).endsWith(FlowEngineConstant.SUFFIX);
        return R.data(Kv.create().set("name", file.getOriginalFilename()).set("success", temp));
    }
@@ -114,6 +126,11 @@
    public R deployUpload(@RequestParam List<MultipartFile> files,
                          @RequestParam String category,
                          @RequestParam(required = false, defaultValue = "") String tenantIds) {
        // ä½¿ç”¨æ–‡ä»¶å®‰å…¨éªŒè¯å™¨
        ComprehensiveFileValidator.MultiUploadValidationResult result = fileValidator.validateFiles(files,true);
        if (!result.isValid()) {
            return R.fail(result.getMessage());
        }
        return R.status(flowEngineService.deployUpload(files, category, Func.toStrList(tenantIds)));
    }
Source/UBCS/ubcs-ops/ubcs-resource/src/main/java/com/vci/ubcs/resource/controller/FileController.java
@@ -1,8 +1,8 @@
package com.vci.ubcs.resource.controller;
import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.common.utils.StringUtils;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.vci.ubcs.common.validator.ComprehensiveFileValidator;
import com.vci.ubcs.resource.dto.FileObjectDTO;
import com.vci.ubcs.resource.dto.FileReleaseDTO;
import com.vci.ubcs.resource.dto.FileShareDTO;
@@ -10,14 +10,11 @@
import com.vci.ubcs.resource.service.IFileService;
import com.vci.ubcs.resource.utils.FileDownloadUtil;
import com.vci.ubcs.resource.vo.FileObjectVO;
import com.vci.ubcs.starter.exception.VciBaseException;
import com.vci.ubcs.starter.web.util.ControllerUtil;
import com.vci.ubcs.starter.web.util.LangBaseUtil;
import com.vci.ubcs.starter.web.util.VciBaseUtil;
import lombok.extern.java.Log;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.mp.support.Query;
import org.springblade.core.oss.MinioTemplate;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.utils.StringUtil;
import org.springframework.beans.factory.annotation.Autowired;
@@ -45,6 +42,12 @@
     */
    @Autowired
    private IFileService fileService;
    /**
     * æ–‡ä»¶å®‰å…¨æ£€æŸ¥
     */
    @Autowired
    private ComprehensiveFileValidator fileValidator;
    /**
     * æ ¹æ®æ–‡ä»¶ä¸»é”®ä¸‹è½½æ–‡ä»¶
@@ -82,7 +85,7 @@
            if(StringUtil.isBlank(msg)){
                msg = "未知错误";
            }
            log.debug(msg);
            log.error(msg);
            return R.fail(msg);
        }
        return R.success("删除成功");
@@ -97,6 +100,12 @@
    @PostMapping("/uploadFile")
    public R<FileObjectVO> uploadFile(MultipartFile file, FileObjectDTO fileObjectDTO){
        if (file != null ) {
            // ä½¿ç”¨æ–‡ä»¶å®‰å…¨éªŒè¯å™¨
            ComprehensiveFileValidator.UploadValidationResult result = fileValidator.validateFile(file);
            if (!result.isValid()) {
                return R.fail(result.getMessage());
            }
            //上传文件
            return fileService.uploadFile(file, fileObjectDTO);
        } else {
            return R.fail("无上传的文件");
Source/UBCS/ubcs-ops/ubcs-resource/src/main/java/com/vci/ubcs/resource/endpoint/OssEndpoint.java
@@ -17,6 +17,7 @@
package com.vci.ubcs.resource.endpoint;
import com.vci.ubcs.resource.entity.Attach;
import com.vci.ubcs.common.validator.ComprehensiveFileValidator;
import io.swagger.annotations.Api;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
@@ -31,8 +32,6 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
/**
 * å¯¹è±¡å­˜å‚¨ç«¯ç‚¹
@@ -59,6 +58,11 @@
     */
    private final IAttachService attachService;
    /**
     * æ–‡ä»¶å®‰å…¨æ£€æŸ¥
     */
    @Autowired
    private ComprehensiveFileValidator fileValidator;
    /**
     * åˆ›å»ºå­˜å‚¨æ¡¶
@@ -150,6 +154,11 @@
    @SneakyThrows
    @PostMapping("/put-file")
    public R<BladeFile> putFile(@RequestParam MultipartFile file) {
        // ä½¿ç”¨æ–‡ä»¶å®‰å…¨éªŒè¯å™¨
        ComprehensiveFileValidator.UploadValidationResult result = fileValidator.validateFile(file);
        if (!result.isValid()) {
            return R.fail(result.getMessage());
        }
        BladeFile bladeFile = ossBuilder.template().putFile(file.getOriginalFilename(), file.getInputStream());
        return R.data(bladeFile);
    }
@@ -164,41 +173,14 @@
    @SneakyThrows
    @PostMapping("/put-file-by-name")
    public R<BladeFile> putFile(@RequestParam String fileName, @RequestParam MultipartFile file) {
        // ä½¿ç”¨æ–‡ä»¶å®‰å…¨éªŒè¯å™¨
        ComprehensiveFileValidator.UploadValidationResult result = fileValidator.validateFile(file);
        if (!result.isValid()) {
            return R.fail(result.getMessage());
        }
        BladeFile bladeFile = ossBuilder.template().putFile(fileName, file.getInputStream());
        return R.data(bladeFile);
    }
//    /**
//     * ä¸Šä¼ æ–‡ä»¶å¹¶ä¿å­˜è‡³é™„件表
//     *
//     * @param file æ–‡ä»¶
//     * @return ObjectStat
//     */
//    @SneakyThrows
//    @PostMapping("/put-file-attach")
//    public R<BladeFile> putFileAttach(@RequestParam MultipartFile file) {
//        String fileName = file.getOriginalFilename();
//        BladeFile bladeFile = ossBuilder.template().putFile(fileName, file.getInputStream());
//        Long attachId = buildAttach(fileName, file.getSize(), bladeFile);
//        bladeFile.setAttachId(attachId);
//        return R.data(bladeFile);
//    }
//    /**
//     * ä¸Šä¼ æ–‡ä»¶å¹¶ä¿å­˜è‡³é™„件表
//     *
//     * @param fileName å­˜å‚¨æ¡¶å¯¹è±¡åç§°
//     * @param file     æ–‡ä»¶
//     * @return ObjectStat
//     */
//    @SneakyThrows
//    @PostMapping("/put-file-attach-by-name")
//    public R<BladeFile> putFileAttach(@RequestParam String fileName, @RequestParam MultipartFile file) {
//        BladeFile bladeFile = ossBuilder.template().putFile(fileName, file.getInputStream());
//        Long attachId = buildAttach(fileName, file.getSize(), bladeFile);
//        bladeFile.setAttachId(attachId);
//        return R.data(bladeFile);
//    }
    /**
     * æž„建附件表
Source/UBCS/ubcs-service/ubcs-code/src/main/java/com/vci/ubcs/code/controller/CodeClassifyController.java
@@ -25,6 +25,7 @@
import com.vci.ubcs.code.vo.pagemodel.CodeClassifyVO;
import com.vci.ubcs.code.vo.pagemodel.CodeImProtRusultVO;
import com.vci.ubcs.code.wrapper.CodeClassifyWrapper;
import com.vci.ubcs.common.validator.ComprehensiveFileValidator;
import com.vci.ubcs.omd.vo.BtmTypeAttributeVO;
import com.vci.ubcs.starter.revision.model.TreeQueryObject;
import com.vci.ubcs.starter.util.LocalFileUtil;
@@ -47,6 +48,7 @@
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.StringUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
@@ -77,6 +79,12 @@
    private final ICodeClassifyService codeClassifyService;
    CodeClassifyMapper codeClassifyMapper;
    /**
     * æ–‡ä»¶å®‰å…¨æ£€æŸ¥
     */
    @Autowired
    private ComprehensiveFileValidator fileValidator;
    /**
     * ä¸»é¢˜åº“定义表 è¯¦æƒ…
@@ -277,6 +285,12 @@
     */
    @PostMapping("/importClassify")
    public R importClassify(MultipartFile file) {
        // ä½¿ç”¨æ–‡ä»¶å®‰å…¨éªŒè¯å™¨
        ComprehensiveFileValidator.UploadValidationResult result = fileValidator.validateFile(file);
        if (!result.isValid()) {
            return R.fail(result.getMessage());
        }
        String excelFileName = LocalFileUtil.getDefaultTempFolder() + File.separator + LocalFileUtil.getFileNameForIE(file.getOriginalFilename());
        File file1 = new File(excelFileName);
        try {
Source/UBCS/ubcs-service/ubcs-code/src/main/java/com/vci/ubcs/code/controller/MdmEngineController.java
@@ -10,6 +10,7 @@
import com.vci.ubcs.code.service.MdmEngineService;
import com.vci.ubcs.code.service.MdmIOService;
import com.vci.ubcs.code.vo.pagemodel.*;
import com.vci.ubcs.common.validator.ComprehensiveFileValidator;
import com.vci.ubcs.flow.core.dto.FlowStatusDTO;
import com.vci.ubcs.starter.annotation.VciBusinessLog;
import com.vci.ubcs.starter.revision.model.BaseModel;
@@ -43,21 +44,31 @@
     * æ—¥å¿—
     */
    private Logger logger = LoggerFactory.getLogger(getClass());
    /**
     * ä¸»æ•°æ®å¼•擎服务
     */
    @Autowired
    private MdmEngineService engineService;
    /**
     * ä¸»æ•°æ®å¯¼å…¥å¯¼å‡ºæœåŠ¡
     */
    @Autowired
    private MdmIOService mdmIOService;
    /**
     * æ—¥å¿—保存工具类
     */
    @Autowired
    private SaveLogUtil saveLogUtil;
    /**
     * æ–‡ä»¶å®‰å…¨æ£€æŸ¥
     */
    @Autowired
    private ComprehensiveFileValidator fileValidator;
    /**
     * ä¸‹è½½æ‰¹é‡ç”³è¯·çš„导入模板
@@ -112,6 +123,12 @@
    @VciBusinessLog(operateName = "导入批量编辑数据")
    @PostMapping("/batchImportEdit")
    public R batchImportEdit(String codeClassifyOid, String classifyAttr,MultipartFile file,HttpServletResponse response) throws Throwable {
        // ä½¿ç”¨æ–‡ä»¶å®‰å…¨éªŒè¯å™¨
        ComprehensiveFileValidator.UploadValidationResult validationResult = fileValidator.validateFile(file);
        if (!validationResult.isValid()) {
            return R.fail(validationResult.getMessage());
        }
        String excelFileName = LocalFileUtil.getDefaultTempFolder() + File.separator + file.getOriginalFilename();
        File file1 = new File(excelFileName);
        try {
@@ -218,6 +235,12 @@
    @VciBusinessLog(operateName = "批量申请编码的信息")
    @PostMapping("/batchImportCode")
    public R batchImportCode(String secDTOList, String codeClassifyOid, MultipartFile file, HttpServletResponse response) throws Throwable {
        // ä½¿ç”¨æ–‡ä»¶å®‰å…¨éªŒè¯å™¨
        ComprehensiveFileValidator.UploadValidationResult validationResult = fileValidator.validateFile(file);
        if (!validationResult.isValid()) {
            return R.fail(validationResult.getMessage());
        }
        CodeOrderDTO orderDTO = new CodeOrderDTO();
        orderDTO.setCodeClassifyOid(codeClassifyOid);
        if(StringUtils.isNotBlank(secDTOList)){
@@ -269,6 +292,12 @@
    @VciBusinessLog(operateName = "导入编码的历史数据")
    @PostMapping("/batchImportHistoryData")
    public R batchImportHistoryData(String codeClassifyOid, String classifyAttr,MultipartFile file,HttpServletResponse response) throws Throwable {
        // ä½¿ç”¨æ–‡ä»¶å®‰å…¨éªŒè¯å™¨
        ComprehensiveFileValidator.UploadValidationResult validationResult = fileValidator.validateFile(file);
        if (!validationResult.isValid()) {
            return R.fail(validationResult.getMessage());
        }
        String excelFileName = LocalFileUtil.getDefaultTempFolder() + File.separator + file.getOriginalFilename();
        File file1 = new File(excelFileName);
        try {
@@ -312,6 +341,12 @@
    @VciBusinessLog(operateName = "批量申请编码的信息")
    @PostMapping("/batchTopImportCode")
    public R batchTopImportCode(String codeClassifyOid, String classifyAttr,MultipartFile file,HttpServletResponse response) throws Throwable {
        // ä½¿ç”¨æ–‡ä»¶å®‰å…¨éªŒè¯å™¨
        ComprehensiveFileValidator.UploadValidationResult result = fileValidator.validateFile(file);
        if (!result.isValid()) {
            return R.fail(result.getMessage());
        }
        String excelFileName = LocalFileUtil.getDefaultTempFolder() + File.separator + file.getOriginalFilename();
        File file1 = new File(excelFileName);
        try {
@@ -867,6 +902,11 @@
     */
    @PostMapping("/importGroupCode")
    public R  importGroupCode(String codeClassifyOid,MultipartFile file,HttpServletResponse response){
        // ä½¿ç”¨æ–‡ä»¶å®‰å…¨éªŒè¯å™¨
        ComprehensiveFileValidator.UploadValidationResult result = fileValidator.validateFile(file);
        if (!result.isValid()) {
            return R.fail(result.getMessage());
        }
        String excelFileName = LocalFileUtil.getDefaultTempFolder() + File.separator + file.getOriginalFilename();
        File file1 = new File(excelFileName);
Source/UBCS/ubcs-service/ubcs-deploy/src/main/java/com/vci/ubcs/deploy/controller/DeployAppsController.java
@@ -1,6 +1,7 @@
package com.vci.ubcs.deploy.controller;
import com.alibaba.nacos.shaded.com.google.protobuf.ServiceException;
import com.vci.ubcs.common.validator.ComprehensiveFileValidator;
import com.vci.ubcs.deploy.entity.DeployApps;
import com.vci.ubcs.deploy.service.IDeployAppsService;
import com.vci.ubcs.deploy.vo.DeployAppsVO;
@@ -10,6 +11,7 @@
import org.springblade.core.tenant.annotation.NonDS;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.utils.Func;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import springfox.documentation.annotations.ApiIgnore;
@@ -32,6 +34,12 @@
public class DeployAppsController {
    private final IDeployAppsService deployAppsService;
    /**
     * æ–‡ä»¶å®‰å…¨æ£€æŸ¥
     */
    @Autowired
    private ComprehensiveFileValidator fileValidator;
    /**
     * èŽ·å–æœåŠ¡è¿è¡Œåˆ—è¡¨
@@ -86,6 +94,11 @@
     */
    @PostMapping("/importUpdateServiceJar")
    public R importClassify(@RequestParam("files") MultipartFile[] files,@RequestParam String serverName) throws ServiceException {
        // ä½¿ç”¨æ–‡ä»¶å®‰å…¨éªŒè¯å™¨
        ComprehensiveFileValidator.MultiUploadValidationResult quickResult = fileValidator.validateFiles(files, true);
        if (!quickResult.isValid()) {
            return R.fail(quickResult.getMessage());
        }
        if(Func.isBlank(serverName)){
            return R.fail("Mandatory parameter service name not found!");
        }
Source/UBCS/ubcs-service/ubcs-system/src/main/java/com/vci/ubcs/system/controller/RegionController.java
@@ -20,6 +20,7 @@
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.vci.ubcs.common.validator.ComprehensiveFileValidator;
import com.vci.ubcs.system.entity.Region;
import com.vci.ubcs.system.excel.RegionExcel;
import com.vci.ubcs.system.excel.RegionImporter;
@@ -35,6 +36,7 @@
import org.springblade.core.tenant.annotation.NonDS;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.utils.DateUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import springfox.documentation.annotations.ApiIgnore;
@@ -58,6 +60,12 @@
public class RegionController extends BladeController {
    private final IRegionService regionService;
    /**
     * æ–‡ä»¶å®‰å…¨æ£€æŸ¥
     */
    @Autowired
    private ComprehensiveFileValidator fileValidator;
    /**
     * è¯¦æƒ…
@@ -170,6 +178,11 @@
    @ApiOperationSupport(order = 10)
    @ApiOperation(value = "导入行政区划", notes = "传入excel")
    public R importRegion(MultipartFile file, Integer isCovered) {
        // ä½¿ç”¨æ–‡ä»¶å®‰å…¨éªŒè¯å™¨
        ComprehensiveFileValidator.UploadValidationResult result = fileValidator.validateFile(file);
        if (!result.isValid()) {
            return R.fail(result.getMessage());
        }
        RegionImporter regionImporter = new RegionImporter(regionService, isCovered == 1);
        ExcelUtil.save(file, regionImporter, RegionExcel.class);
        return R.success("操作成功");
Source/UBCS/ubcs-service/ubcs-user/src/main/java/com/vci/ubcs/system/user/controller/UserController.java
@@ -21,6 +21,7 @@
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.vci.ubcs.common.validator.ComprehensiveFileValidator;
import com.vci.ubcs.system.cache.NacosConfigCache;
import com.vci.ubcs.system.user.entity.User;
import com.vci.ubcs.system.user.excel.UserExcel;
@@ -32,8 +33,7 @@
import io.swagger.annotations.ApiParam;
import lombok.AllArgsConstructor;
import com.vci.ubcs.common.cache.CacheNames;
import org.apache.ibatis.annotations.Param;
import org.hibernate.validator.internal.util.logging.Log;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.cache.utils.CacheUtil;
import org.springblade.core.excel.util.ExcelUtil;
import org.springblade.core.mp.support.Condition;
@@ -50,6 +50,7 @@
import org.springblade.core.tool.utils.StringUtil;
import com.vci.ubcs.system.user.service.IUserService;
import com.vci.ubcs.system.user.vo.UserVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import springfox.documentation.annotations.ApiIgnore;
@@ -71,12 +72,19 @@
@RestController
@RequestMapping
@AllArgsConstructor
@lombok.extern.java.Log
@Slf4j
public class UserController {
    private final IUserService userService;
    private final BladeRedis bladeRedis;
    private final NacosConfigCache nacosConfigCache;
    /**
     * æ–‡ä»¶å®‰å…¨æ£€æŸ¥
     */
    private ComprehensiveFileValidator fileValidator;
    /**
     * æŸ¥è¯¢å•条
@@ -263,6 +271,11 @@
    @ApiOperationSupport(order = 12)
    @ApiOperation(value = "导入用户", notes = "传入excel")
    public R importUser(MultipartFile file, Integer isCovered) {
        // ä½¿ç”¨æ–‡ä»¶å®‰å…¨éªŒè¯å™¨
        ComprehensiveFileValidator.UploadValidationResult result = fileValidator.validateFile(file);
        if (!result.isValid()) {
            return R.fail(result.getMessage());
        }
        UserImporter userImporter = new UserImporter(userService, isCovered == 1);
        ExcelUtil.save(file, userImporter, UserExcel.class);
        return R.success("操作成功");