mirror of
https://github.com/hpd840321/craftlabs-authorization-sdk.git
synced 2026-06-09 10:00:30 +08:00
fix: remove error message leakage in LicenseController and ContractController
Replaced try-catch blocks returning e.getMessage() in HTTP 500 responses with proper ResponseStatusException propagation through global ApiExceptionHandler. Added file size (50MB) and MIME type whitelist validation to contract attachment upload. Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
+51
-21
@@ -16,10 +16,13 @@ import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.Max;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
@@ -34,6 +37,7 @@ import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/contracts")
|
||||
@@ -50,6 +54,16 @@ public class ContractController {
|
||||
this.contractStatusTransitionService = contractStatusTransitionService;
|
||||
}
|
||||
|
||||
private static final long MAX_FILE_SIZE = 50 * 1024 * 1024; // 50MB
|
||||
private static final Set<String> ALLOWED_CONTENT_TYPES = Set.of(
|
||||
MediaType.APPLICATION_PDF_VALUE,
|
||||
"image/jpeg", "image/png", "image/tiff",
|
||||
"application/msword",
|
||||
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||
"application/vnd.ms-excel",
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
||||
);
|
||||
|
||||
@GetMapping
|
||||
public PageResponse<ContractResponse> list(
|
||||
@RequestParam(value = "page", defaultValue = "0") @Min(0) int page,
|
||||
@@ -113,28 +127,44 @@ public class ContractController {
|
||||
public ResponseEntity<Map<String, Object>> uploadAttachment(
|
||||
@PathVariable Long id,
|
||||
@RequestParam("file") MultipartFile file) {
|
||||
try {
|
||||
String uploadDir = System.getProperty("user.dir") + "/uploads/contracts/" + id + "/";
|
||||
File dir = new File(uploadDir);
|
||||
if (!dir.exists()) dir.mkdirs();
|
||||
|
||||
String fileName = System.currentTimeMillis() + "_" + file.getOriginalFilename();
|
||||
File dest = new File(uploadDir + fileName);
|
||||
file.transferTo(dest);
|
||||
|
||||
PlatformContractAttachment attachment = new PlatformContractAttachment();
|
||||
attachment.setContractId(id);
|
||||
attachment.setFileName(file.getOriginalFilename());
|
||||
attachment.setFilePath(dest.getAbsolutePath());
|
||||
attachment.setFileSize(file.getSize());
|
||||
attachment.setContentType(file.getContentType());
|
||||
attachment.setCreatedAt(OffsetDateTime.now());
|
||||
attachmentMapper.insert(attachment);
|
||||
|
||||
return ResponseEntity.ok(Map.of("id", attachment.getId(), "fileName", attachment.getFileName()));
|
||||
} catch (Exception e) {
|
||||
return ResponseEntity.status(500).body(Map.of("error", e.getMessage()));
|
||||
if (file.isEmpty()) {
|
||||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "上传文件为空");
|
||||
}
|
||||
if (file.getSize() > MAX_FILE_SIZE) {
|
||||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "文件大小超过限制(最大 50MB)");
|
||||
}
|
||||
String contentType = file.getContentType();
|
||||
if (contentType == null || !ALLOWED_CONTENT_TYPES.contains(contentType)) {
|
||||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST,
|
||||
"不支持的文件类型: " + (contentType != null ? contentType : "未知"));
|
||||
}
|
||||
|
||||
String uploadDir = System.getProperty("user.dir") + "/uploads/contracts/" + id + "/";
|
||||
File dir = new File(uploadDir);
|
||||
if (!dir.exists()) dir.mkdirs();
|
||||
|
||||
String originalName = file.getOriginalFilename();
|
||||
String ext = originalName != null && originalName.contains(".")
|
||||
? originalName.substring(originalName.lastIndexOf('.'))
|
||||
: "";
|
||||
String storedName = java.util.UUID.randomUUID().toString() + ext;
|
||||
File dest = new File(uploadDir + storedName);
|
||||
try {
|
||||
file.transferTo(dest);
|
||||
} catch (IOException e) {
|
||||
throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "文件存储失败");
|
||||
}
|
||||
|
||||
PlatformContractAttachment attachment = new PlatformContractAttachment();
|
||||
attachment.setContractId(id);
|
||||
attachment.setFileName(originalName);
|
||||
attachment.setFilePath(dest.getAbsolutePath());
|
||||
attachment.setFileSize(file.getSize());
|
||||
attachment.setContentType(contentType);
|
||||
attachment.setCreatedAt(OffsetDateTime.now());
|
||||
attachmentMapper.insert(attachment);
|
||||
|
||||
return ResponseEntity.ok(Map.of("id", attachment.getId(), "fileName", attachment.getFileName()));
|
||||
}
|
||||
|
||||
@GetMapping("/{id}/attachments")
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ public class LicenseController {
|
||||
try {
|
||||
return ResponseEntity.ok(licenseService.create(request));
|
||||
} catch (Exception e) {
|
||||
return ResponseEntity.internalServerError().body(Map.of("error", e.getMessage()));
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user