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:
+38
-8
@@ -16,10 +16,13 @@ import jakarta.validation.Valid;
|
|||||||
import jakarta.validation.constraints.Max;
|
import jakarta.validation.constraints.Max;
|
||||||
import jakarta.validation.constraints.Min;
|
import jakarta.validation.constraints.Min;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
import java.time.OffsetDateTime;
|
import java.time.OffsetDateTime;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
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.ResponseStatus;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
import org.springframework.web.server.ResponseStatusException;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/api/v1/contracts")
|
@RequestMapping("/api/v1/contracts")
|
||||||
@@ -50,6 +54,16 @@ public class ContractController {
|
|||||||
this.contractStatusTransitionService = contractStatusTransitionService;
|
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
|
@GetMapping
|
||||||
public PageResponse<ContractResponse> list(
|
public PageResponse<ContractResponse> list(
|
||||||
@RequestParam(value = "page", defaultValue = "0") @Min(0) int page,
|
@RequestParam(value = "page", defaultValue = "0") @Min(0) int page,
|
||||||
@@ -113,28 +127,44 @@ public class ContractController {
|
|||||||
public ResponseEntity<Map<String, Object>> uploadAttachment(
|
public ResponseEntity<Map<String, Object>> uploadAttachment(
|
||||||
@PathVariable Long id,
|
@PathVariable Long id,
|
||||||
@RequestParam("file") MultipartFile file) {
|
@RequestParam("file") MultipartFile file) {
|
||||||
try {
|
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 + "/";
|
String uploadDir = System.getProperty("user.dir") + "/uploads/contracts/" + id + "/";
|
||||||
File dir = new File(uploadDir);
|
File dir = new File(uploadDir);
|
||||||
if (!dir.exists()) dir.mkdirs();
|
if (!dir.exists()) dir.mkdirs();
|
||||||
|
|
||||||
String fileName = System.currentTimeMillis() + "_" + file.getOriginalFilename();
|
String originalName = file.getOriginalFilename();
|
||||||
File dest = new File(uploadDir + fileName);
|
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);
|
file.transferTo(dest);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "文件存储失败");
|
||||||
|
}
|
||||||
|
|
||||||
PlatformContractAttachment attachment = new PlatformContractAttachment();
|
PlatformContractAttachment attachment = new PlatformContractAttachment();
|
||||||
attachment.setContractId(id);
|
attachment.setContractId(id);
|
||||||
attachment.setFileName(file.getOriginalFilename());
|
attachment.setFileName(originalName);
|
||||||
attachment.setFilePath(dest.getAbsolutePath());
|
attachment.setFilePath(dest.getAbsolutePath());
|
||||||
attachment.setFileSize(file.getSize());
|
attachment.setFileSize(file.getSize());
|
||||||
attachment.setContentType(file.getContentType());
|
attachment.setContentType(contentType);
|
||||||
attachment.setCreatedAt(OffsetDateTime.now());
|
attachment.setCreatedAt(OffsetDateTime.now());
|
||||||
attachmentMapper.insert(attachment);
|
attachmentMapper.insert(attachment);
|
||||||
|
|
||||||
return ResponseEntity.ok(Map.of("id", attachment.getId(), "fileName", attachment.getFileName()));
|
return ResponseEntity.ok(Map.of("id", attachment.getId(), "fileName", attachment.getFileName()));
|
||||||
} catch (Exception e) {
|
|
||||||
return ResponseEntity.status(500).body(Map.of("error", e.getMessage()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/{id}/attachments")
|
@GetMapping("/{id}/attachments")
|
||||||
|
|||||||
+1
-1
@@ -22,7 +22,7 @@ public class LicenseController {
|
|||||||
try {
|
try {
|
||||||
return ResponseEntity.ok(licenseService.create(request));
|
return ResponseEntity.ok(licenseService.create(request));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return ResponseEntity.internalServerError().body(Map.of("error", e.getMessage()));
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user