Checkpoint before follow-up message
This commit is contained in:
@@ -8,6 +8,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
|||||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||||
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
|
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
|
||||||
import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.*;
|
import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.*;
|
||||||
|
import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.validator.ValidDirectoryParam;
|
||||||
import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileDO;
|
import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileDO;
|
||||||
import cn.iocoder.yudao.module.infra.service.file.FileService;
|
import cn.iocoder.yudao.module.infra.service.file.FileService;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
@@ -43,7 +44,7 @@ public class FileController {
|
|||||||
|
|
||||||
@PostMapping("/upload")
|
@PostMapping("/upload")
|
||||||
@Operation(summary = "上传文件", description = "模式一:后端上传文件")
|
@Operation(summary = "上传文件", description = "模式一:后端上传文件")
|
||||||
public CommonResult<String> uploadFile(FileUploadReqVO uploadReqVO) throws Exception {
|
public CommonResult<String> uploadFile(@Valid FileUploadReqVO uploadReqVO) throws Exception {
|
||||||
MultipartFile file = uploadReqVO.getFile();
|
MultipartFile file = uploadReqVO.getFile();
|
||||||
byte[] content = IoUtil.readBytes(file.getInputStream());
|
byte[] content = IoUtil.readBytes(file.getInputStream());
|
||||||
return success(fileService.createFile(content, file.getOriginalFilename(),
|
return success(fileService.createFile(content, file.getOriginalFilename(),
|
||||||
@@ -58,7 +59,7 @@ public class FileController {
|
|||||||
})
|
})
|
||||||
public CommonResult<FilePresignedUrlRespVO> getFilePresignedUrl(
|
public CommonResult<FilePresignedUrlRespVO> getFilePresignedUrl(
|
||||||
@RequestParam("name") String name,
|
@RequestParam("name") String name,
|
||||||
@RequestParam(value = "directory", required = false) String directory) {
|
@ValidDirectoryParam @RequestParam(value = "directory", required = false) String directory) {
|
||||||
return success(fileService.getFilePresignedUrl(name, directory));
|
return success(fileService.getFilePresignedUrl(name, directory));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
package cn.iocoder.yudao.module.infra.controller.admin.file.vo.file;
|
package cn.iocoder.yudao.module.infra.controller.admin.file.vo.file;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.validator.ValidDirectory;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import jakarta.validation.constraints.NotNull;
|
import jakarta.validation.constraints.NotNull;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
@@ -14,6 +15,7 @@ public class FileUploadReqVO {
|
|||||||
private MultipartFile file;
|
private MultipartFile file;
|
||||||
|
|
||||||
@Schema(description = "文件目录", example = "XXX/YYY")
|
@Schema(description = "文件目录", example = "XXX/YYY")
|
||||||
|
@ValidDirectory(message = "目录路径无效,包含非法字符")
|
||||||
private String directory;
|
private String directory;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,65 @@
|
|||||||
|
package cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.validator;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import jakarta.validation.ConstraintValidator;
|
||||||
|
import jakarta.validation.ConstraintValidatorContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 目录路径参数验证器实现
|
||||||
|
*
|
||||||
|
* @author 芋道源码
|
||||||
|
*/
|
||||||
|
public class DirectoryParamValidator implements ConstraintValidator<ValidDirectoryParam, String> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize(ValidDirectoryParam constraintAnnotation) {
|
||||||
|
// 初始化方法,如果需要可以在这里进行一些初始化操作
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isValid(String directory, ConstraintValidatorContext context) {
|
||||||
|
// 如果为空,则认为是有效的(可选字段)
|
||||||
|
if (StrUtil.isEmpty(directory)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否包含路径遍历攻击
|
||||||
|
return !containsPathTraversal(directory);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查路径是否包含路径遍历攻击
|
||||||
|
*
|
||||||
|
* @param path 路径
|
||||||
|
* @return 是否包含路径遍历攻击
|
||||||
|
*/
|
||||||
|
private boolean containsPathTraversal(String path) {
|
||||||
|
if (StrUtil.isEmpty(path)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 统一使用正斜杠
|
||||||
|
String normalizedPath = path.replace('\\', '/');
|
||||||
|
|
||||||
|
// 检查常见的路径遍历模式
|
||||||
|
String[] dangerousPatterns = {
|
||||||
|
"..", "..\\", "../", "..%2f", "..%5c", "..%2F", "..%5C",
|
||||||
|
"%2e%2e", "%2E%2E", "%2e%2e%2f", "%2E%2E%2F",
|
||||||
|
"....//", "....\\\\", "....%2f", "....%5c"
|
||||||
|
};
|
||||||
|
|
||||||
|
String lowerPath = normalizedPath.toLowerCase();
|
||||||
|
for (String pattern : dangerousPatterns) {
|
||||||
|
if (lowerPath.contains(pattern)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查绝对路径
|
||||||
|
if (normalizedPath.startsWith("/") || normalizedPath.matches("^[A-Za-z]:.*")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,65 @@
|
|||||||
|
package cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.validator;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import jakarta.validation.ConstraintValidator;
|
||||||
|
import jakarta.validation.ConstraintValidatorContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 目录路径验证器实现
|
||||||
|
*
|
||||||
|
* @author 芋道源码
|
||||||
|
*/
|
||||||
|
public class DirectoryValidator implements ConstraintValidator<ValidDirectory, String> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize(ValidDirectory constraintAnnotation) {
|
||||||
|
// 初始化方法,如果需要可以在这里进行一些初始化操作
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isValid(String directory, ConstraintValidatorContext context) {
|
||||||
|
// 如果为空,则认为是有效的(可选字段)
|
||||||
|
if (StrUtil.isEmpty(directory)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否包含路径遍历攻击
|
||||||
|
return !containsPathTraversal(directory);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查路径是否包含路径遍历攻击
|
||||||
|
*
|
||||||
|
* @param path 路径
|
||||||
|
* @return 是否包含路径遍历攻击
|
||||||
|
*/
|
||||||
|
private boolean containsPathTraversal(String path) {
|
||||||
|
if (StrUtil.isEmpty(path)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 统一使用正斜杠
|
||||||
|
String normalizedPath = path.replace('\\', '/');
|
||||||
|
|
||||||
|
// 检查常见的路径遍历模式
|
||||||
|
String[] dangerousPatterns = {
|
||||||
|
"..", "..\\", "../", "..%2f", "..%5c", "..%2F", "..%5C",
|
||||||
|
"%2e%2e", "%2E%2E", "%2e%2e%2f", "%2E%2E%2F",
|
||||||
|
"....//", "....\\\\", "....%2f", "....%5c"
|
||||||
|
};
|
||||||
|
|
||||||
|
String lowerPath = normalizedPath.toLowerCase();
|
||||||
|
for (String pattern : dangerousPatterns) {
|
||||||
|
if (lowerPath.contains(pattern)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查绝对路径
|
||||||
|
if (normalizedPath.startsWith("/") || normalizedPath.matches("^[A-Za-z]:.*")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,23 @@
|
|||||||
|
package cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.validator;
|
||||||
|
|
||||||
|
import jakarta.validation.Constraint;
|
||||||
|
import jakarta.validation.Payload;
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 目录路径验证注解
|
||||||
|
*
|
||||||
|
* @author 芋道源码
|
||||||
|
*/
|
||||||
|
@Target({ElementType.FIELD, ElementType.PARAMETER})
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Documented
|
||||||
|
@Constraint(validatedBy = DirectoryValidator.class)
|
||||||
|
public @interface ValidDirectory {
|
||||||
|
|
||||||
|
String message() default "目录路径无效,包含非法字符";
|
||||||
|
|
||||||
|
Class<?>[] groups() default {};
|
||||||
|
|
||||||
|
Class<? extends Payload>[] payload() default {};
|
||||||
|
}
|
@@ -0,0 +1,23 @@
|
|||||||
|
package cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.validator;
|
||||||
|
|
||||||
|
import jakarta.validation.Constraint;
|
||||||
|
import jakarta.validation.Payload;
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 目录路径参数验证注解
|
||||||
|
*
|
||||||
|
* @author 芋道源码
|
||||||
|
*/
|
||||||
|
@Target({ElementType.FIELD, ElementType.PARAMETER})
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Documented
|
||||||
|
@Constraint(validatedBy = DirectoryParamValidator.class)
|
||||||
|
public @interface ValidDirectoryParam {
|
||||||
|
|
||||||
|
String message() default "目录路径无效,包含非法字符";
|
||||||
|
|
||||||
|
Class<?>[] groups() default {};
|
||||||
|
|
||||||
|
Class<? extends Payload>[] payload() default {};
|
||||||
|
}
|
@@ -1,10 +1,11 @@
|
|||||||
package cn.iocoder.yudao.module.infra.controller.app.file;
|
THIS SHOULD BE A LINTER ERRORpackage cn.iocoder.yudao.module.infra.controller.app.file;
|
||||||
|
|
||||||
import cn.hutool.core.io.IoUtil;
|
import cn.hutool.core.io.IoUtil;
|
||||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||||
import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FileCreateReqVO;
|
import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FileCreateReqVO;
|
||||||
import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FilePresignedUrlRespVO;
|
import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FilePresignedUrlRespVO;
|
||||||
import cn.iocoder.yudao.module.infra.controller.app.file.vo.AppFileUploadReqVO;
|
import cn.iocoder.yudao.module.infra.controller.app.file.vo.AppFileUploadReqVO;
|
||||||
|
import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.validator.ValidDirectoryParam;
|
||||||
import cn.iocoder.yudao.module.infra.service.file.FileService;
|
import cn.iocoder.yudao.module.infra.service.file.FileService;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
@@ -33,7 +34,7 @@ public class AppFileController {
|
|||||||
@PostMapping("/upload")
|
@PostMapping("/upload")
|
||||||
@Operation(summary = "上传文件")
|
@Operation(summary = "上传文件")
|
||||||
@PermitAll
|
@PermitAll
|
||||||
public CommonResult<String> uploadFile(AppFileUploadReqVO uploadReqVO) throws Exception {
|
public CommonResult<String> uploadFile(@Valid AppFileUploadReqVO uploadReqVO) throws Exception {
|
||||||
MultipartFile file = uploadReqVO.getFile();
|
MultipartFile file = uploadReqVO.getFile();
|
||||||
byte[] content = IoUtil.readBytes(file.getInputStream());
|
byte[] content = IoUtil.readBytes(file.getInputStream());
|
||||||
return success(fileService.createFile(content, file.getOriginalFilename(),
|
return success(fileService.createFile(content, file.getOriginalFilename(),
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
package cn.iocoder.yudao.module.infra.controller.app.file.vo;
|
package cn.iocoder.yudao.module.infra.controller.app.file.vo;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.validator.ValidDirectory;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import jakarta.validation.constraints.NotNull;
|
import jakarta.validation.constraints.NotNull;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
@@ -14,6 +15,7 @@ public class AppFileUploadReqVO {
|
|||||||
private MultipartFile file;
|
private MultipartFile file;
|
||||||
|
|
||||||
@Schema(description = "文件目录", example = "XXX/YYY")
|
@Schema(description = "文件目录", example = "XXX/YYY")
|
||||||
|
@ValidDirectory(message = "目录路径无效,包含非法字符")
|
||||||
private String directory;
|
private String directory;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user