diff --git a/WebContent/WEB-INF/classes/com/pms/mapper/admin.xml b/WebContent/WEB-INF/classes/com/pms/mapper/admin.xml index 1bf6bd3..b72f70f 100644 --- a/WebContent/WEB-INF/classes/com/pms/mapper/admin.xml +++ b/WebContent/WEB-INF/classes/com/pms/mapper/admin.xml @@ -801,6 +801,7 @@ SELECT T.* +
diff --git a/WebContent/WEB-INF/view/main/header.jsp b/WebContent/WEB-INF/view/main/header.jsp index 8a103af..207889d 100644 --- a/WebContent/WEB-INF/view/main/header.jsp +++ b/WebContent/WEB-INF/view/main/header.jsp @@ -7,6 +7,7 @@ PersonBean person = (PersonBean)session.getAttribute(Constants.PERSON_BEAN); String userId = CommonUtils.checkNull(person.getUserId()); String authName = CommonUtils.checkNull(person.getAuthName()); +String empseq = CommonUtils.checkNull(person.getEmpseq()); ArrayList userMenuList = new ArrayList(); userMenuList = (ArrayList)request.getAttribute("userMenuList"); %> @@ -333,16 +334,21 @@ function openAdminMngPop(){ } -//결재건수조회 +//결재건수조회 (Amaranth10만) function fn_setApprovalCnt(){ + // Amaranth10 결재 건수 조회 $.ajax({ - url:"/approval/getApprovalCnt.do", + url:"/approval/getAmaranthApprovalCnt.do", type:"POST", - data:{"userId":"${connectUserId}"}, + data:{"userId":"${connectUserId}", "empseq":"<%=empseq%>"}, dataType:"json", success:function(data){ - var cnt = fnc_checkNullDefaultValue(data.CNT, "0"); + var cnt = parseInt(fnc_checkNullDefaultValue(data.CNT, "0")); + + console.log("Amaranth 결재 건수: " + cnt + "건"); + $(".notice_no").text(cnt); + if(cnt > 0){ $("#blink").attr("class", "work_notice btnApprovalList blinkcss"); $(".blinkcss").children("span").children("img").attr({src:"/images/bell.gif"}); @@ -350,8 +356,12 @@ function fn_setApprovalCnt(){ $("#blink").attr("class", "work_notice btnApprovalList blink_none"); $(".blink_none").children("span").children("img").attr({src:"/images/bell.png"}); } - }, + }, error: function(jqxhr, status, error){ + console.log("Amaranth 결재 건수 조회 실패"); + $(".notice_no").text("0"); + $("#blink").attr("class", "work_notice btnApprovalList blink_none"); + $(".blink_none").children("span").children("img").attr({src:"/images/bell.png"}); } }); } diff --git a/WebContent/init.jsp b/WebContent/init.jsp index 5a52930..b31fdca 100644 --- a/WebContent/init.jsp +++ b/WebContent/init.jsp @@ -14,6 +14,7 @@ String connectUserDeptCode = ""; String connectUserName = ""; String connectUserDeptName = ""; String partnerCd = ""; +String empseq = ""; if(!isLoggedIn){ out.write(""); out.write(""); @@ -25,7 +26,7 @@ if(!isLoggedIn){ connectUserName = CommonUtils.checkNull(initPerson.getUserName()); connectUserDeptName = CommonUtils.checkNull(initPerson.getDeptName()); partnerCd = CommonUtils.checkNull(initPerson.getPartner_cd()); - + empseq = CommonUtils.checkNull(initPerson.getEmpseq()); if("plm_admin".equals(connectUserId)){ isAdmin = true; } diff --git a/WebContent/init_new.jsp b/WebContent/init_new.jsp index 6ff5a26..c303e2c 100644 --- a/WebContent/init_new.jsp +++ b/WebContent/init_new.jsp @@ -14,6 +14,7 @@ String connectUserDeptCode = ""; String connectUserName = ""; String connectUserDeptName = ""; String partnerCd = ""; +String empseq = ""; if(!isLoggedIn){ out.write(""); out.write(""); @@ -25,7 +26,7 @@ if(!isLoggedIn){ connectUserName = CommonUtils.checkNull(initPerson.getUserName()); connectUserDeptName = CommonUtils.checkNull(initPerson.getDeptName()); partnerCd = CommonUtils.checkNull(initPerson.getPartner_cd()); - + empseq = CommonUtils.checkNull(initPerson.getEmpseq()); if("plm_admin".equals(connectUserId)){ isAdmin = true; } diff --git a/WebContent/init_toastGrid.jsp b/WebContent/init_toastGrid.jsp index 924a339..4659712 100644 --- a/WebContent/init_toastGrid.jsp +++ b/WebContent/init_toastGrid.jsp @@ -10,6 +10,7 @@ String connectUserId = ""; String connectUserDeptCode = ""; String connectUserName = ""; String connectUserDeptName = ""; +String empseq = ""; if(!isLoggedIn){ out.write(""); out.write(""); @@ -20,7 +21,7 @@ if(!isLoggedIn){ connectUserDeptCode = CommonUtils.checkNull(initPerson.getDeptCode()); connectUserName = CommonUtils.checkNull(initPerson.getUserName()); connectUserDeptName = CommonUtils.checkNull(initPerson.getDeptName()); - + empseq = CommonUtils.checkNull(initPerson.getEmpseq()); if("plm_admin".equals(connectUserId)){ isAdmin = true; } diff --git a/src/com/pms/api/AmaranthApprovalApiClient.java b/src/com/pms/api/AmaranthApprovalApiClient.java new file mode 100644 index 0000000..f1d4097 --- /dev/null +++ b/src/com/pms/api/AmaranthApprovalApiClient.java @@ -0,0 +1,585 @@ +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 java.util.HashMap; +import java.util.Map; +import java.util.Random; +import javax.crypto.Cipher; +import javax.crypto.Mac; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +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 org.apache.commons.codec.binary.Base64; + +/** + * Amaranth10 전자결재 API 클라이언트 + * 결재함 조회 및 결재 건수 조회 + */ +public class AmaranthApprovalApiClient { + + // Amaranth10 서버 정보 + 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"; + private static final String AES_KEY = "gcmsAmaranth40578"; // SSO 암호화 키 + + /** + * 인증 토큰 발급 + * @param baseUrl API 서버의 기본 URL + * @param loginId 로그인 ID + * @return authToken, hashKey를 포함한 Map + */ + public Map getAuthToken(String baseUrl, String loginId) throws Exception { + // 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; + } + }); + + String urlPath = "/apiproxy/api99u01A01"; + 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(); + connection.setConnectTimeout(30000); + connection.setReadTimeout(30000); + connection.setInstanceFollowRedirects(false); + + try { + 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); + + // loginId 암호화 + String loginIdEnc = encryptLoginId(loginId); + + // 요청 본문 작성 + String requestBody = buildAuthRequestBody(loginIdEnc); + + 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(); + + // 응답 읽기 + 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("인증 토큰 발급 실패: HTTP " + responseCode); + } + } + + String line; + while ((line = reader.readLine()) != null) { + response.append(line); + } + } finally { + if (reader != null) { + reader.close(); + } + } + + if (responseCode >= 200 && responseCode < 300) { + return parseAuthResponse(response.toString()); + } else { + throw new Exception("인증 토큰 발급 실패: HTTP " + responseCode + " - " + response.toString()); + } + + } finally { + connection.disconnect(); + } + } + + /** + * 결재함 조회 (결재 건수 포함) + * @param baseUrl API 서버의 기본 URL + * @param empSeq 사용자 시퀀스 + * @param compSeq 회사 시퀀스 + * @param deptSeq 부서 시퀀스 + * @param authToken 인증 토큰 + * @param hashKey 해시 키 + * @return 결재함 목록 JSON 문자열 + */ + public String getApprovalBoxList(String baseUrl, String empSeq, String compSeq, String deptSeq, + String authToken, String hashKey) throws Exception { + // 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; + } + }); + + String urlPath = "/apiproxy/api99u02A01"; + 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(); + connection.setConnectTimeout(30000); + connection.setReadTimeout(30000); + connection.setInstanceFollowRedirects(false); + + try { + 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 " + authToken); + + String transactionId = generateTransactionId(); + connection.setRequestProperty("transaction-id", transactionId); + + String timestamp = String.valueOf(System.currentTimeMillis() / 1000); + connection.setRequestProperty("timestamp", timestamp); + + connection.setRequestProperty("groupSeq", GROUP_SEQ); + + // 사용자 인증용 wehago-sign 생성 (authToken과 hashKey 사용) + String wehagoSign = generateWehagoSign(authToken, transactionId, timestamp, urlPath, hashKey); + connection.setRequestProperty("wehago-sign", wehagoSign); + + // 요청 본문 작성 + String requestBody = buildApprovalBoxRequestBody(empSeq, compSeq, deptSeq); + + System.out.println("=== Amaranth 결재함 조회 요청 ==="); + System.out.println("URL: " + fullUrl); + System.out.println("Request Body: " + requestBody); + + 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(); + + // 응답 읽기 + 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("결재함 조회 실패: HTTP " + responseCode); + } + } + + String line; + while ((line = reader.readLine()) != null) { + response.append(line); + } + } finally { + if (reader != null) { + reader.close(); + } + } + + System.out.println("=== Amaranth 결재함 조회 응답 ==="); + System.out.println("Response Code: " + responseCode); + System.out.println("Response: " + response.toString()); + + if (responseCode >= 200 && responseCode < 300) { + return response.toString(); + } else { + throw new Exception("결재함 조회 실패: HTTP " + responseCode + " - " + response.toString()); + } + + } finally { + connection.disconnect(); + } + } + + /** + * 결재함 조회 - 서버 인증 방식 (인증 토큰 불필요) + * ACCESS_TOKEN + HASH_KEY를 사용하여 직접 결재함 조회 + * @param baseUrl API 서버의 기본 URL + * @param empSeq 사원 시퀀스 + * @param compSeq 회사 시퀀스 + * @return API 응답 결과 (JSON 문자열) + */ + public String getApprovalBoxListDirect(String baseUrl, String empSeq, String compSeq) throws Exception { + // 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; } + }); + + String urlPath = "/apiproxy/api99u02A01"; + 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(); + connection.setConnectTimeout(30000); + connection.setReadTimeout(30000); + connection.setInstanceFollowRedirects(false); + + try { + connection.setRequestMethod("POST"); + connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8"); + connection.setRequestProperty("Accept", "application/json"); + + // 서버 인증 헤더 설정 (ACCESS_TOKEN, HASH_KEY 사용) + 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); + + // 서버 인증 wehago-sign (ACCESS_TOKEN + HASH_KEY 사용) + String wehagoSign = generateWehagoSign(ACCESS_TOKEN, transactionId, timestamp, urlPath, HASH_KEY); + connection.setRequestProperty("wehago-sign", wehagoSign); + + // 요청 본문 작성 + String requestBody = "{" + + "\"header\":{" + + "\"empSeq\":\"" + escapeJson(empSeq) + "\"," + + "\"groupSeq\":\"" + escapeJson(GROUP_SEQ) + "\"" + + "}," + + "\"body\":{" + + "\"companyInfo\":{" + + "\"compSeq\":\"" + escapeJson(compSeq) + "\"" + + "}" + + "}" + + "}"; + + System.out.println("=== Amaranth 결재함 조회 (서버 인증) ==="); + System.out.println("URL: " + fullUrl); + System.out.println("empSeq: " + empSeq); + System.out.println("Request Body: " + requestBody); + + 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(); + + 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("결재함 조회 실패: HTTP " + responseCode); + } + } + + String line; + while ((line = reader.readLine()) != null) { + response.append(line); + } + } finally { + if (reader != null) reader.close(); + } + + System.out.println("Response Code: " + responseCode); + System.out.println("Response: " + response.toString()); + + if (responseCode >= 200 && responseCode < 300) { + return response.toString(); + } else { + throw new Exception("결재함 조회 실패: HTTP " + responseCode + " - " + response.toString()); + } + + } finally { + connection.disconnect(); + } + } + + /** + * loginId 암호화 (AES128 CBC PKCS5Padding) + */ + private String encryptLoginId(String loginId) throws Exception { + // 현재 날짜시간 (YYYYMMDDHHmmss) + String currentDateTime = new java.text.SimpleDateFormat("yyyyMMddHHmmss").format(new java.util.Date()); + + // 암호화할 평문: 현재날짜시간▦loginId + String plainText = currentDateTime + "▦" + loginId; + + // AES 키와 IV는 정확히 16바이트여야 함 + byte[] keyBytes = AES_KEY.getBytes(StandardCharsets.UTF_8); + byte[] key16Bytes = new byte[16]; + System.arraycopy(keyBytes, 0, key16Bytes, 0, Math.min(keyBytes.length, 16)); + + SecretKeySpec secretKey = new SecretKeySpec(key16Bytes, "AES"); + + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(key16Bytes)); + + byte[] encrypted = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8)); + return Base64.encodeBase64String(encrypted); + } + + /** + * wehago-sign 생성 (HmacSHA256) + */ + private String generateWehagoSign(String authToken, String transactionId, + String timestamp, String urlPath) throws Exception { + return generateWehagoSign(authToken, transactionId, timestamp, urlPath, HASH_KEY); + } + + /** + * wehago-sign 생성 (HmacSHA256) - hashKey 지정 + */ + private String generateWehagoSign(String authToken, String transactionId, + String timestamp, String urlPath, String hashKey) throws Exception { + try { + // value = authToken + transactionId + timestamp + urlPath + String value = authToken + transactionId + timestamp + urlPath; + + SecretKeySpec keySpec = new SecretKeySpec( + hashKey.getBytes(StandardCharsets.UTF_8), "HmacSHA256"); + Mac mac = Mac.getInstance("HmacSHA256"); + mac.init(keySpec); + byte[] encrypted = mac.doFinal(value.getBytes(StandardCharsets.UTF_8)); + + return Base64.encodeBase64String(encrypted); + + } catch (Exception e) { + System.err.println("Wehago-sign 생성 오류: " + e.getMessage()); + e.printStackTrace(); + throw e; + } + } + + /** + * 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(); + } + + /** + * 인증 토큰 발급 요청 Body 생성 + */ + private String buildAuthRequestBody(String loginIdEnc) { + StringBuilder json = new StringBuilder(); + json.append("{"); + json.append("\"header\":{},"); + json.append("\"body\":{"); + json.append("\"groupSeq\":\"").append(escapeJson(GROUP_SEQ)).append("\","); + json.append("\"loginIdEnc\":\"").append(escapeJson(loginIdEnc)).append("\""); + json.append("}"); + json.append("}"); + return json.toString(); + } + + /** + * 결재함 조회 요청 Body 생성 + */ + private String buildApprovalBoxRequestBody(String empSeq, String compSeq, String deptSeq) { + StringBuilder json = new StringBuilder(); + json.append("{"); + + // header 섹션 + json.append("\"header\":{"); + json.append("\"empSeq\":\"").append(escapeJson(empSeq)).append("\","); + json.append("\"groupSeq\":\"").append(escapeJson(GROUP_SEQ)).append("\""); + json.append("}"); + + // body 섹션 + json.append(",\"body\":{"); + json.append("\"companyInfo\":{"); + json.append("\"compSeq\":\"").append(escapeJson(compSeq)).append("\""); + if (deptSeq != null && !deptSeq.isEmpty()) { + json.append(",\"deptSeq\":\"").append(escapeJson(deptSeq)).append("\""); + } + json.append("}"); + json.append("}"); + + json.append("}"); + return json.toString(); + } + + /** + * 인증 응답 파싱 + */ + private Map parseAuthResponse(String jsonResponse) { + Map result = new HashMap(); + + try { + // resultCode 추출 + String resultCode = extractJsonValue(jsonResponse, "resultCode"); + + if ("0".equals(resultCode)) { + // authToken 추출 + String authToken = extractJsonValue(jsonResponse, "authToken"); + // hashKey 추출 + String hashKey = extractJsonValue(jsonResponse, "hashKey"); + + result.put("authToken", authToken); + result.put("hashKey", hashKey); + result.put("success", "true"); + } else { + result.put("success", "false"); + result.put("resultMsg", extractJsonValue(jsonResponse, "resultMsg")); + } + } catch (Exception e) { + e.printStackTrace(); + result.put("success", "false"); + result.put("error", e.getMessage()); + } + + return result; + } + + /** + * JSON에서 특정 키의 값 추출 (간단한 파싱) + */ + private String extractJsonValue(String json, String key) { + String searchKey = "\"" + key + "\""; + int startIndex = json.indexOf(searchKey); + if (startIndex == -1) { + return ""; + } + + startIndex = json.indexOf(":", startIndex) + 1; + int endIndex = json.indexOf(",", startIndex); + if (endIndex == -1) { + endIndex = json.indexOf("}", startIndex); + } + + String value = json.substring(startIndex, endIndex).trim(); + // 따옴표 제거 + value = value.replace("\"", "").trim(); + + return value; + } + + /** + * JSON 문자열 이스케이프 처리 + */ + private String escapeJson(String value) { + if (value == null) { + return ""; + } + return value.replace("\\", "\\\\") + .replace("\"", "\\\"") + .replace("\n", "\\n") + .replace("\r", "\\r") + .replace("\t", "\\t"); + } +} diff --git a/src/com/pms/api/AmaranthUserApiClient.java b/src/com/pms/api/AmaranthUserApiClient.java new file mode 100644 index 0000000..4268017 --- /dev/null +++ b/src/com/pms/api/AmaranthUserApiClient.java @@ -0,0 +1,324 @@ +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; + +/** + * Amaranth10 사용자 정보 조회 API 클라이언트 + * empSeq 기준으로 사용자 정보(loginId 포함)를 조회합니다. + */ +public class AmaranthUserApiClient { + + private static final String API_URL = "~/apiproxy/api99u01A11"; + 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"; + + /** + * empSeq 배열로 사용자 정보를 조회합니다. + * + * @param baseUrl API 서버의 기본 URL (예: https://erp.rps-korea.com) + * @param empSeqArray empSeq 배열 (예: ["100001337135", "100001337136"]) + * @return API 응답 결과 (JSON 문자열) + * @throws Exception API 호출 중 발생하는 예외 + */ + public String getUserInfoByEmpSeqArray(String baseUrl, String[] empSeqArray) throws Exception { + if (empSeqArray == null || empSeqArray.length == 0) { + throw new IllegalArgumentException("empSeq 배열은 필수입니다."); + } + + // 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); + + // 요청 바디 작성 (arrEmpSeq 배열 사용) + StringBuilder empSeqArrayJson = new StringBuilder("["); + for (int i = 0; i < empSeqArray.length; i++) { + if (i > 0) { + empSeqArrayJson.append(","); + } + empSeqArrayJson.append("\"").append(empSeqArray[i]).append("\""); + } + empSeqArrayJson.append("]"); + + String requestBody = "{" + + "\"header\":{" + + "\"groupSeq\":\"" + GROUP_SEQ + "\"," + + "\"tId\":\"\"," + + "\"pId\":\"\"" + + "}," + + "\"body\":{" + + "\"langCode\":\"kr\"," + + "\"arrEmpSeq\":" + empSeqArrayJson.toString() + + "}" + + "}"; + + System.out.println("[Amaranth User API] 요청 URL: " + fullUrl); + System.out.println("[Amaranth User API] 요청 Body: " + requestBody); + + // 요청 전송 + connection.setDoOutput(true); + OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream(), StandardCharsets.UTF_8); + writer.write(requestBody); + writer.flush(); + writer.close(); + + // 응답 읽기 + int responseCode = connection.getResponseCode(); + System.out.println("[Amaranth User API] 응답 코드: " + responseCode); + + BufferedReader reader; + if (responseCode >= 200 && responseCode < 300) { + reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8)); + } else { + reader = new BufferedReader(new InputStreamReader(connection.getErrorStream(), StandardCharsets.UTF_8)); + } + + StringBuilder response = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + response.append(line); + } + reader.close(); + + String result = response.toString(); + System.out.println("[Amaranth User API] 응답: " + result); + + if (responseCode >= 200 && responseCode < 300) { + return result; + } else { + throw new Exception("API 호출 실패 (HTTP " + responseCode + "): " + result); + } + + } finally { + connection.disconnect(); + } + } + + /** + * 단건 empSeq로 사용자 정보를 조회합니다. + * + * @param baseUrl API 서버의 기본 URL + * @param empSeq 사원 시퀀스 + * @return API 응답 결과 (JSON 문자열) + * @throws Exception API 호출 중 발생하는 예외 + */ + public String getUserInfoByEmpSeq(String baseUrl, String empSeq) throws Exception { + return getUserInfoByEmpSeqArray(baseUrl, new String[]{empSeq}); + } + + /** + * 전체 사용자 정보를 조회합니다 (empSeq, loginId 포함) + * + * @param baseUrl API 서버의 기본 URL + * @return API 응답 결과 (JSON 문자열) + * @throws Exception API 호출 중 발생하는 예외 + */ + public String getAllUserInfo(String baseUrl) throws Exception { + // 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); + + // 요청 바디 작성 (전체 조회 - empSeq 없이) + String requestBody = "{" + + "\"header\":{" + + "\"groupSeq\":\"" + GROUP_SEQ + "\"," + + "\"tId\":\"\"," + + "\"pId\":\"\"" + + "}," + + "\"body\":{" + + "\"langCode\":\"kr\"" + + "}" + + "}"; + + System.out.println("[Amaranth User API - 전체 조회] 요청 URL: " + fullUrl); + System.out.println("[Amaranth User API - 전체 조회] 요청 Body: " + requestBody); + + // 요청 전송 + connection.setDoOutput(true); + OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream(), StandardCharsets.UTF_8); + writer.write(requestBody); + writer.flush(); + writer.close(); + + // 응답 읽기 + int responseCode = connection.getResponseCode(); + System.out.println("[Amaranth User API - 전체 조회] 응답 코드: " + responseCode); + + BufferedReader reader; + if (responseCode >= 200 && responseCode < 300) { + reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8)); + } else { + reader = new BufferedReader(new InputStreamReader(connection.getErrorStream(), StandardCharsets.UTF_8)); + } + + StringBuilder response = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + response.append(line); + } + reader.close(); + + String result = response.toString(); + System.out.println("[Amaranth User API - 전체 조회] 응답 길이: " + result.length() + " bytes"); + + if (responseCode >= 200 && responseCode < 300) { + return result; + } else { + throw new Exception("API 호출 실패 (HTTP " + responseCode + "): " + result); + } + + } finally { + connection.disconnect(); + } + } + + /** + * 30자리 랜덤 transaction-id 생성 + */ + private String generateTransactionId() { + String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + Random random = new Random(); + StringBuilder sb = new StringBuilder(30); + for (int i = 0; i < 30; i++) { + sb.append(chars.charAt(random.nextInt(chars.length()))); + } + return sb.toString(); + } + + /** + * wehago-sign 생성 (HmacSHA256) + */ + private String generateWehagoSign(String accessToken, String transactionId, String timestamp, String url) throws Exception { + String message = accessToken + transactionId + timestamp + url; + + Mac sha256Hmac = Mac.getInstance("HmacSHA256"); + SecretKeySpec secretKey = new SecretKeySpec(HASH_KEY.getBytes(StandardCharsets.UTF_8), "HmacSHA256"); + sha256Hmac.init(secretKey); + + byte[] hash = sha256Hmac.doFinal(message.getBytes(StandardCharsets.UTF_8)); + return Base64.encodeBase64String(hash); + } +} diff --git a/src/com/pms/common/bean/PersonBean.java b/src/com/pms/common/bean/PersonBean.java index e072f32..59b4f93 100644 --- a/src/com/pms/common/bean/PersonBean.java +++ b/src/com/pms/common/bean/PersonBean.java @@ -21,8 +21,17 @@ public class PersonBean{ private String userTypeName = ""; private String authName = ""; private String partner_cd = ""; + private String empseq = ""; + + public String getEmpseq() { + return empseq; + } + public void setEmpseq(String empseq) { + this.empseq = empseq; + } + public String getPartner_cd() { return partner_cd; } diff --git a/src/com/pms/common/utils/SessionManager.java b/src/com/pms/common/utils/SessionManager.java index 4553806..dc061bf 100644 --- a/src/com/pms/common/utils/SessionManager.java +++ b/src/com/pms/common/utils/SessionManager.java @@ -46,7 +46,7 @@ public class SessionManager { person.setUserTypeName(CommonUtils.checkNull(userInfo.get("user_type_name"))); person.setAuthName(CommonUtils.checkNull(userInfo.get("auth_name"))); person.setPartner_cd(CommonUtils.checkNull(userInfo.get("partner_objid"))); - + person.setEmpseq(CommonUtils.checkNull(userInfo.get("empseq"))); if(Constants.SUPER_ADMIN.equals(CommonUtils.checkNull(userInfo.get("user_id")))){ diff --git a/src/com/pms/controller/AdminController.java b/src/com/pms/controller/AdminController.java index f923059..2266eea 100644 --- a/src/com/pms/controller/AdminController.java +++ b/src/com/pms/controller/AdminController.java @@ -5229,6 +5229,33 @@ public String clientImportFileProc(HttpServletRequest request, HttpSession sessi return resultMap; } + /** + * LoginId 업데이트 배치 수동 실행 (Amaranth10 인증용) + * @param request + * @param paramMap + * @return + */ + @RequestMapping("/admin/updateLoginIdManual.do") + @ResponseBody + public Map updateLoginIdManual(HttpServletRequest request, @RequestParam Map paramMap) { + Map resultMap = new HashMap(); + + try { + System.out.println("===================================="); + System.out.println("관리자 수동 LoginId 업데이트 배치 실행 요청"); + System.out.println("===================================="); + + resultMap = batchService.updateLoginIdManual(); + + } catch (Exception e) { + e.printStackTrace(); + resultMap.put("success", false); + resultMap.put("message", "LoginId 업데이트 실행 중 오류가 발생했습니다: " + e.getMessage()); + } + + return resultMap; + } + /** * ERP 부서 정보 동기화 수동 실행 * @param request diff --git a/src/com/pms/controller/ApprovalController.java b/src/com/pms/controller/ApprovalController.java index ce7d319..a6fe717 100644 --- a/src/com/pms/controller/ApprovalController.java +++ b/src/com/pms/controller/ApprovalController.java @@ -267,6 +267,19 @@ public class ApprovalController { return "/ajax/ajaxResult"; } + /** + * Amaranth10 전자결재 건수 조회 + * @param request + * @param paramMap + * @return + */ + @RequestMapping("/approval/getAmaranthApprovalCnt.do") + public String getAmaranthApprovalCnt(HttpServletRequest request, @RequestParam Map paramMap){ + Map map = approvalService.getAmaranthApprovalCnt(request, paramMap); + request.setAttribute("RESULT", CommonUtils.getJsonMap(map)); + return "/ajax/ajaxResult"; + } + /** * 결재완료 여부 확인 (견적서 메일 발송용) * @param request diff --git a/src/com/pms/mapper/admin.xml b/src/com/pms/mapper/admin.xml index 1bf6bd3..b72f70f 100644 --- a/src/com/pms/mapper/admin.xml +++ b/src/com/pms/mapper/admin.xml @@ -801,6 +801,7 @@ SELECT T.*