From b1e6eabc2640c76d5d84b8cffc179ac412596e38 Mon Sep 17 00:00:00 2001 From: "liang.chao" <1360241448@qq.com> Date: Thu, 23 Oct 2025 19:17:56 +0800 Subject: [PATCH] =?UTF-8?q?bug=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/archive/ArchiveController.java | 4 +- .../archive/FileManagementController.java | 4 +- .../controller/tool/FileShareController.java | 68 +++++++ .../com/bonus/web/mapper/FileShareMapper.java | 24 +++ .../bonus/web/service/FileShareService.java | 23 +++ .../service/impl/FileShareServiceImpl.java | 190 ++++++++++++++++++ .../main/resources/mapper/FileShareMapper.xml | 31 +++ .../framework/config/ResourcesConfig.java | 1 + .../framework/config/SecurityConfig.java | 2 +- .../interceptor/ReplayAttackInterceptor.java | 2 +- 10 files changed, 343 insertions(+), 6 deletions(-) create mode 100644 bonus-admin/src/main/java/com/bonus/web/controller/tool/FileShareController.java create mode 100644 bonus-admin/src/main/java/com/bonus/web/mapper/FileShareMapper.java create mode 100644 bonus-admin/src/main/java/com/bonus/web/service/FileShareService.java create mode 100644 bonus-admin/src/main/java/com/bonus/web/service/impl/FileShareServiceImpl.java create mode 100644 bonus-admin/src/main/resources/mapper/FileShareMapper.xml diff --git a/bonus-admin/src/main/java/com/bonus/web/controller/archive/ArchiveController.java b/bonus-admin/src/main/java/com/bonus/web/controller/archive/ArchiveController.java index 0d512a3..c9de4e0 100644 --- a/bonus-admin/src/main/java/com/bonus/web/controller/archive/ArchiveController.java +++ b/bonus-admin/src/main/java/com/bonus/web/controller/archive/ArchiveController.java @@ -149,7 +149,7 @@ public class ArchiveController extends BaseController { // 如果子集有数据 无法修改 Integer child = archiveMapper.getchild(dto); if (child > 0){ - return R.fail("该节点下有子集,无法修改"); + return R.fail("该节点下存在子节点或该节点下存在档案文件,无法修改"); } Integer num = service.geMaxSort(vo.getParentId().toString()); if (num == null) { @@ -176,7 +176,7 @@ public class ArchiveController extends BaseController { if (i > 0) { return R.ok(); } else { - return R.fail("该节点下有子集,无法删除"); + return R.fail("该节点下存在子节点或该节点下存在档案文件,无法删除"); } } catch (Exception e) { log.error(e.toString(), e); diff --git a/bonus-admin/src/main/java/com/bonus/web/controller/archive/FileManagementController.java b/bonus-admin/src/main/java/com/bonus/web/controller/archive/FileManagementController.java index 95f43e6..4777f7c 100644 --- a/bonus-admin/src/main/java/com/bonus/web/controller/archive/FileManagementController.java +++ b/bonus-admin/src/main/java/com/bonus/web/controller/archive/FileManagementController.java @@ -289,7 +289,7 @@ public class FileManagementController extends BaseController { try { Integer child = fileManageMapper.getchild(dto); if (child > 0){ - return R.fail("该节点下有子集,无法修改"); + return R.fail("该节点下存在子节点或该节点下存在档案文件,无法修改"); } Integer num = fileManageService.getMaxSort(dto); if (num == null) { @@ -314,7 +314,7 @@ public class FileManagementController extends BaseController { try { Integer child = fileManageMapper.getchild(dto); if (child > 0){ - return R.fail("该节点下有子集,无法删除"); + return R.fail("该节点下存在子节点或该节点下存在档案文件,无法删除"); } Integer i = fileManageService.delFileManage(dto); if (i > 0) { diff --git a/bonus-admin/src/main/java/com/bonus/web/controller/tool/FileShareController.java b/bonus-admin/src/main/java/com/bonus/web/controller/tool/FileShareController.java new file mode 100644 index 0000000..41f68b1 --- /dev/null +++ b/bonus-admin/src/main/java/com/bonus/web/controller/tool/FileShareController.java @@ -0,0 +1,68 @@ +package com.bonus.web.controller.tool; + +import com.bonus.common.annotation.RequiresPermissions; +import com.bonus.common.annotation.SysLog; +import com.bonus.common.core.controller.BaseController; +import com.bonus.common.core.domain.AjaxResult; +import com.bonus.common.core.domain.R; +import com.bonus.common.core.page.TableDataInfo; +import com.bonus.common.enums.OperaType; +import com.bonus.system.domain.KyDataClassify; +import com.bonus.system.domain.KyDataCollectData; +import com.bonus.system.domain.vo.KyDataClassifyVo; +import com.bonus.web.service.FileShareService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletResponse; +import java.util.List; +import java.util.Map; + +/** + * @Author:liang.chao + * @Date:2025/10/23 - 14:07 + */ +@RestController +@RequestMapping("/file/share") +@Slf4j +public class FileShareController extends BaseController { + + @Resource + private FileShareService fileShareService; + + /** + * 获取数据分类管理下拉框 + * @param kyDataClassify + * @return + */ + @RequiresPermissions("file:share:listAll") + @GetMapping("/listAll") + public TableDataInfo listAll(KyDataClassify kyDataClassify) + { + List list = fileShareService.listAll(kyDataClassify); + return getDataTable(list); + } + /** + * 根据数据类型数据汇集管理列表 + * + * @param kyDataCollectData + * @return + */ + @SysLog(title = "档案分享管理", businessType = OperaType.QUERY, module = "档案分享管理->档案分享管理", details = "根据档案文件数据汇集管理列表") + @RequiresPermissions("file:share:list") + @GetMapping("/list") + public TableDataInfo list(KyDataCollectData kyDataCollectData) { + List list = fileShareService.selectKyDataCollectDataList(kyDataCollectData); + return getDataTable(list); + } + + @SysLog(title = "档案分享文件详情", businessType = OperaType.QUERY, module = "档案分享管理->档案分享管理", details = "档案分享文件详情") + @GetMapping("/queryById") + public void queryById(KyDataCollectData kyDataCollectData, HttpServletResponse response) { + fileShareService.queryDetailById(kyDataCollectData,response); + } + +} diff --git a/bonus-admin/src/main/java/com/bonus/web/mapper/FileShareMapper.java b/bonus-admin/src/main/java/com/bonus/web/mapper/FileShareMapper.java new file mode 100644 index 0000000..8a6c813 --- /dev/null +++ b/bonus-admin/src/main/java/com/bonus/web/mapper/FileShareMapper.java @@ -0,0 +1,24 @@ +package com.bonus.web.mapper; + +import com.bonus.system.domain.KyDataClassify; +import com.bonus.system.domain.KyDataCollectData; +import com.bonus.system.domain.vo.KyDataClassifyVo; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * @Author:liang.chao + * @Date:2025/10/23 - 14:45 + */ +@Mapper +public interface FileShareMapper { + + List getParenList(); + + List listAll(KyDataClassify kyDataClassify); + + List selectKyDataCollectDataList(KyDataCollectData kyDataCollectData); + + String queryDetailById(KyDataCollectData kyDataCollectData); +} diff --git a/bonus-admin/src/main/java/com/bonus/web/service/FileShareService.java b/bonus-admin/src/main/java/com/bonus/web/service/FileShareService.java new file mode 100644 index 0000000..8edd53e --- /dev/null +++ b/bonus-admin/src/main/java/com/bonus/web/service/FileShareService.java @@ -0,0 +1,23 @@ +package com.bonus.web.service; + +import com.bonus.common.core.domain.R; +import com.bonus.system.domain.KyDataClassify; +import com.bonus.system.domain.KyDataCollectData; +import com.bonus.system.domain.vo.KyDataClassifyVo; + +import javax.servlet.http.HttpServletResponse; +import java.util.List; +import java.util.Map; + +/** + * @Author:liang.chao + * @Date:2025/10/23 - 14:43 + */ +public interface FileShareService { + + List listAll(KyDataClassify kyDataClassify); + + List selectKyDataCollectDataList(KyDataCollectData kyDataCollectData); + + void queryDetailById(KyDataCollectData kyDataCollectData, HttpServletResponse response); +} diff --git a/bonus-admin/src/main/java/com/bonus/web/service/impl/FileShareServiceImpl.java b/bonus-admin/src/main/java/com/bonus/web/service/impl/FileShareServiceImpl.java new file mode 100644 index 0000000..1898ca3 --- /dev/null +++ b/bonus-admin/src/main/java/com/bonus/web/service/impl/FileShareServiceImpl.java @@ -0,0 +1,190 @@ +package com.bonus.web.service.impl; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; +import com.bonus.common.core.domain.R; +import com.bonus.system.domain.KyDataClassify; +import com.bonus.system.domain.KyDataCollectData; +import com.bonus.system.domain.vo.KyDataClassifyVo; +import com.bonus.web.domain.vo.DaKyProFilesContentsVo; +import com.bonus.web.mapper.FileShareMapper; +import com.bonus.web.service.FileManageService; +import com.bonus.web.service.FileShareService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Service; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletResponse; +import java.io.File; +import java.io.IOException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.Base64; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @Author:liang.chao + * @Date:2025/10/23 - 14:43 + */ +@Service +public class FileShareServiceImpl implements FileShareService { + + @Resource + private FileShareMapper fileShareMapper; + @Resource + private FileManageService fileManageService; + + @Value("${bonus.profile}") + private String uploadDir; + + + @Override + public List listAll(KyDataClassify kyDataClassify) { + //获取父级 + List KyDataClassifyVoList = fileShareMapper.getParenList(); + if (KyDataClassifyVoList.size() > 0) { + //获取子级 + for (KyDataClassifyVo kyDataClassifyVo : KyDataClassifyVoList) { + kyDataClassify.setPid(kyDataClassifyVo.getCode()); + List kyDataClassifyList = fileShareMapper.listAll(kyDataClassify); + kyDataClassifyVo.setKyDataClassifyList(kyDataClassifyList); + } + } + return KyDataClassifyVoList; + } + + @Override + public List selectKyDataCollectDataList(KyDataCollectData kyDataCollectData) { + return fileShareMapper.selectKyDataCollectDataList(kyDataCollectData); + } + + + @Override + public void queryDetailById(KyDataCollectData kyDataCollectData, HttpServletResponse response) { + try { + String jsonData = fileShareMapper.queryDetailById(kyDataCollectData); + if (jsonData != null) { + JSONArray objects = JSON.parseArray(jsonData); + for (Object obj : objects) { + JSONObject object = (JSONObject) obj; + if (object != null) { + String id = object.getString("id"); + if (id.equals(kyDataCollectData.getJsonId().toString())) { + DaKyProFilesContentsVo record = fileManageService.getFileById(Long.parseLong(id)); + + if (record == null || StringUtils.isBlank(record.getFilePath())) { + response.setStatus(HttpStatus.NOT_FOUND.value()); + return; + } + + String safeFullPath = buildSecureFullPath(uploadDir, record.getFilePath()); + if (safeFullPath == null) { + response.setStatus(HttpStatus.BAD_REQUEST.value()); + return; + } + + File file = new File(safeFullPath); + if (!file.exists() || !file.isFile()) { + response.setStatus(HttpStatus.NOT_FOUND.value()); + return; + } + + // 设置响应头 + String contentType = getContentType(record.getSuffixName()); + response.setContentType(contentType); + String encodedFileName = URLEncoder.encode(record.getFileName(), String.valueOf(StandardCharsets.UTF_8)) + .replaceAll("\\+", "%20"); + response.setHeader("Content-Disposition", "inline; filename*=UTF-8''" + encodedFileName); + + // 写入文件流到响应 + Files.copy(file.toPath(), response.getOutputStream()); + response.flushBuffer(); + return; + } + } + } + } + response.setStatus(HttpStatus.NOT_FOUND.value()); + } catch (Exception e) { + response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); + } + } + + private String getContentType(String suffix) { + if (StringUtils.isBlank(suffix)) { + return "application/octet-stream"; + } + + switch (suffix.toLowerCase()) { + case "jpg": + case "jpeg": + return "image/jpeg"; + case "png": + return "image/png"; + case "gif": + return "image/gif"; + case "bmp": + return "image/bmp"; + case "webp": + return "image/webp"; + case "svg": + return "image/svg+xml"; + default: + return "application/octet-stream"; + } + } + + private String buildSecureFullPath(String baseDir, String filePath) { + if (StringUtils.isBlank(filePath)) { + return null; + } + + // 统一路径分隔符 + String unifiedPath = filePath.replace('\\', '/'); + + // 移除开头的斜杠 + unifiedPath = unifiedPath.replaceAll("^/+", ""); + + // 检查路径遍历 + if (unifiedPath.contains("../") || unifiedPath.contains("..\\")) { + return null; + } + + // 检查绝对路径 + if (unifiedPath.matches("^[a-zA-Z]:/.*") || unifiedPath.startsWith("/")) { + return null; + } + + // 构建完整路径 + String fullPath; + if (baseDir.endsWith(File.separator)) { + fullPath = baseDir + unifiedPath; + } else { + fullPath = baseDir + File.separator + unifiedPath; + } + + // 使用File的getCanonicalPath进行最终验证 + try { + File canonicalFile = new File(fullPath); + String canonicalPath = canonicalFile.getCanonicalPath(); + + // 验证是否仍在基础目录内 + String canonicalBaseDir = new File(baseDir).getCanonicalPath(); + if (!canonicalPath.startsWith(canonicalBaseDir)) { + return null; + } + + return canonicalPath; + } catch (IOException e) { + return null; + } + } +} diff --git a/bonus-admin/src/main/resources/mapper/FileShareMapper.xml b/bonus-admin/src/main/resources/mapper/FileShareMapper.xml new file mode 100644 index 0000000..c369df2 --- /dev/null +++ b/bonus-admin/src/main/resources/mapper/FileShareMapper.xml @@ -0,0 +1,31 @@ + + + + + + + + + diff --git a/bonus-framework/src/main/java/com/bonus/framework/config/ResourcesConfig.java b/bonus-framework/src/main/java/com/bonus/framework/config/ResourcesConfig.java index a2a803e..c50e1b0 100644 --- a/bonus-framework/src/main/java/com/bonus/framework/config/ResourcesConfig.java +++ b/bonus-framework/src/main/java/com/bonus/framework/config/ResourcesConfig.java @@ -74,6 +74,7 @@ public class ResourcesConfig implements WebMvcConfigurer .excludePathPatterns("/smartArchives/session/check") .excludePathPatterns("/smartArchives/sys/config/getConfig") .excludePathPatterns("/smartArchives/data/Collect/queryById") + .excludePathPatterns("/smartArchives/file/share/queryById") .excludePathPatterns(EXCLUDEURLS) .order(-15); } diff --git a/bonus-framework/src/main/java/com/bonus/framework/config/SecurityConfig.java b/bonus-framework/src/main/java/com/bonus/framework/config/SecurityConfig.java index d11396b..c235231 100644 --- a/bonus-framework/src/main/java/com/bonus/framework/config/SecurityConfig.java +++ b/bonus-framework/src/main/java/com/bonus/framework/config/SecurityConfig.java @@ -112,7 +112,7 @@ public class SecurityConfig .authorizeHttpRequests((requests) -> { permitAllUrl.getUrls().forEach(url -> requests.antMatchers(url).permitAll()); // 对于登录login 注册register 验证码captchaImage 允许匿名访问 - requests.antMatchers("/login", "/register", "/captchaImage","/sys/config/getConfig","/session/check","/data/Collect/queryById").permitAll() + requests.antMatchers("/login", "/register", "/captchaImage","/sys/config/getConfig","/session/check","/data/Collect/queryById","/file/share/queryById").permitAll() // 静态资源,可匿名访问 .antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll() .antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll() diff --git a/bonus-framework/src/main/java/com/bonus/framework/interceptor/ReplayAttackInterceptor.java b/bonus-framework/src/main/java/com/bonus/framework/interceptor/ReplayAttackInterceptor.java index b42f44d..2467f16 100644 --- a/bonus-framework/src/main/java/com/bonus/framework/interceptor/ReplayAttackInterceptor.java +++ b/bonus-framework/src/main/java/com/bonus/framework/interceptor/ReplayAttackInterceptor.java @@ -48,7 +48,7 @@ public class ReplayAttackInterceptor implements HandlerInterceptor { ignoreUrlPatterns.add("/smartArchives/getRouters"); ignoreUrlPatterns.add("/smartArchives/session/check"); ignoreUrlPatterns.add("/smartArchives/sys/config/getConfig"); - ignoreUrlPatterns.add("/smartArchives/data/Collect/queryById"); + ignoreUrlPatterns.add("/smartArchives/file/share/queryById"); } private final RedisCache redisUtil;