diff --git a/src/main/java/kr/re/etri/autoflow/controllers/ExternalAuthController.java b/src/main/java/kr/re/etri/autoflow/controllers/ExternalAuthController.java index ead3311..dfd0212 100644 --- a/src/main/java/kr/re/etri/autoflow/controllers/ExternalAuthController.java +++ b/src/main/java/kr/re/etri/autoflow/controllers/ExternalAuthController.java @@ -2,16 +2,29 @@ package kr.re.etri.autoflow.controllers; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.ParameterIn; import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.tags.Tag; -import kr.re.etri.autoflow.payload.request.EdgePkgInfoRequest; +import jakarta.annotation.PostConstruct; +import kr.re.etri.autoflow.payload.request.EdgeSWVO; import kr.re.etri.autoflow.service.ExternalAuthService; import lombok.RequiredArgsConstructor; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; +import org.springframework.core.io.FileSystemResource; +import org.springframework.http.*; +import org.springframework.http.client.SimpleClientHttpRequestFactory; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; import org.springframework.web.bind.annotation.*; +import org.springframework.web.client.RestTemplate; import org.springframework.web.multipart.MultipartFile; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.cert.X509Certificate; import java.util.Map; @RestController @@ -22,6 +35,47 @@ public class ExternalAuthController { private final ExternalAuthService externalAuthService; + + private RestTemplate restTemplate; + @PostConstruct + public void init() { + this.restTemplate = createUnsafeRestTemplate(); + } + + /** + * SSL 무시용 RestTemplate 생성 + */ + private RestTemplate createUnsafeRestTemplate() { + try { + TrustManager[] trustAllCerts = new TrustManager[]{ + new X509TrustManager() { + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } + + public void checkClientTrusted(X509Certificate[] certs, String authType) { + } + + public void checkServerTrusted(X509Certificate[] certs, String authType) { + } + } + }; + + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, trustAllCerts, new java.security.SecureRandom()); + + HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory()); + HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> true); + + SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory(); + return new RestTemplate(requestFactory); + + } catch (Exception e) { + throw new RuntimeException("Failed to create unsafe RestTemplate", e); + } + } + + @Operation(summary = "외부 로그인 후 사용자 정보 조회", description = "외부 Spring 백엔드에 로그인 요청 후 id, name, token을 반환합니다.") @PostMapping("/signin") public ResponseEntity signin(@RequestBody SigninRequest request) { @@ -47,42 +101,50 @@ public class ExternalAuthController { return ResponseEntity.ok(ApiResponse.failure(e.getMessage())); } } + private final String TARGET_URL = "https://cuuva.com:24443/api/datamanager/edge-pkg/add"; + @PostMapping(value = "/add", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) @Operation( summary = "외부 Edge 패키지 등록", - description = "외부 서버로 Edge 패키지 정보를 파일과 함께 전송하여 등록합니다.", - security = @SecurityRequirement(name = "bearerAuth") // 이 컨트롤러만 Bearer 적용 + description = "VO 정보와 파일을 업로드하여 외부 Edge 패키지를 등록합니다.", + security = {@SecurityRequirement(name = "bearerAuth")} ) - @PostMapping(value = "/add", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public ResponseEntity addEdgePackage( - @Parameter(description = "로그인 시 발급받은 Bearer 토큰") - @RequestHeader("Authorization") String bearerToken, - - @Parameter(description = "Edge 패키지 등록 요청 데이터(JSON 형식)") - @RequestPart(value = "edgePkgInfoVO", required = true) EdgePkgInfoRequest edgePkgInfoRequest, - - @Parameter(description = "업로드할 패키지 파일") - @RequestPart(value = "file", required = false) MultipartFile file + @RequestPart("sw_id") String sw_id, + @RequestPart("sw_name") String sw_name, + @RequestPart("sw_version") String sw_version, + @RequestPart("creation_datetime") String creation_datetime, + @RequestPart("auth_id") String auth_id, + @RequestPart(value = "files", required = false) MultipartFile file, // ✅ 수정: file → files + @RequestHeader("Authorization") String bearerToken ) { + try { + // VO 생성 + EdgeSWVO edgeSWVO = new EdgeSWVO(); + edgeSWVO.setSw_id(sw_id); + edgeSWVO.setSw_name(sw_name); + edgeSWVO.setSw_version(sw_version); + edgeSWVO.setCreation_datetime(creation_datetime); + edgeSWVO.setAuth_id(auth_id); + edgeSWVO.setFile(file); // ✅ 반드시 추가해야 WebClient에서 파일 전송 가능 + + // 토큰 정제 + String token = bearerToken.startsWith("Bearer ") ? bearerToken.substring(7) : bearerToken; + // 서비스 호출 - Object response = externalAuthService.uploadEdgePackage(bearerToken, edgePkgInfoRequest, file); - return ResponseEntity.ok(response); + Map result = externalAuthService.addEdgePackage(token, edgeSWVO); - } catch (IllegalArgumentException e) { - return ResponseEntity.badRequest().body( - Map.of("success", false, "message", "잘못된 요청: " + e.getMessage()) - ); + return ResponseEntity.ok(result); } catch (Exception e) { - return ResponseEntity.internalServerError().body( - Map.of("success", false, "message", "파일 업로드 실패: " + e.getMessage()) - ); + return ResponseEntity.badRequest().body(Map.of( + "success", false, + "message", e.getMessage() + )); } } - - // DTO: 요청 public static record SigninRequest(String id, String password) { } diff --git a/src/main/java/kr/re/etri/autoflow/payload/request/EdgePkgInfoRequest.java b/src/main/java/kr/re/etri/autoflow/payload/request/EdgePkgInfoRequest.java deleted file mode 100644 index bb6fc2f..0000000 --- a/src/main/java/kr/re/etri/autoflow/payload/request/EdgePkgInfoRequest.java +++ /dev/null @@ -1,51 +0,0 @@ -package kr.re.etri.autoflow.payload.request; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; - -import java.util.Date; -import java.util.List; - -@Getter -@Setter -@NoArgsConstructor -@AllArgsConstructor -@Schema(description = "Edge 패키지 등록 요청 객체") -public class EdgePkgInfoRequest { - - @Schema(description = "Edge 패키지 시리얼 번호", example = "1") - private int ed_pkg_serial; - - @Schema(description = "설치 위치", example = "/opt/edge/package") - private String download_location; - - @Schema(description = "패키지 ID", example = "pkg_001") - private String sw_id; - - @Schema(description = "소프트웨어 버전", example = "v1.0.0") - private String sw_version; - - @Schema(description = "소프트웨어 이름", example = "Edge Analytics") - private String sw_name; - - @Schema(description = "등록 사용자 ID", example = "admin") - private String user_id; - - @Schema(description = "인증 ID", example = "auth_12345") - private String auth_id; - - @Schema(description = "설치 OS", example = "Linux") - private String install_os; - - @Schema(description = "등록 일시", example = "2025-10-15T14:30:00") - private Date archive_type; - - @Schema(description = "실행 여부 (0/1)", example = "1") - private String exec_yn; - - @Schema(description = "등록인만 접근 여부 (0/1)", example = "1") - private String secret_at; -} \ No newline at end of file diff --git a/src/main/java/kr/re/etri/autoflow/payload/request/EdgeSWVO.java b/src/main/java/kr/re/etri/autoflow/payload/request/EdgeSWVO.java new file mode 100644 index 0000000..3d3bf67 --- /dev/null +++ b/src/main/java/kr/re/etri/autoflow/payload/request/EdgeSWVO.java @@ -0,0 +1,19 @@ +package kr.re.etri.autoflow.payload.request; + +import lombok.Data; +import org.springframework.web.multipart.MultipartFile; + +import java.io.Serializable; + +@Data +public class EdgeSWVO { + + private String sw_id; + private String sw_name; + private String sw_version; + private String creation_datetime; + private String auth_id; + + // 파일 업로드용 + private MultipartFile file; +} diff --git a/src/main/java/kr/re/etri/autoflow/service/ExternalAuthService.java b/src/main/java/kr/re/etri/autoflow/service/ExternalAuthService.java index d758042..e8201b4 100644 --- a/src/main/java/kr/re/etri/autoflow/service/ExternalAuthService.java +++ b/src/main/java/kr/re/etri/autoflow/service/ExternalAuthService.java @@ -2,13 +2,15 @@ package kr.re.etri.autoflow.service; import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.annotation.PostConstruct; -import kr.re.etri.autoflow.payload.request.EdgePkgInfoRequest; +import kr.re.etri.autoflow.payload.request.EdgeSWVO; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.io.ByteArrayResource; import org.springframework.http.*; import org.springframework.stereotype.Service; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; +import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.client.HttpServerErrorException; import org.springframework.web.client.RestTemplate; import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.web.multipart.MultipartFile; @@ -41,14 +43,22 @@ public class ExternalAuthService { this.restTemplate = createUnsafeRestTemplate(); } - /** SSL 무시용 RestTemplate 생성 */ + /** + * SSL 무시용 RestTemplate 생성 + */ private RestTemplate createUnsafeRestTemplate() { try { TrustManager[] trustAllCerts = new TrustManager[]{ new X509TrustManager() { - public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } - public void checkClientTrusted(X509Certificate[] certs, String authType) { } - public void checkServerTrusted(X509Certificate[] certs, String authType) { } + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } + + public void checkClientTrusted(X509Certificate[] certs, String authType) { + } + + public void checkServerTrusted(X509Certificate[] certs, String authType) { + } } }; @@ -66,7 +76,9 @@ public class ExternalAuthService { } } - /** Bearer 토큰 발급 */ + /** + * Bearer 토큰 발급 + */ public Map getUserInfo(String id, String password) { // 요청 본문 Map body = Map.of("id", id, "password", password); @@ -145,36 +157,60 @@ public class ExternalAuthService { } } - public Object uploadEdgePackage(String bearerToken, EdgePkgInfoRequest edgePkgInfoRequest, MultipartFile file) throws IOException { + public Map addEdgePackage(String token, EdgeSWVO edgeSWVO) throws IOException { + HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.MULTIPART_FORM_DATA); - headers.set("Authorization", bearerToken.startsWith("Bearer ") ? bearerToken : "Bearer " + bearerToken); - - // Object를 JSON 문자열로 변환 - ObjectMapper objectMapper = new ObjectMapper(); - String edgePkgInfoJson = objectMapper.writeValueAsString(edgePkgInfoRequest); + headers.setBearerAuth(token); MultiValueMap body = new LinkedMultiValueMap<>(); - body.add("edgePkgInfoVO", edgePkgInfoJson); - if (file != null && !file.isEmpty()) { - body.add("file", new ByteArrayResource(file.getBytes()) { + // 문자열 필드들 + body.add("sw_id", edgeSWVO.getSw_id()); + body.add("sw_name", edgeSWVO.getSw_name()); + body.add("sw_version", edgeSWVO.getSw_version()); + body.add("creation_datetime", edgeSWVO.getCreation_datetime()); + body.add("auth_id", edgeSWVO.getAuth_id()); + + // 파일 필드 + if (edgeSWVO.getFile() != null && !edgeSWVO.getFile().isEmpty()) { + MultipartFile file = edgeSWVO.getFile(); + + // Content-Type 지정 + 파일명 유지 + ByteArrayResource fileResource = new ByteArrayResource(file.getBytes()) { @Override public String getFilename() { return file.getOriginalFilename(); } - }); + + @Override + public long contentLength() { + return file.getSize(); + } + }; + + HttpHeaders fileHeaders = new HttpHeaders(); + fileHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM); + + HttpEntity fileEntity = new HttpEntity<>(fileResource, fileHeaders); + body.add("files", fileEntity); // ✅ 반드시 "files" } - HttpEntity> request = new HttpEntity<>(body, headers); + HttpEntity> requestEntity = new HttpEntity<>(body, headers); - ResponseEntity response = restTemplate.exchange( - edgeAddUrl, - HttpMethod.POST, - request, - Object.class - ); + try { + ResponseEntity response = restTemplate.postForEntity(edgeAddUrl, requestEntity, Map.class); - return response.getBody(); + if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) { + return response.getBody(); + } else { + throw new RuntimeException("외부 Edge 패키지 등록 실패 (응답 코드: " + response.getStatusCode() + ")"); + } + + } catch (HttpClientErrorException | HttpServerErrorException e) { + throw new RuntimeException( + e.getStatusCode().value() + " on POST request for \"" + edgeAddUrl + "\": \"" + e.getResponseBodyAsString() + "\"" + ); + } } }