1. 完善附件加密下载验证码获取流程
This commit is contained in:
@@ -21,6 +21,7 @@ import jakarta.servlet.http.HttpServletRequest;
|
|||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
@@ -28,6 +29,8 @@ import org.springframework.web.bind.annotation.*;
|
|||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||||
@@ -45,6 +48,9 @@ public class FileController {
|
|||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private FileService fileService;
|
private FileService fileService;
|
||||||
|
@Value("${yudao.kkfile:}")
|
||||||
|
private String onlinePreview;
|
||||||
|
|
||||||
|
|
||||||
@PostMapping("/upload")
|
@PostMapping("/upload")
|
||||||
@Operation(summary = "上传文件", description = "模式一:后端上传文件")
|
@Operation(summary = "上传文件", description = "模式一:后端上传文件")
|
||||||
@@ -59,7 +65,13 @@ public class FileController {
|
|||||||
public CommonResult<FileRespVO> uploadWithReturn(FileUploadReqVO uploadReqVO) throws IOException {
|
public CommonResult<FileRespVO> uploadWithReturn(FileUploadReqVO uploadReqVO) throws IOException {
|
||||||
MultipartFile file = uploadReqVO.getFile();
|
MultipartFile file = uploadReqVO.getFile();
|
||||||
byte[] content = IoUtil.readBytes(file.getInputStream());
|
byte[] content = IoUtil.readBytes(file.getInputStream());
|
||||||
return success(fileService.createFileWhitReturn(content, file.getOriginalFilename(), uploadReqVO.getDirectory(), file.getContentType(), uploadReqVO.getEncrypt()));
|
FileRespVO fileWhitReturn = fileService.createFileWhitReturn(content, file.getOriginalFilename(), uploadReqVO.getDirectory(), file.getContentType(), uploadReqVO.getEncrypt());
|
||||||
|
if (StrUtil.isEmpty(onlinePreview) || StrUtil.isEmpty(fileWhitReturn.getUrl())) {
|
||||||
|
fileWhitReturn.setPreviewUrl(fileWhitReturn.getUrl());
|
||||||
|
return success(fileWhitReturn);
|
||||||
|
}
|
||||||
|
fileWhitReturn.setPreviewUrl(onlinePreview + URLEncoder.encode(fileWhitReturn.getUrl(), StandardCharsets.UTF_8));
|
||||||
|
return success(fileWhitReturn);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/presigned-url")
|
@GetMapping("/presigned-url")
|
||||||
@@ -147,12 +159,13 @@ public class FileController {
|
|||||||
@Operation(summary = "获取下载验证码")
|
@Operation(summary = "获取下载验证码")
|
||||||
public CommonResult<String> preDownloadEncrypt(@RequestParam("fileId") Long fileId) {
|
public CommonResult<String> preDownloadEncrypt(@RequestParam("fileId") Long fileId) {
|
||||||
Long userId = getLoginUserId();
|
Long userId = getLoginUserId();
|
||||||
|
FileDO activeFileById = new FileDO();
|
||||||
try {
|
try {
|
||||||
|
activeFileById = fileService.getActiveFileById(fileId);
|
||||||
fileService.generateFileVerificationCode(fileId, userId);
|
fileService.generateFileVerificationCode(fileId, userId);
|
||||||
FileDO activeFileById = fileService.getActiveFileById(fileId);
|
|
||||||
return CommonResult.customize(activeFileById.getName(), HttpStatus.OK.value(), "验证码已生成,请使用验证码下载文件");
|
return CommonResult.customize(activeFileById.getName(), HttpStatus.OK.value(), "验证码已生成,请使用验证码下载文件");
|
||||||
} catch (ServiceException e) {
|
} catch (ServiceException e) {
|
||||||
return CommonResult.customize("生成验证码失败", HttpStatus.OK.value(), e.getMessage());
|
return CommonResult.customize(activeFileById.getName(), HttpStatus.BAD_REQUEST.value(), e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,10 @@ public class FileRespVO {
|
|||||||
@Schema(description = "文件 URL", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/yudao.jpg")
|
@Schema(description = "文件 URL", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/yudao.jpg")
|
||||||
private String url;
|
private String url;
|
||||||
|
|
||||||
|
// 附件预览地址
|
||||||
|
@Schema(description = "附件预览地址", example = "https://www.iocoder.cn/yudao.jpg")
|
||||||
|
private String previewUrl;
|
||||||
|
|
||||||
@Schema(description = "文件MIME类型", example = "application/octet-stream")
|
@Schema(description = "文件MIME类型", example = "application/octet-stream")
|
||||||
private String type;
|
private String type;
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import cn.hutool.core.io.FileUtil;
|
|||||||
import cn.hutool.core.lang.Assert;
|
import cn.hutool.core.lang.Assert;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import cn.hutool.crypto.digest.DigestUtil;
|
import cn.hutool.crypto.digest.DigestUtil;
|
||||||
import cn.iocoder.yudao.framework.common.exception.ServiceException;
|
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
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.module.infra.controller.admin.file.vo.file.FileCreateReqVO;
|
import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FileCreateReqVO;
|
||||||
@@ -48,7 +47,7 @@ public class FileServiceImpl implements FileService {
|
|||||||
|
|
||||||
@Value("${yudao.AES.key}")
|
@Value("${yudao.AES.key}")
|
||||||
private String aesKey;
|
private String aesKey;
|
||||||
@Value("${yudao.verify-code:''}")
|
@Value("${yudao.verify-code:}")
|
||||||
private String fixedVerifyCode;
|
private String fixedVerifyCode;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
@@ -59,11 +58,7 @@ public class FileServiceImpl implements FileService {
|
|||||||
validateFileExists(fileId);
|
validateFileExists(fileId);
|
||||||
String codeKey = String.format(RedisKeyConstants.FILE_VERIFICATION_CODE, userId, fileId);
|
String codeKey = String.format(RedisKeyConstants.FILE_VERIFICATION_CODE, userId, fileId);
|
||||||
String userSetKey = String.format(RedisKeyConstants.FILE_VERIFICATION_CODE_USER_SET, userId);
|
String userSetKey = String.format(RedisKeyConstants.FILE_VERIFICATION_CODE_USER_SET, userId);
|
||||||
try {
|
return VerificationCodeUtil.generateCode(codeKey, userSetKey, stringRedisTemplate);
|
||||||
return VerificationCodeUtil.generateCode(codeKey, userSetKey, stringRedisTemplate);
|
|
||||||
} catch (ServiceException e) {
|
|
||||||
throw exception(FILE_CAPTCHA_GENERATE_FAIL, e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -28,13 +28,13 @@ public class VerificationCodeUtil {
|
|||||||
if (existCode != null) {
|
if (existCode != null) {
|
||||||
Long ttl = redisTemplate.getExpire(codeKey, TimeUnit.SECONDS);
|
Long ttl = redisTemplate.getExpire(codeKey, TimeUnit.SECONDS);
|
||||||
if (ttl > 10) {
|
if (ttl > 10) {
|
||||||
throw exception(FILE_CAPTCHA_EXISTS, "同一附件60秒内只能获取一次验证码");
|
throw exception(FILE_CAPTCHA_EXISTS, "当前附件已存在生效中的验证码");
|
||||||
}
|
}
|
||||||
// ttl <= 10 秒,允许重新获取
|
// ttl <= 10 秒,允许重新获取
|
||||||
}
|
}
|
||||||
Long size = redisTemplate.opsForSet().size(userSetKey);
|
Long size = redisTemplate.opsForSet().size(userSetKey);
|
||||||
if (size != null && size >= MAX_CODE_PER_USER) {
|
if (size != null && size >= MAX_CODE_PER_USER) {
|
||||||
throw exception(FILE_CAPTCHA_MAX, "同一用户最多只能有10个有效验证码");
|
throw exception(FILE_CAPTCHA_MAX, "同一用户最多只能获取10个有效验证码");
|
||||||
}
|
}
|
||||||
String code = String.format("%06d", new Random().nextInt(1000000));
|
String code = String.format("%06d", new Random().nextInt(1000000));
|
||||||
redisTemplate.opsForValue().set(codeKey, code, CODE_EXPIRE_SECONDS, TimeUnit.SECONDS);
|
redisTemplate.opsForValue().set(codeKey, code, CODE_EXPIRE_SECONDS, TimeUnit.SECONDS);
|
||||||
|
|||||||
@@ -132,6 +132,8 @@ yudao:
|
|||||||
# 附件加密相关配置
|
# 附件加密相关配置
|
||||||
AES:
|
AES:
|
||||||
key: "0123456789abcdef0123456789abcdef"
|
key: "0123456789abcdef0123456789abcdef"
|
||||||
|
# 附件预览
|
||||||
|
kkfile: "http://172.16.46.63:30012/onlinePreview?url="
|
||||||
info:
|
info:
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
base-package: cn.iocoder.yudao.module.infra
|
base-package: cn.iocoder.yudao.module.infra
|
||||||
|
|||||||
@@ -218,6 +218,8 @@ yudao:
|
|||||||
tencent-lbs-key: TVDBZ-TDILD-4ON4B-PFDZA-RNLKH-VVF6E # QQ 地图的密钥 https://lbs.qq.com/service/staticV2/staticGuide/staticDoc
|
tencent-lbs-key: TVDBZ-TDILD-4ON4B-PFDZA-RNLKH-VVF6E # QQ 地图的密钥 https://lbs.qq.com/service/staticV2/staticGuide/staticDoc
|
||||||
AES:
|
AES:
|
||||||
key: "0123456789abcdef0123456789abcdef"
|
key: "0123456789abcdef0123456789abcdef"
|
||||||
|
verify-code: 666666
|
||||||
|
|
||||||
|
|
||||||
justauth:
|
justauth:
|
||||||
enabled: true
|
enabled: true
|
||||||
|
|||||||
Reference in New Issue
Block a user