diff --git a/rdms-system/rdms-system-api/src/main/java/com/njcn/rdms/module/system/enums/ErrorCodeConstants.java b/rdms-system/rdms-system-api/src/main/java/com/njcn/rdms/module/system/enums/ErrorCodeConstants.java index 34d70bd..fc2dbff 100644 --- a/rdms-system/rdms-system-api/src/main/java/com/njcn/rdms/module/system/enums/ErrorCodeConstants.java +++ b/rdms-system/rdms-system-api/src/main/java/com/njcn/rdms/module/system/enums/ErrorCodeConstants.java @@ -106,6 +106,7 @@ public interface ErrorCodeConstants { ErrorCode FILE_PATH_EXISTS = new ErrorCode(1_001_003_000, "文件路径已存在"); ErrorCode FILE_NOT_EXISTS = new ErrorCode(1_001_003_001, "文件不存在"); ErrorCode FILE_IS_EMPTY = new ErrorCode(1_001_003_002, "文件为空"); + ErrorCode FILE_DELETE_NO_PERMISSION = new ErrorCode(1_001_003_003, "无权删除该文件,仅可删除本人上传的文件"); // ========== OAuth2 客户端 1-002-020-000 ========= ErrorCode OAUTH2_CLIENT_NOT_EXISTS = new ErrorCode(1_002_020_000, "OAuth2 客户端不存在"); diff --git a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/file/FileController.java b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/file/FileController.java index 6176446..494d6a0 100644 --- a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/file/FileController.java +++ b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/controller/admin/file/FileController.java @@ -84,7 +84,8 @@ public class FileController { @DeleteMapping("/delete") @Operation(summary = "删除文件") @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('system:file:delete')") + // 权限下沉到 FileService:上传者本人可删自己上传的文件,其余需 system:file:delete 权限。 + // 故意不挂 @PreAuthorize,否则无该权限码的上传者会在进方法体前被 AOP 挡死(口径同项目域"属主放行+权限码兜底")。 public CommonResult deleteFile(@RequestParam("id") Long id) throws Exception { fileService.deleteFile(id); return success(true); @@ -93,7 +94,7 @@ public class FileController { @DeleteMapping("/delete-list") @Operation(summary = "批量删除文件") @Parameter(name = "ids", description = "编号列表", required = true) - @PreAuthorize("@ss.hasPermission('system:file:delete')") + // 权限下沉到 FileService:逐个按上传者归属校验,其余需 system:file:delete 权限(见 deleteFile 注释)。 public CommonResult deleteFileList(@RequestParam("ids") List ids) throws Exception { fileService.deleteFileList(ids); return success(true); diff --git a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/service/file/FileServiceImpl.java b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/service/file/FileServiceImpl.java index 8432844..38eafcc 100644 --- a/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/service/file/FileServiceImpl.java +++ b/rdms-system/rdms-system-boot/src/main/java/com/njcn/rdms/module/system/service/file/FileServiceImpl.java @@ -10,6 +10,8 @@ import com.google.common.annotations.VisibleForTesting; import com.njcn.rdms.framework.common.pojo.PageResult; import com.njcn.rdms.framework.common.util.http.HttpUtils; import com.njcn.rdms.framework.common.util.object.BeanUtils; +import com.njcn.rdms.framework.security.core.service.SecurityFrameworkService; +import com.njcn.rdms.framework.security.core.util.SecurityFrameworkUtils; import com.njcn.rdms.module.system.controller.admin.file.vo.file.FileCreateReqVO; import com.njcn.rdms.module.system.controller.admin.file.vo.file.FilePageReqVO; import com.njcn.rdms.module.system.controller.admin.file.vo.file.FilePresignedUrlRespVO; @@ -27,6 +29,7 @@ import java.util.List; import static cn.hutool.core.date.DatePattern.PURE_DATE_PATTERN; import static com.njcn.rdms.framework.common.exception.util.ServiceExceptionUtil.exception; +import static com.njcn.rdms.module.system.enums.ErrorCodeConstants.FILE_DELETE_NO_PERMISSION; import static com.njcn.rdms.module.system.enums.ErrorCodeConstants.FILE_NOT_EXISTS; /** @@ -52,6 +55,9 @@ public class FileServiceImpl implements FileService { @Resource private FileContentMapper fileContentMapper; + @Resource + private SecurityFrameworkService securityFrameworkService; + @Override public PageResult getFilePage(FilePageReqVO pageReqVO) { return fileMapper.selectPage(pageReqVO); @@ -172,6 +178,8 @@ public class FileServiceImpl implements FileService { public void deleteFile(Long id) throws Exception { // 校验存在 FileDO file = validateFileExists(id); + // 校验删除权限:上传者本人,或具备文件删除权限者 + validateFileDeletePermission(file); // 从文件存储器中删除 FileClient client = fileConfigService.getFileClient(file.getConfigId()); @@ -187,6 +195,8 @@ public class FileServiceImpl implements FileService { public void deleteFileList(List ids) { // 删除文件 List files = fileMapper.selectByIds(ids); + // 逐个校验删除权限,任一越权即整体拒绝 + files.forEach(this::validateFileDeletePermission); for (FileDO file : files) { // 获取客户端 FileClient client = fileConfigService.getFileClient(file.getConfigId()); @@ -207,6 +217,25 @@ public class FileServiceImpl implements FileService { return fileDO; } + /** + * 校验文件删除权限。 + *

+ * 口径:上传者本人可删除自己上传的文件;其余情况需具备文件管理删除权限 {@code system:file:delete}(运维 / 管理员)。 + * 该接口按 id 删除、不校验业务归属,故不能对所有登录用户放开,只对"本人上传"放行。 + */ + private void validateFileDeletePermission(FileDO file) { + Long loginUserId = SecurityFrameworkUtils.getLoginUserId(); + // 上传者本人放行(creator 存的是上传时的登录用户 id) + if (loginUserId != null && String.valueOf(loginUserId).equals(file.getCreator())) { + return; + } + // 兜底:具备文件删除权限者(运维 / 管理员)可删除任意文件 + if (securityFrameworkService.hasPermission("system:file:delete")) { + return; + } + throw exception(FILE_DELETE_NO_PERMISSION); + } + @Override public byte[] getFileContent(Long configId, String path) throws Exception { // 软删 / 不存在的记录直接拒绝