From 1a59c1c1ec723223a24540177b81f2183ab420aa Mon Sep 17 00:00:00 2001 From: gulongcheng <474084054@qq.com> Date: Thu, 4 Dec 2025 16:35:59 +0800 Subject: [PATCH] =?UTF-8?q?=E7=94=A8=E6=88=B7=E6=9D=83=E9=99=90=E5=88=A4?= =?UTF-8?q?=E6=96=AD=EF=BC=8C=E9=BB=98=E8=AE=A4=E7=BB=A7=E6=89=BF=E7=9B=AE?= =?UTF-8?q?=E5=BD=95=E6=9D=83=E9=99=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/pom.xml | 14 ++++ .../sdm/data/aop/PermissionCheckAspect.java | 72 +++++++++++++++++-- .../data/controller/DataFileController.java | 1 - .../sdm/data/service/IDataFileService.java | 2 + .../impl/MinioFileIDataFileServiceImpl.java | 22 +++--- 5 files changed, 94 insertions(+), 17 deletions(-) diff --git a/data/pom.xml b/data/pom.xml index f6b20554..6722fa82 100644 --- a/data/pom.xml +++ b/data/pom.xml @@ -123,6 +123,20 @@ + + + org.apache.maven.plugins + maven-compiler-plugin + 3.11.0 + + 17 + 17 + + -parameters + + + + org.springframework.boot spring-boot-maven-plugin diff --git a/data/src/main/java/com/sdm/data/aop/PermissionCheckAspect.java b/data/src/main/java/com/sdm/data/aop/PermissionCheckAspect.java index b8d86ab9..77a3c2d9 100644 --- a/data/src/main/java/com/sdm/data/aop/PermissionCheckAspect.java +++ b/data/src/main/java/com/sdm/data/aop/PermissionCheckAspect.java @@ -6,7 +6,12 @@ import com.sdm.data.service.IFileUserPermissionService; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; +import org.aspectj.lang.reflect.MethodSignature; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.expression.EvaluationContext; +import org.springframework.expression.ExpressionParser; +import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.expression.spel.support.SimpleEvaluationContext; import org.springframework.stereotype.Component; import java.lang.annotation.ElementType; @@ -18,27 +23,80 @@ import java.nio.file.AccessDeniedException; @Aspect @Component public class PermissionCheckAspect { - + @Autowired private IFileUserPermissionService fileUserPermissionService; - + + private final ExpressionParser parser = new SpelExpressionParser(); + private final EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build(); + @Before("@annotation(filePermissionCheck)") - public void checkPermission(JoinPoint joinPoint, FilePermissionCheck filePermissionCheck) throws AccessDeniedException { + public void checkPermission(JoinPoint joinPoint, FilePermissionCheck filePermissionCheck){ Long userId = ThreadLocalContext.getUserId(); + if (userId == null) { + throw new RuntimeException("用户未登录"); + } - Object[] args = joinPoint.getArgs(); + // 解析 fileId + Long fileId = resolveFileId(joinPoint, filePermissionCheck.fileIdExpression()); + if (fileId == null) { + throw new RuntimeException("无法解析 fileId,请检查 @FilePermissionCheck.fileIdExpression()"); + } - Long fileId = 1L; // 检查权限 if (!fileUserPermissionService.hasFilePermission(fileId, userId, filePermissionCheck.value())) { - throw new AccessDeniedException("Permission denied"); + throw new RuntimeException("用户无操作文件和文件夹权限"); } } - + + private Long resolveFileId(JoinPoint joinPoint, String expression) { + if (expression == null || expression.isEmpty()) { + // 默认:假设第一个参数是 fileId(Long 类型) + Object[] args = joinPoint.getArgs(); + if (args.length > 0 && args[0] instanceof Long) { + return (Long) args[0]; + } + throw new RuntimeException("未指定 fileIdExpression,且第一个参数不是 Long 类型"); + } + + // 使用 SpEL 解析 + try { + // 获取方法参数名(需要编译时保留参数名:-parameters 或 debug info) + String[] paramNames = getParameterNames(joinPoint); + Object[] args = joinPoint.getArgs(); + + // 构建变量映射 + for (int i = 0; i < paramNames.length && i < args.length; i++) { + context.setVariable(paramNames[i], args[i]); + } + + Object result = parser.parseExpression(expression).getValue(context); + if (result instanceof Number) { + return ((Number) result).longValue(); + } else if (result instanceof String) { + return Long.parseLong((String) result); + } + return null; + } catch (Exception e) { + throw new RuntimeException("解析 fileId 失败: " + expression, e); + } + } + + // 简单方式:依赖编译参数保留形参名(推荐加 -parameters 到 javac) + private String[] getParameterNames(JoinPoint joinPoint) { + MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); + return methodSignature.getParameterNames(); // 需要 JDK 8+ 且编译时加 -parameters + } + + // 自定义注解用于标记需要权限检查的方法 @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface FilePermissionCheck { FilePermissionEnum value(); + + // 新增:指定 fileId 在哪个参数位置(从 0 开始),或通过 SpEL 表达式 + // 例如: "#fileId", "#req.fileId", "#delFileReq.delFileId" + String fileIdExpression() default ""; } } diff --git a/data/src/main/java/com/sdm/data/controller/DataFileController.java b/data/src/main/java/com/sdm/data/controller/DataFileController.java index 587f6b71..17f9a320 100644 --- a/data/src/main/java/com/sdm/data/controller/DataFileController.java +++ b/data/src/main/java/com/sdm/data/controller/DataFileController.java @@ -26,7 +26,6 @@ import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; -import java.io.File; import java.util.List; @RestController diff --git a/data/src/main/java/com/sdm/data/service/IDataFileService.java b/data/src/main/java/com/sdm/data/service/IDataFileService.java index ade1458a..6e12ca83 100644 --- a/data/src/main/java/com/sdm/data/service/IDataFileService.java +++ b/data/src/main/java/com/sdm/data/service/IDataFileService.java @@ -1,12 +1,14 @@ package com.sdm.data.service; import com.sdm.common.common.SdmResponse; +import com.sdm.common.entity.enums.FilePermissionEnum; import com.sdm.common.entity.req.data.*; import com.sdm.common.entity.req.system.LaunchApproveReq; import com.sdm.common.entity.resp.PageDataResp; import com.sdm.common.entity.resp.data.BatchAddFileInfoResp; import com.sdm.common.entity.resp.data.ChunkUploadMinioFileResp; import com.sdm.common.entity.resp.data.FileMetadataInfoResp; +import com.sdm.data.aop.PermissionCheckAspect; import com.sdm.data.model.req.*; import com.sdm.data.model.resp.KKFileViewURLFromMinioResp; import jakarta.servlet.http.HttpServletResponse; diff --git a/data/src/main/java/com/sdm/data/service/impl/MinioFileIDataFileServiceImpl.java b/data/src/main/java/com/sdm/data/service/impl/MinioFileIDataFileServiceImpl.java index 7a43b1db..3e578159 100644 --- a/data/src/main/java/com/sdm/data/service/impl/MinioFileIDataFileServiceImpl.java +++ b/data/src/main/java/com/sdm/data/service/impl/MinioFileIDataFileServiceImpl.java @@ -31,6 +31,7 @@ import com.sdm.common.feign.inter.system.IApproveFeignClient; import com.sdm.common.log.CoreLogger; import com.sdm.common.utils.*; import com.sdm.common.utils.excel.ExcelUtil; +import com.sdm.data.aop.PermissionCheckAspect; import com.sdm.data.model.bo.ApprovalFileDataContentsModel; import com.sdm.data.model.entity.*; import com.sdm.data.model.req.*; @@ -496,7 +497,6 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { * @param req 创建目录请求 * @return 创建结果 */ - public SdmResponse createRootDir(CreateDirReq req) { if(ObjectUtils.isEmpty(req.getDirType())){ log.error("请选择目录类型:1 知识库文件夹,2 项目节点文件夹,3 头像库文件夹,4 仿真参数库文件夹,5 训练模型文件夹"); @@ -839,6 +839,7 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { @Override @Transactional(rollbackFor = Exception.class) + @PermissionCheckAspect.FilePermissionCheck(value = FilePermissionEnum.DELETE, fileIdExpression = "#req.delFileId") public SdmResponse delFile(DelFileReq req) { try { Long delFileId = req.getDelFileId(); @@ -852,10 +853,10 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { String deleteFileMinioObejctKey = deleteFileMetadataInfo.getObjectKey(); - boolean hasDeletePermission = fileUserPermissionService.hasFilePermission(deleteFileMetadataInfo.getId(), ThreadLocalContext.getUserId(), FilePermissionEnum.DELETE); + /*boolean hasDeletePermission = fileUserPermissionService.hasFilePermission(deleteFileMetadataInfo.getId(), ThreadLocalContext.getUserId(), FilePermissionEnum.DELETE); if (!hasDeletePermission) { return SdmResponse.failed("没有删除权限"); - } + }*/ // 知识库 if(dirMetadataInfo!=null&&Objects.equals(DirTypeEnum.KNOWLEDGE_BASE_DIR.getValue(), dirMetadataInfo.getDirType())){ @@ -1047,6 +1048,7 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { @Override @Transactional(rollbackFor = Exception.class) + @PermissionCheckAspect.FilePermissionCheck(value = FilePermissionEnum.WRITE, fileIdExpression = "#req.fileId") public SdmResponse renameFile(RenameFileReq req) { String oldObjectKey = null; String newObjectKey = null; @@ -1056,10 +1058,10 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { if (ObjectUtils.isEmpty(oldFile)) { return SdmResponse.failed("文件不存在"); } - boolean hasDeletePermission = fileUserPermissionService.hasFilePermission(fileId, ThreadLocalContext.getUserId(), FilePermissionEnum.WRITE); + /*boolean hasDeletePermission = fileUserPermissionService.hasFilePermission(fileId, ThreadLocalContext.getUserId(), FilePermissionEnum.WRITE); if (!hasDeletePermission){ return SdmResponse.failed("没有修改权限"); - } + }*/ String newName = req.getNewName(); oldObjectKey = oldFile.getObjectKey(); newObjectKey = oldObjectKey.substring(0, oldObjectKey.lastIndexOf("/") + 1) + newName; @@ -1221,6 +1223,7 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { } @Override + @PermissionCheckAspect.FilePermissionCheck(value = FilePermissionEnum.DOWNLOAD, fileIdExpression = "#req.fileId") public void downloadFile(DownloadFileReq req, HttpServletResponse response) { try { FileMetadataInfo fileMetadataInfo = fileMetadataInfoService.lambdaQuery().eq(FileMetadataInfo::getId, req.getFileId()).one(); @@ -1230,11 +1233,11 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { } String fileObjectKey = fileMetadataInfo.getObjectKey(); - boolean hasDownloadPermission = fileUserPermissionService.hasFilePermission(fileMetadataInfo.getId(), ThreadLocalContext.getUserId(), FilePermissionEnum.DOWNLOAD); + /* boolean hasDownloadPermission = fileUserPermissionService.hasFilePermission(fileMetadataInfo.getId(), ThreadLocalContext.getUserId(), FilePermissionEnum.DOWNLOAD); if (!hasDownloadPermission) { response.sendError(HttpServletResponse.SC_NON_AUTHORITATIVE_INFORMATION, "用户无权限"); return; - } + }*/ // 从MinIO下载文件 String encodedFileName = URLEncoder.encode(fileMetadataInfo.getOriginalName(), StandardCharsets.UTF_8); minioService.downloadFile(fileObjectKey,response,encodedFileName,false,""); @@ -1467,6 +1470,7 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { } @Transactional(rollbackFor = Exception.class) + @PermissionCheckAspect.FilePermissionCheck(value = FilePermissionEnum.UPLOAD, fileIdExpression = "#req.id") public SdmResponse updateFile(UpdateFileReq req) { FileMetadataInfo fileMetadataInfo = fileMetadataInfoService.lambdaQuery().eq(FileMetadataInfo::getId, req.getId()).one(); if (fileMetadataInfo == null) { @@ -1474,10 +1478,10 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { } // 文件夹 FileMetadataInfo dirMetadataInfo = fileMetadataInfoService.lambdaQuery().eq(FileMetadataInfo::getId, fileMetadataInfo.getParentId()).eq(FileMetadataInfo::getDataType, DataTypeEnum.DIRECTORY.getValue()).one(); - boolean hasUploadPermission = fileUserPermissionService.hasFilePermission(fileMetadataInfo.getId(), ThreadLocalContext.getUserId(), FilePermissionEnum.UPLOAD); + /* boolean hasUploadPermission = fileUserPermissionService.hasFilePermission(fileMetadataInfo.getId(), ThreadLocalContext.getUserId(), FilePermissionEnum.UPLOAD); if (!hasUploadPermission) { return SdmResponse.failed("没有更新权限"); - } + }*/ String oldFileMinioObjectKey = fileMetadataInfo.getObjectKey(); Long fileGroupId = fileMetadataInfo.getFileGroupId(); Long versionNo = fileMetadataInfo.getVersionNo();