[파일 업로드] tusHook 작업

- 리줌 키로 다른 파일 올릴때 막기
This commit is contained in:
2026-03-09 11:40:04 +09:00
parent bc5c44b4d7
commit 2275c857e9
4 changed files with 69 additions and 18 deletions
@@ -64,6 +64,8 @@ public class FileController {
public ResponseEntity<ApiResponse<String>> uploadAuth( public ResponseEntity<ApiResponse<String>> uploadAuth(
@RequestHeader(value = "Authorization", required = false) String uploadToken @RequestHeader(value = "Authorization", required = false) String uploadToken
, @RequestHeader(value = "x-File-Uuid", required = false) String fileUuid , @RequestHeader(value = "x-File-Uuid", required = false) String fileUuid
, @RequestHeader(value = "Upload-Metadata", required = false) String uploadMetadata
, @RequestHeader(value = "Upload-Length", required = false) String uploadLength
) { ) {
if (uploadToken == null || !uploadToken.startsWith("Bearer ")) { if (uploadToken == null || !uploadToken.startsWith("Bearer ")) {
return ApiResponse.entity("", ApiResponseCode.CODE_401); return ApiResponse.entity("", ApiResponseCode.CODE_401);
@@ -75,6 +77,8 @@ public class FileController {
UploadTokenDto uploadTokenDto = new UploadTokenDto(); UploadTokenDto uploadTokenDto = new UploadTokenDto();
uploadTokenDto.setUploadToken(uploadToken.substring(7).trim()); uploadTokenDto.setUploadToken(uploadToken.substring(7).trim());
uploadTokenDto.setFileUuid(fileUuid.trim()); uploadTokenDto.setFileUuid(fileUuid.trim());
uploadTokenDto.setUploadMetadataRaw(uploadMetadata);
uploadTokenDto.setUploadLengthRaw(uploadLength);
UploadTokenDto uploadTokenCheck = fileService.isUploadTokenCheck(uploadTokenDto); UploadTokenDto uploadTokenCheck = fileService.isUploadTokenCheck(uploadTokenDto);
@@ -11,6 +11,13 @@ public class UploadTokenDto {
private String fileUuid; private String fileUuid;
private String uploadToken; private String uploadToken;
private String uploadMetadataRaw;
private String uploadLengthRaw;
private String originName;
private Long sizeBytes;
private String contentType;
@JsonIgnore @JsonIgnore
private int resultCode; private int resultCode;
} }
@@ -88,7 +88,9 @@ public class FileService {
return uploadTokenDto; return uploadTokenDto;
} }
String cacheKey = buildAuthCacheKey(uploadTokenDto.getFileUuid(), uploadTokenDto.getUploadToken()); applyTusMeta(uploadTokenDto);
String cacheKey = buildAuthCacheKey(uploadTokenDto);
try { try {
String cached = stringRedisTemplate.opsForValue().get(cacheKey); String cached = stringRedisTemplate.opsForValue().get(cacheKey);
@@ -122,6 +124,34 @@ public class FileService {
return uploadTokenDto; return uploadTokenDto;
} }
private void applyTusMeta(UploadTokenDto dto) {
String len = dto.getUploadLengthRaw();
if (len != null && !len.isBlank()) {
try { dto.setSizeBytes(Long.parseLong(len.trim())); } catch (Exception ignored) {}
}
String raw = dto.getUploadMetadataRaw();
if (raw == null || raw.isBlank()) return;
for (String part : raw.split(",")) {
String p = part.trim();
int sp = p.indexOf(' ');
if (sp <= 0) continue;
String key = p.substring(0, sp).trim();
String val = p.substring(sp + 1).trim();
String decoded;
try {
decoded = new String(java.util.Base64.getDecoder().decode(val), java.nio.charset.StandardCharsets.UTF_8);
} catch (Exception e) {
continue;
}
if ("filename".equals(key)) dto.setOriginName(decoded);
else if ("filetype".equals(key)) dto.setContentType(decoded);
}
}
public UploadStatusDto getUploadStatus(UploadStatusDto uploadStatusDto) { public UploadStatusDto getUploadStatus(UploadStatusDto uploadStatusDto) {
String key = buildUploadStatusKey(uploadStatusDto.getFileUuid()); String key = buildUploadStatusKey(uploadStatusDto.getFileUuid());
Map<Object, Object> map = stringRedisTemplate.opsForHash().entries(key); Map<Object, Object> map = stringRedisTemplate.opsForHash().entries(key);
@@ -341,8 +371,24 @@ public class FileService {
} }
} }
private String buildAuthCacheKey(String fileUuid, String uploadToken) { private String buildAuthCacheKey(UploadTokenDto dto) {
return "upload:alist:auth:" + fileUuid + ":" + sha256(uploadToken); String normalizedName = normalize(dto.getOriginName());
String normalizedType = normalize(dto.getContentType());
String normalizedSize = (dto.getSizeBytes() == null) ? "" : String.valueOf(dto.getSizeBytes());
String raw = String.join("|",
normalize(dto.getFileUuid()),
dto.getUploadToken() == null ? "" : dto.getUploadToken(),
normalizedName,
normalizedType,
normalizedSize
);
return "upload:alist:auth:" + sha256(raw);
}
private String normalize(String v) {
return (v == null) ? "" : v.trim().toLowerCase();
} }
private String sha256(String value) { private String sha256(String value) {
@@ -355,18 +401,6 @@ public class FileService {
} }
} }
private void evictUploadAuthCacheByFileUuid(String fileUuid) {
try {
String pattern = "upload:alist:auth:" + fileUuid + ":*";
var keys = stringRedisTemplate.keys(pattern);
if (!keys.isEmpty()) {
stringRedisTemplate.delete(keys);
}
} catch (Exception e) {
log.warn("uploadAuth cache evict fail. fileUuid={}", fileUuid, e);
}
}
private long safeTtlSeconds() { private long safeTtlSeconds() {
// 설정 실수 방어: 0/음수면 기본값 20초 사용 // 설정 실수 방어: 0/음수면 기본값 20초 사용
return (uploadAuthCacheTtlSeconds > 0) ? uploadAuthCacheTtlSeconds : 20L; return (uploadAuthCacheTtlSeconds > 0) ? uploadAuthCacheTtlSeconds : 20L;
@@ -130,12 +130,18 @@
/*FileMapper.selectFileDetailCountByFileUuidUserToKenIdx*/ /*FileMapper.selectFileDetailCountByFileUuidUserToKenIdx*/
SELECT COUNT(*) SELECT COUNT(*)
FROM ALISTLMS.FILE_DETAIL LFD FROM ALISTLMS.FILE_DETAIL LFD
INNER JOIN ALISTLMS.test_user_token LTUT INNER JOIN ALISTLMS.test_user_token LTUT ON LTUT.user_idx = LFD.CREATE_MEMBER
ON LTUT.user_idx = LFD.CREATE_MEMBER
WHERE LFD.DEL_YN = 'N' WHERE LFD.DEL_YN = 'N'
AND LFD.FILE_UUID = #{fileUuid} AND LFD.FILE_UUID = #{fileUuid}
AND LFD.STATUS IN (0,1,2,3) <!-- 정책에 맞게 조정 -->
AND LTUT.user_token_idx = #{userTokenIdx} AND LTUT.user_token_idx = #{userTokenIdx}
AND LFD.STATUS IN (0,1,2,3)
AND (#{sizeBytes} IS NULL OR LFD.SIZE_BYTES = #{sizeBytes})
AND (#{originName} IS NULL OR #{originName} = '' OR LFD.ORIGIN_NAME = #{originName})
AND (
#{contentType} IS NULL OR #{contentType} = ''
OR LFD.CONTENT_TYPE IS NULL OR LFD.CONTENT_TYPE = ''
OR LOWER(LFD.CONTENT_TYPE) = LOWER(#{contentType})
)
</select> </select>
<select id="selectFileUploadMetaByFileUuid" resultType="com.alist.api.common.modules.file.dto.FileUploadMetaDto"> <select id="selectFileUploadMetaByFileUuid" resultType="com.alist.api.common.modules.file.dto.FileUploadMetaDto">
/*FileMapper.selectFileUploadMetaByFileUuid*/ /*FileMapper.selectFileUploadMetaByFileUuid*/