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.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.validator.ValidDirectoryParam;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileDO;
|
||||
import cn.iocoder.yudao.module.infra.service.file.FileService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
@@ -43,7 +44,7 @@ public class FileController {
|
||||
|
||||
@PostMapping("/upload")
|
||||
@Operation(summary = "上传文件", description = "模式一:后端上传文件")
|
||||
public CommonResult<String> uploadFile(FileUploadReqVO uploadReqVO) throws Exception {
|
||||
public CommonResult<String> uploadFile(@Valid FileUploadReqVO uploadReqVO) throws Exception {
|
||||
MultipartFile file = uploadReqVO.getFile();
|
||||
byte[] content = IoUtil.readBytes(file.getInputStream());
|
||||
return success(fileService.createFile(content, file.getOriginalFilename(),
|
||||
@@ -58,7 +59,7 @@ public class FileController {
|
||||
})
|
||||
public CommonResult<FilePresignedUrlRespVO> getFilePresignedUrl(
|
||||
@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));
|
||||
}
|
||||
|
||||
|
@@ -1,5 +1,6 @@
|
||||
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 jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
@@ -14,6 +15,7 @@ public class FileUploadReqVO {
|
||||
private MultipartFile file;
|
||||
|
||||
@Schema(description = "文件目录", example = "XXX/YYY")
|
||||
@ValidDirectory(message = "目录路径无效,包含非法字符")
|
||||
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.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.FilePresignedUrlRespVO;
|
||||
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 io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
@@ -33,7 +34,7 @@ public class AppFileController {
|
||||
@PostMapping("/upload")
|
||||
@Operation(summary = "上传文件")
|
||||
@PermitAll
|
||||
public CommonResult<String> uploadFile(AppFileUploadReqVO uploadReqVO) throws Exception {
|
||||
public CommonResult<String> uploadFile(@Valid AppFileUploadReqVO uploadReqVO) throws Exception {
|
||||
MultipartFile file = uploadReqVO.getFile();
|
||||
byte[] content = IoUtil.readBytes(file.getInputStream());
|
||||
return success(fileService.createFile(content, file.getOriginalFilename(),
|
||||
|
@@ -1,5 +1,6 @@
|
||||
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 jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
@@ -14,6 +15,7 @@ public class AppFileUploadReqVO {
|
||||
private MultipartFile file;
|
||||
|
||||
@Schema(description = "文件目录", example = "XXX/YYY")
|
||||
@ValidDirectory(message = "目录路径无效,包含非法字符")
|
||||
private String directory;
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user