package com.pms.api; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.HttpURLConnection; import java.net.URL; import java.nio.charset.StandardCharsets; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import java.security.cert.X509Certificate; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import org.apache.commons.codec.binary.Base64; import java.util.Random; /** * 부서 정보 조회 API 클라이언트 * 회사의 부서정보를 조회하는 API를 호출합니다. */ public class DepartmentApiClient { private static final String API_URL = "~/apiproxy/api16S10"; private static final String CALLER_NAME = "API_gcmsAmaranth40578"; private static final String ACCESS_TOKEN = "MN5KzKBWRAa92BPxDlRLl3GcsxeZXc"; private static final String HASH_KEY = "22519103205540290721741689643674301018832465"; private static final String GROUP_SEQ = "gcmsAmaranth40578"; /** * 부서 정보를 조회합니다. * * @param baseUrl API 서버의 기본 URL (예: https://erp.rps-korea.com) * @param coCd 회사코드 (4자리, 필수) * @param searchText 검색명 (부서코드 또는 부서명, 선택사항, 미입력 시 전체 조회) * @return API 응답 결과 (JSON 문자열) * @throws Exception API 호출 중 발생하는 예외 */ public String getDepartmentList(String baseUrl, String coCd, String searchText) throws Exception { if (coCd == null || coCd.trim().isEmpty()) { throw new IllegalArgumentException("회사코드(coCd)는 필수입니다."); } // JDK 1.7에서 TLS 1.2 활성화 System.setProperty("https.protocols", "TLSv1.2"); // SSL 인증서 검증 우회 (개발 환경용) TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() { public X509Certificate[] getAcceptedIssuers() { return null; } public void checkClientTrusted(X509Certificate[] certs, String authType) { } public void checkServerTrusted(X509Certificate[] certs, String authType) { } } }; SSLContext sc = SSLContext.getInstance("TLS"); sc.init(null, trustAllCerts, new java.security.SecureRandom()); HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); HttpsURLConnection.setDefaultHostnameVerifier(new javax.net.ssl.HostnameVerifier() { public boolean verify(String hostname, javax.net.ssl.SSLSession session) { return true; } }); // API URL 구성 String urlPath = API_URL.replace("~", ""); String cleanBaseUrl = baseUrl.endsWith("/") ? baseUrl.substring(0, baseUrl.length() - 1) : baseUrl; String fullUrl = cleanBaseUrl + urlPath; URL url = new URL(fullUrl); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); // 연결 타임아웃 설정 (30초) connection.setConnectTimeout(30000); connection.setReadTimeout(30000); connection.setInstanceFollowRedirects(false); try { // HTTP 메서드 설정 connection.setRequestMethod("POST"); connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8"); connection.setRequestProperty("Accept", "application/json"); // 인증 헤더 설정 connection.setRequestProperty("callerName", CALLER_NAME); connection.setRequestProperty("Authorization", "Bearer " + ACCESS_TOKEN); String transactionId = generateTransactionId(); connection.setRequestProperty("transaction-id", transactionId); String timestamp = String.valueOf(System.currentTimeMillis() / 1000); connection.setRequestProperty("timestamp", timestamp); connection.setRequestProperty("groupSeq", GROUP_SEQ); String wehagoSign = generateWehagoSign(ACCESS_TOKEN, transactionId, timestamp, urlPath); connection.setRequestProperty("wehago-sign", wehagoSign); // 요청 본문 작성 String requestBody = buildRequestBody(coCd, searchText); // 요청 전송 설정 connection.setDoOutput(true); connection.setDoInput(true); // 요청 본문 전송 OutputStreamWriter writer = new OutputStreamWriter( connection.getOutputStream(), StandardCharsets.UTF_8); writer.write(requestBody); writer.flush(); writer.close(); // 응답 코드 확인 int responseCode = connection.getResponseCode(); // 리다이렉트 처리 if (responseCode == 301 || responseCode == 302 || responseCode == 303 || responseCode == 307 || responseCode == 308) { String location = connection.getHeaderField("Location"); connection.disconnect(); if (location != null) { URL redirectUrl = new URL(location); connection = (HttpURLConnection) redirectUrl.openConnection(); connection.setConnectTimeout(30000); connection.setReadTimeout(30000); connection.setInstanceFollowRedirects(false); connection.setRequestMethod("POST"); connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8"); connection.setRequestProperty("Accept", "application/json"); connection.setRequestProperty("callerName", CALLER_NAME); connection.setRequestProperty("Authorization", "Bearer " + ACCESS_TOKEN); connection.setRequestProperty("transaction-id", transactionId); connection.setRequestProperty("timestamp", timestamp); connection.setRequestProperty("groupSeq", GROUP_SEQ); connection.setRequestProperty("wehago-sign", wehagoSign); connection.setDoOutput(true); connection.setDoInput(true); OutputStreamWriter redirectWriter = new OutputStreamWriter( connection.getOutputStream(), StandardCharsets.UTF_8); redirectWriter.write(requestBody); redirectWriter.flush(); redirectWriter.close(); responseCode = connection.getResponseCode(); } } // 응답 읽기 BufferedReader reader = null; StringBuilder response = new StringBuilder(); try { if (responseCode >= 200 && responseCode < 300) { reader = new BufferedReader(new InputStreamReader( connection.getInputStream(), StandardCharsets.UTF_8)); } else { java.io.InputStream errorStream = connection.getErrorStream(); if (errorStream != null) { reader = new BufferedReader(new InputStreamReader( errorStream, StandardCharsets.UTF_8)); } else { throw new Exception("API 호출 실패: HTTP " + responseCode + " (에러 응답 본문 없음)"); } } String line; while ((line = reader.readLine()) != null) { response.append(line); } } finally { if (reader != null) { reader.close(); } } if (responseCode >= 200 && responseCode < 300) { return response.toString(); } else { throw new Exception("API 호출 실패: HTTP " + responseCode + " - " + response.toString()); } } finally { connection.disconnect(); } } /** * 요청 본문 JSON을 생성합니다. */ private String buildRequestBody(String coCd, String searchText) { StringBuilder json = new StringBuilder(); json.append("{"); json.append("\"coCd\":\"").append(escapeJson(coCd)).append("\""); if (searchText != null && !searchText.trim().isEmpty()) { json.append(",\"searchText\":\"").append(escapeJson(searchText)).append("\""); } json.append("}"); return json.toString(); } /** * JSON 문자열 이스케이프 처리 */ private String escapeJson(String value) { if (value == null) { return ""; } return value.replace("\\", "\\\\") .replace("\"", "\\\"") .replace("\n", "\\n") .replace("\r", "\\r") .replace("\t", "\\t"); } /** * 32자리 랜덤 transaction-id 생성 */ private String generateTransactionId() { String chars = "0123456789abcdef"; Random random = new Random(); StringBuilder sb = new StringBuilder(32); for (int i = 0; i < 32; i++) { sb.append(chars.charAt(random.nextInt(chars.length()))); } return sb.toString(); } /** * Wehago-sign 생성 */ private String generateWehagoSign(String accessToken, String transactionId, String timestamp, String urlPath) throws Exception { try { String value = accessToken + transactionId + timestamp + urlPath; SecretKeySpec keySpec = new SecretKeySpec( HASH_KEY.getBytes(StandardCharsets.UTF_8), "HmacSHA256"); Mac mac = Mac.getInstance("HmacSHA256"); mac.init(keySpec); byte[] encrypted = mac.doFinal(value.getBytes(StandardCharsets.UTF_8)); String base64Binary = Base64.encodeBase64String(encrypted); return base64Binary; } catch (Exception e) { System.err.println("Wehago-sign 생성 오류: " + e.getMessage()); e.printStackTrace(); throw e; } } }