From 60e6b30c9b060b293a3ef9a64677e16dd03443bc Mon Sep 17 00:00:00 2001 From: curry772 Date: Tue, 2 Jun 2026 18:06:19 +0900 Subject: [PATCH] =?UTF-8?q?api=20id,=20eai=20svc=20cd=20=EC=B6=94=EC=B6=9C?= =?UTF-8?q?=20=EB=B6=80=EB=B6=84=20ApiKeyExtractFilter=EB=A1=9C=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/eactive/eai/MessageKeyTest.java | 380 ------------- .../controller/ApiRequestBodyFilter.java | 34 +- .../controller/DJErpApiAdapterController.java | 111 +--- .../service/DJErpApiAdapterService.java | 514 ++---------------- 4 files changed, 81 insertions(+), 958 deletions(-) delete mode 100644 src/main/java/com/eactive/eai/MessageKeyTest.java diff --git a/src/main/java/com/eactive/eai/MessageKeyTest.java b/src/main/java/com/eactive/eai/MessageKeyTest.java deleted file mode 100644 index c7f2a12..0000000 --- a/src/main/java/com/eactive/eai/MessageKeyTest.java +++ /dev/null @@ -1,380 +0,0 @@ -package com.eactive.eai; - -import com.eactive.eai.common.EAITable; -import com.eactive.eai.common.util.MessageUtil; -import org.json.simple.JSONValue; - -/** - * 테스트용 메시지 키 검증 클래스 - */ -public class MessageKeyTest { - - - /** - * XML 또는 JSON 메시지의 성공 여부를 검증하는 테스트용 메소드 - * 추출된 값을 배열로 반환 - */ - public static TestResult isSuccessMessageTest(String messageType, Object message, String path, String expectedValue) { - try { - String extractedValue = ""; - - if(EAITable.XML_TYPE.equals(messageType)) { - if(message instanceof byte[]) { - extractedValue = extractXmlValue(new String((byte[])message), path); - } else if(message instanceof String) { - extractedValue = extractXmlValue((String)message, path); - } - } - else if(EAITable.JSON_TYPE.equals(messageType)) { - Object json = JSONValue.parse((String)message); - extractedValue = extractJsonValue(json, path); - } - - if(extractedValue == null) extractedValue = ""; - boolean result = compareMessageValue(extractedValue, expectedValue); - - return new TestResult(result, extractedValue); - - } catch(Exception e) { - e.printStackTrace(); - return new TestResult(false, "오류 발생: " + e.getMessage()); - } - } - - // 테스트 결과를 담는 클래스 - static class TestResult { - boolean success; - String extractedValue; - - TestResult(boolean success, String extractedValue) { - this.success = success; - this.extractedValue = extractedValue; - } - } - - /** - * 메시지 값 비교를 수행하는 메소드 - */ - private static boolean compareMessageValue(String actualValue, String expectedValue) { - if(actualValue == null) actualValue = ""; - if(expectedValue == null) return false; - - // 예약어로 시작하는 경우 - if(expectedValue.startsWith("@")) { - return compareWithReservedWord(actualValue, expectedValue); - } - - // 일반 equals 비교 - return actualValue.equals(expectedValue); - } - - /** - * 예약어를 포함한 값 비교 처리 - */ - private static boolean compareWithReservedWord(String actualValue, String definition) { - if (actualValue == null) actualValue = ""; - if (definition == null) return false; - - try { - // 1. 기본 비교 - if (definition.startsWith("@eq:")) { - return actualValue.equals(definition.substring(4)); - } - if (definition.startsWith("@ne:")) { - return !actualValue.equals(definition.substring(4)); - } - if ("@null".equals(definition)) { - return actualValue.isEmpty(); - } - if ("@!null".equals(definition)) { - return !actualValue.isEmpty(); - } - - // 2. 문자열 패턴 - if (definition.startsWith("@contains:")) { - String[] values = definition.substring(10).split(","); - for (String value : values) { - if (actualValue.equals(value.trim())) { - return true; - } - } - return false; - } - if (definition.startsWith("@!contains:")) { - String[] values = definition.substring(11).split(","); - for (String value : values) { - if (actualValue.equals(value.trim())) { - return false; - } - } - return true; - } - if (definition.startsWith("@starts:")) { - return actualValue.startsWith(definition.substring(8)); - } - if (definition.startsWith("@ends:")) { - return actualValue.endsWith(definition.substring(6)); - } - - // 3. 숫자 비교 - if (definition.startsWith("@gt:") || - definition.startsWith("@lt:") || - definition.startsWith("@ge:") || - definition.startsWith("@le:")) { - - double actualNum = Double.parseDouble(actualValue); - if (definition.startsWith("@gt:")) { - double defNum = Double.parseDouble(definition.substring(4)); - return actualNum > defNum; - } - if (definition.startsWith("@lt:")) { - double defNum = Double.parseDouble(definition.substring(4)); - return actualNum < defNum; - } - if (definition.startsWith("@ge:")) { - double defNum = Double.parseDouble(definition.substring(4)); - return actualNum >= defNum; - } - if (definition.startsWith("@le:")) { - double defNum = Double.parseDouble(definition.substring(4)); - return actualNum <= defNum; - } - } - - return false; - } catch (Exception e) { - return false; - } - } - - /** - * XML 값 추출 메소드 - */ - private static String extractXmlValue(String xmlMessage, String path) { - if(path == null) return ""; - - // XPath 형식인 경우 - if(path.startsWith("//") || path.startsWith("/")) { - return MessageUtil.getXmlFldByXPath(xmlMessage, path); - } - - // 단순 태그명인 경우 - return MessageUtil.getXmlFld(xmlMessage, path); - } - - /** - * JSON 값 추출 메소드 - */ - private static String extractJsonValue(Object json, String path) { - if(path == null) return ""; - - // JSONPath 형식인 경우 - if(path.startsWith("$.")) { - return MessageUtil.getJSONValueByPath(json, path); - } - - // 단순 키인 경우 - return MessageUtil.getJSONValue(json, path); - } - - private static void testXml() { - // System.out.println("\n=== XML 테스트 ==="); - - // 단순 경로 테스트 - String xml1 = "SUCCESS"; - test("XML 단순 경로", - EAITable.XML_TYPE, - xml1, - "status", - "SUCCESS"); - - // 중첩 경로 테스트 (일반 방식) - String xml2 = "
0000
"; - test("XML 중첩 경로 (일반)", - EAITable.XML_TYPE, - xml2, - "resultCode", - "0000"); - - // 중첩 경로 테스트 (XPath 방식) - test("XML 중첩 경로 (XPath)", - EAITable.XML_TYPE, - xml2, - "//response/header/resultCode", - "0000"); - - // 네임스페이스가 있는 XML - String xml3 = "OK"; - test("XML 네임스페이스", - EAITable.XML_TYPE, - xml3, - "//ns:status", - "OK"); - - // 실패 케이스 - test("XML 실패 케이스", - EAITable.XML_TYPE, - xml1, - "status", - "FAIL"); - - // 존재하지 않는 경로 - test("XML 없는 경로", - EAITable.XML_TYPE, - xml1, - "nonexistent", - "SUCCESS"); - } - - private static void testJson() { - // System.out.println("\n=== JSON 테스트 ==="); - - // 단순 경로 테스트 - String json1 = "{\"status\":\"success\"}"; - test("JSON 단순 경로", - EAITable.JSON_TYPE, - json1, - "status", - "success"); - - // 중첩 경로 테스트 (일반 방식) - String json2 = "{\"response\":{\"header\":{\"resultCode\":\"0000\"}}}"; - test("JSON 중첩 경로 (일반)", - EAITable.JSON_TYPE, - json2, - "resultCode", - "0000"); - - // 중첩 경로 테스트 (JSONPath 방식) - test("JSON 중첩 경로 (JSONPath)", - EAITable.JSON_TYPE, - json2, - "$.response.header.resultCode", - "0000"); - - // 배열이 포함된 JSON - String json3 = "{\"responses\":[{\"status\":\"success\"},{\"status\":\"pending\"}]}"; - test("JSON 배열 경로", - EAITable.JSON_TYPE, - json3, - "$.responses[0].status", - "success"); - - // 실패 케이스 - test("JSON 실패 케이스", - EAITable.JSON_TYPE, - json1, - "status", - "failure"); - - // 존재하지 않는 경로 - test("JSON 없는 경로", - EAITable.JSON_TYPE, - json1, - "nonexistent", - "success"); - } - - public static void main(String[] args) { - testXml(); - testJson(); - testReservedWords(); - } - - private static void testReservedWords() { - // System.out.println("\n=== 예약어 테스트 ==="); - - // 1. 기본 비교 테스트 - test("예약어 - eq", - EAITable.XML_TYPE, - "SUCCESS", - "code", - "@eq:SUCCESS"); - - test("예약어 - ne", - EAITable.XML_TYPE, - "SUCCESS", - "code", - "@ne:FAIL"); - - test("예약어 - null", - EAITable.XML_TYPE, - "", - "code", - "@null"); - - test("예약어 - !null", - EAITable.XML_TYPE, - "SUCCESS", - "code", - "@!null"); - - // 2. 문자열 패턴 테스트 - test("예약어 - contains", - EAITable.JSON_TYPE, - "{\"status\":\"success\"}", - "status", - "@contains:success,ok,done"); - - test("예약어 - !contains", - EAITable.JSON_TYPE, - "{\"status\":\"success\"}", - "status", - "@!contains:fail,error,invalid"); - - test("예약어 - starts", - EAITable.JSON_TYPE, - "{\"code\":\"SUCCESS_001\"}", - "code", - "@starts:SUCCESS"); - - test("예약어 - ends", - EAITable.JSON_TYPE, - "{\"code\":\"ERR_001\"}", - "code", - "@ends:001"); - - // 3. 숫자 비교 테스트 - test("예약어 - gt", - EAITable.JSON_TYPE, - "{\"value\":\"100\"}", - "value", - "@gt:50"); - - test("예약어 - lt", - EAITable.JSON_TYPE, - "{\"value\":\"100\"}", - "value", - "@lt:150"); - - test("예약어 - ge", - EAITable.JSON_TYPE, - "{\"value\":\"100\"}", - "value", - "@ge:100"); - - test("예약어 - le", - EAITable.JSON_TYPE, - "{\"value\":\"100\"}", - "value", - "@le:100"); - - // 4. 실패 케이스 - test("예약어 - 잘못된 숫자", - EAITable.JSON_TYPE, - "{\"value\":\"invalid\"}", - "value", - "@gt:50"); - - test("예약어 - 잘못된 패턴", - EAITable.JSON_TYPE, - "{\"status\":\"success\"}", - "status", - "@invalid:pattern"); - } - - private static void test(String testName, String type, String message, String path, String expected) { - TestResult result = isSuccessMessageTest(type, message, path, expected); - // System.out.println(String.format( "테스트: %s\n결과: %s\n메시지: %s\n경로: %s\n기대값: %s\n추출된 값: [%s]\n",testName, result.success ? "성공" : "실패",message,path,expected,result.extractedValue )); - } -} \ No newline at end of file diff --git a/src/main/java/com/eactive/eai/adapter/controller/ApiRequestBodyFilter.java b/src/main/java/com/eactive/eai/adapter/controller/ApiRequestBodyFilter.java index d557e84..ab9d793 100644 --- a/src/main/java/com/eactive/eai/adapter/controller/ApiRequestBodyFilter.java +++ b/src/main/java/com/eactive/eai/adapter/controller/ApiRequestBodyFilter.java @@ -21,6 +21,9 @@ import com.eactive.eai.adapter.http.dynamic.HttpDynamicInAdapterManager; import com.eactive.eai.adapter.http.dynamic.HttpDynamicInAdapterUri; import com.eactive.eai.common.stdmessage.STDMessageManager; import com.eactive.eai.common.stdmessage.STDMsgInfoAddOnVO; +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; public class ApiRequestBodyFilter extends OncePerRequestFilter { @@ -32,17 +35,12 @@ public class ApiRequestBodyFilter extends OncePerRequestFilter { HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { - + System.out.println("ApiRequestBodyFilter : " + request.getRequestURI()); if (!isTarget(request)) { filterChain.doFilter(request, response); return; } - - String encoding = getInboundGroupAdapterEncoding(request); - - - //원본 바디를 byte[]로 읽기 (개행 포함 그대로) 왜? 뒤에 필터에서 HMAC값 계산할수도 있어서 변조되면 안됨. 개행문자도 그대로 가야함. byte[] rawBody = readBodyAsBytes(request); @@ -54,14 +52,16 @@ public class ApiRequestBodyFilter extends OncePerRequestFilter { byte[] finalBody = rawBody; try { + if(isJsonArray(rawBody)) { + String encoding = getInboundGroupAdapterEncoding(request); + String bodyStr = new String(rawBody, encoding); + Object json = JSONValue.parse(bodyStr); - String bodyStr = new String(rawBody, encoding); - Object json = JSONValue.parse(bodyStr); - - if (json instanceof JSONArray) { - bodyStr = DJB_ROOTLESS_ARRAY + bodyStr + "}"; - finalBody = bodyStr.getBytes(encoding); - } + if (json instanceof JSONArray) { + bodyStr = DJB_ROOTLESS_ARRAY + bodyStr + "}"; + finalBody = bodyStr.getBytes(encoding); + } + } } catch (Exception e) { // JSON 파싱 실패 시 원본 유지 @@ -74,6 +74,14 @@ public class ApiRequestBodyFilter extends OncePerRequestFilter { filterChain.doFilter(wrapped, response); } + + public static boolean isJsonArray(byte[] json) { + try (JsonParser parser = new JsonFactory().createParser(json)) { + return parser.nextToken() == JsonToken.START_ARRAY; + } catch (Exception e) { + return false; + } + } private boolean isTarget(HttpServletRequest request) { String uri = request.getRequestURI(); diff --git a/src/main/java/com/eactive/eai/adapter/controller/DJErpApiAdapterController.java b/src/main/java/com/eactive/eai/adapter/controller/DJErpApiAdapterController.java index a8cb361..c5a464a 100644 --- a/src/main/java/com/eactive/eai/adapter/controller/DJErpApiAdapterController.java +++ b/src/main/java/com/eactive/eai/adapter/controller/DJErpApiAdapterController.java @@ -112,105 +112,35 @@ public class DJErpApiAdapterController implements HttpAdapterServiceKey { MediaType mediaType = MediaType.valueOf("application/json;charset=" + encode); String errorResponseFormat = httpProp.getProperty(ERROR_RESPONSE_FORMAT); - String responseType = httpProp.getProperty(RESPONSE_TYPE, "SYNC"); - ResponseEntity responseEntity = null; + ResponseEntity responseEntity = null; Properties transactionProp = new Properties(); String uuid = UUIDGenerator.getUUID().toString().replaceAll("-", ""); transactionProp.setProperty(TransactionContextKeys.TRANSACTION_UUID, uuid); MDC.put(Logger.DISCRIMINATOR, transactionProp.getProperty(TransactionContextKeys.TRANSACTION_UUID)); + // jwhong, put api received time, eaiSvcCode transactionProp.put(INBOUND_REQUESTED_TIME, receivedTimeStr); - - // ** jwhong TSEAIHS04의 api full path와 비교하여 adapter를 가져온다 -// String bzwkSvcKeyName = ""; // Adapter Group별 Action Class에 따라 구성이 달라짐. 예) -// // _AGW_IN_RST_SyS:POST/account/{acc_no} -// String adapterGrpName = ""; // Adapter Group명 예) _AGW_IN_RST_SyS : API_PATH(/api/test) - String apiSvcCode = ""; // eaiSvcCd 예) LONNCHCON00005S2 - String apiFullPathKey = ""; // 예) POST|/api/test/account/list, POST|/api/test/account/{acc_no} - Map pathVariables = null; - try { - // PathVariable(ex:/api/test/account/{acc_no}) 대응 및 DAO조회 제거. - // /api/test/account/1234567 호출시 /api/test/account/{acc_no} API 로 식별. - -// String methodAndUri = getRequestRuledPath(servletRequest, apiUri, adapterVO, transactionProp); - String methodAndUri = servletRequest.getMethod() + "|" + apiUri; - STDMessageManager manager = STDMessageManager.getInstance(); - STDMsgInfoAddOnVO stdMsgInfo = manager.getStdMsgInfoAddOn(methodAndUri); -// bzwkSvcKeyName = stdMsgInfo.getBzwksvckeyname(); - apiSvcCode = stdMsgInfo.getEaiSvcCd(); - transactionProp.put(API_SERVICE_CODE, apiSvcCode); - -// adapterGrpName = stdMsgInfo.gAdapterGroupName(); - apiFullPathKey = stdMsgInfo.getApiFullPath(); - if (STDMessageManager.isPathVariable(apiFullPathKey)) { - pathVariables = new AntPathMatcher().extractUriTemplateVariables(apiFullPathKey, methodAndUri); - } - -// adptUri = findAdptUri(apiUri, HttpDynamicInAdapterManager.getInstance(), adapterGrpName); - } catch (Exception e) { -// adptUri = null; - } - // *** - if (pathVariables != null) { - transactionProp.put(INBOUND_PATH_VARIABLES, pathVariables); - } String responseData = ""; try { responseData = service.callApi(servletRequest, servletResponse, httpProp, adapterGroupVO, adapterVO, transactionProp); - // if (RESPONSE_TYPE_ASYNC.equals(responseType)) { // table의 값이 ASYNC 로 들어가 있는데 - // response_type_async는 ASYN 로 되어 있어 아래처럼 비교함 jwhong 2025 - if ("ASYNC".equals(responseType)) { - // responseEntity = ResponseEntity.ok("dummy"); // comment by jwhong - // servletResponse.addHeader("traceId", responseData); // uuid를 response header에 - servletResponse.addHeader("traceId", - transactionProp.getProperty(TransactionContextKeys.TRANSACTION_UUID)); // uuid를 response header에 - int httpStatus = servletResponse.getStatus(); - if (httpStatus == 200) { - // 업체별 aync response message 가 다르다. - String asyncMsgStyle = httpProp.getProperty("ASYNC_RTNMSG_TYPE", ""); - String responseBody = ""; - boolean encryptAsyncAckApply = StringUtils - .equalsIgnoreCase(httpProp.getProperty("ASYNC_ENCRYPT_ACK", "N"), "Y"); - if (asyncMsgStyle == null || asyncMsgStyle.trim().isEmpty()) { - responseBody = makeResponseBodyMsg(); - } else { - responseBody = asyncMsgStyle; - } - if (encryptAsyncAckApply) { - responseBody = service.doPostEncryption(responseBody, transactionProp, servletRequest); - } - - responseEntity = ResponseEntity.status(httpStatus).contentType(mediaType).body(responseBody); - // jwhong - } else { - responseEntity = ResponseEntity.status(httpStatus).contentType(mediaType).body(responseData); - } + servletResponse.addHeader("traceId", + transactionProp.getProperty(TransactionContextKeys.TRANSACTION_UUID)); // uuid를 response header에 + int httpStatus = servletResponse.getStatus(); + if (httpStatus != 200) { + responseEntity = ResponseEntity.status(httpStatus).contentType(mediaType).body(responseData); } else { -// responseEntity = ResponseEntity.ok().contentType(mediaType).body(responseData); -// 버즈빌 포인트 적립 처리하기 위하여 정상응답이더라도 응답코드(apiRsltCd) 값이 200이 아닌 경우 처리를 위하여 수정 -// Filter에서 설정한 response status값을 responseEntity 생성시 적용 modify by lwk 2025.03.24 - - servletResponse.addHeader("traceId", - transactionProp.getProperty(TransactionContextKeys.TRANSACTION_UUID)); // uuid를 response header에 - int httpStatus = servletResponse.getStatus(); - if (httpStatus != 200) { - responseEntity = ResponseEntity.status(httpStatus).contentType(mediaType).body(responseData); - } else { - responseEntity = ResponseEntity.ok().contentType(mediaType).body(responseData); - } + responseEntity = ResponseEntity.ok().contentType(mediaType).body(responseData); } } catch (Exception e) { - - logger.error(adapterGroupName + "-" + adapterName + ">>" + e.getMessage(), e); String errorMsg = null; - if( e instanceof HttpStatusException ) { + if(e instanceof HttpStatusException ) { logger.warn("ApiAdapterController] " + adapterGroupName + "-" + adapterName + ">>" + e.getMessage()); HttpStatusException e1 = (HttpStatusException) e; errorMsg = MessageUtil.makeErrorMessageByMessageType(adptMsgType, encode, @@ -232,13 +162,6 @@ public class DJErpApiAdapterController implements HttpAdapterServiceKey { responseEntity = ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).contentType(mediaType) .body(errorMsg); } - String postFilterNames = httpProp.getProperty("POST_FILTERS",""); - - if( postFilterNames.contains("HMAC_SHA256") ) { - HttpAdapterFilter filter = HttpAdapterFilterFactory.createFilter("HMAC_SHA256"); - filter.doPostFilter(adapterGroupName, postFilterNames, errorMsg, transactionProp, servletRequest, servletResponse); - } - responseData = errorMsg; } finally { @@ -252,22 +175,6 @@ public class DJErpApiAdapterController implements HttpAdapterServiceKey { servletRequest.setAttribute(TransactionContextKeys.TRANSACTION_PROP, transactionProp); - if (!"ASYNC".equals(responseType)) { -// String uuid = transactionProp.getProperty(TransactionContextKeys.TRANSACTION_UUID); - String url = transactionProp.getProperty(HttpClientAdapterServiceKey.INBOUND_EXTURI); - String method = transactionProp.getProperty(HttpClientAdapterServiceKey.INBOUND_METHOD); - int httpStatusCode = servletResponse.getStatus(); - - Map headerMap = new HashMap<>(); - // 응답 헤더 로깅 - for (String headerName : servletResponse.getHeaderNames()) { - String headerValue = servletResponse.getHeader(headerName); - logger.debug(String.format("httpHeader logging headerKey=%s, headerValue=%s", - headerName, headerValue)); - headerMap.put(headerName, headerValue); - } - //HttpAdapterExtraLogUtil.insertHttpAdapterExtraLog(uuid, 400, adapterGroupName,adapterName, headerMap, url, method, httpStatusCode); - } } catch (Exception e) { logger.warn("http header db logging fail.", e); } diff --git a/src/main/java/com/eactive/eai/adapter/service/DJErpApiAdapterService.java b/src/main/java/com/eactive/eai/adapter/service/DJErpApiAdapterService.java index 4e1353d..5e8cf28 100644 --- a/src/main/java/com/eactive/eai/adapter/service/DJErpApiAdapterService.java +++ b/src/main/java/com/eactive/eai/adapter/service/DJErpApiAdapterService.java @@ -1,14 +1,49 @@ package com.eactive.eai.adapter.service; +import java.io.IOException; +import java.io.StringWriter; +import java.net.URLDecoder; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; + +import javax.servlet.ServletInputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.time.StopWatch; +import org.apache.mina.common.ByteBuffer; +import org.dom4j.Document; +import org.dom4j.DocumentException; +import org.dom4j.DocumentHelper; +import org.dom4j.Element; +import org.dom4j.Node; +import org.dom4j.io.OutputFormat; +import org.dom4j.io.XMLWriter; +import org.json.simple.JSONObject; +import org.json.simple.JSONValue; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Service; +import org.springframework.util.AntPathMatcher; + import com.eactive.eai.adapter.AdapterGroupVO; -import com.eactive.eai.adapter.AdapterPropManager; import com.eactive.eai.adapter.AdapterVO; import com.eactive.eai.adapter.Keys; import com.eactive.eai.adapter.http.HttpMemoryLogger; import com.eactive.eai.adapter.http.HttpMethodType; import com.eactive.eai.adapter.http.dynamic.HttpAdapterServiceSupport; +import com.eactive.eai.adapter.http.dynamic.filter.ApiKeyExtractFilter; import com.eactive.eai.adapter.http.filter.AbstractCryptoFilter; -import com.eactive.eai.adapter.http.dynamic.filter.JwtAuthFilter; +import com.eactive.eai.common.context.ElinkTransactionContext; import com.eactive.eai.common.exception.ExceptionUtil; import com.eactive.eai.common.message.MessageType; import com.eactive.eai.common.util.CommonLib; @@ -24,49 +59,6 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.time.StopWatch; -import org.apache.mina.common.ByteBuffer; -import org.dom4j.Document; -import org.dom4j.DocumentException; -import org.dom4j.DocumentHelper; -import org.dom4j.Element; -import org.dom4j.Node; -import org.dom4j.io.OutputFormat; -import org.dom4j.io.XMLWriter; -import org.json.simple.JSONObject; -import org.json.simple.JSONValue; -import org.json.simple.parser.JSONParser; -import org.slf4j.MDC; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.MediaType; -import org.springframework.stereotype.Service; -import org.springframework.util.AntPathMatcher; - -import javax.servlet.ServletInputStream; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.net.URLDecoder; -import java.util.*; - -//for encrypt/decrypt jwhong -import com.eactive.eai.authserver.service.OAuth2Manager; -import com.kjbank.encrypt.exchange.crypto.AES256Cipher; -import com.kjbank.encrypt.exchange.crypto.AESCipher; -import com.kjbank.encrypt.exchange.crypto.AES256GCMCipher; -import java.nio.charset.StandardCharsets; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.ObjectOutputStream; -import java.io.StringWriter; -import java.io.IOException; -import java.io.ObjectInputStream; -import org.springframework.security.oauth2.provider.ClientDetails; -import com.nimbusds.jwt.SignedJWT; -import com.eactive.eai.common.TransactionContextKeys; -import com.eactive.eai.common.context.ElinkTransactionContext; - @Service public class DJErpApiAdapterService extends HttpAdapterServiceSupport { @@ -79,17 +71,12 @@ public class DJErpApiAdapterService extends HttpAdapterServiceSupport { public static final String HEADER_KEYS = "HEADER_KEYS"; public static final String PROPERTIES_NAME_HTTP_REQUEST_METHOD = "httpRequestMethod"; - private static final String JSON_CONTENT_TYPE = "application/json"; - private static final String JSON_FIELD_NAME = "json-body"; - private static final String FILE_GROUP_NAME = "image-file"; - private static final String UPLOAD_ROOT_PATH = "UPLOAD_ROOT_PATH"; - public static final String PAYLOAD_PARAM_NAME_CLIENT_ID = "client_id"; // jwhong - private boolean encryptResponseApply; // inbound에 대한 응답 암호화 여부 flag - - public String callApi(HttpServletRequest request, HttpServletResponse response, Properties httpProp, AdapterGroupVO adapterGroupVO, AdapterVO adapterVO, Properties transactionProp) throws Exception { + public static final String PAYLOAD_PARAM_NAME_CLIENT_ID = "client_id"; // jwhong + + public String callApi(HttpServletRequest request, HttpServletResponse response, Properties httpProp, + AdapterGroupVO adapterGroupVO, AdapterVO adapterVO, Properties transactionProp) throws Exception { int traceLevel = 0; - AdapterPropManager manager = null; String adptGrpName = adapterGroupVO.getName(); String adptName = adapterVO.getName(); @@ -106,10 +93,11 @@ public class DJErpApiAdapterService extends HttpAdapterServiceSupport { String paramValue = null; String adptMsgType = null; + Properties inboundHeaderProp = getHeaders(request); transactionProp.put(INBOUND_METHOD, request.getMethod()); transactionProp.put(INBOUND_URI, request.getRequestURI()); transactionProp.put(INBOUND_QUERY_STRING, StringUtils.defaultString(request.getQueryString())); - transactionProp.put(INBOUND_HEADER, getHeaders(request)); + transactionProp.put(INBOUND_HEADER, inboundHeaderProp); transactionProp.put(INBOUND_EXTPARAMS, StringUtils.defaultString(request.getQueryString())); if (StringUtils.equals(adapterVO.getAdapterGroupVO().getType(), Keys.TYPE_REST) || StringUtils.equals(adapterVO.getAdapterGroupVO().getType(), Keys.TYPE_HTTP_CUSTOM)) { @@ -144,13 +132,13 @@ public class DJErpApiAdapterService extends HttpAdapterServiceSupport { transactionProp.put(ENC_PATHS, httpProp.getProperty(ENC_PATHS, "")); transactionProp.put(ENCRYPT_ALGORITHM, httpProp.getProperty(ENCRYPT_ALGORITHM, "")); //jwhong - if (getHeaders(request).get(HEADER_NAME_CLIENT_ID) != null) { // jwhong - transactionProp.put(HEADER_NAME_CLIENT_ID, getHeaders(request).get(HEADER_NAME_CLIENT_ID)); + if (inboundHeaderProp.get(HEADER_NAME_CLIENT_ID) != null) { // jwhong + transactionProp.put(HEADER_NAME_CLIENT_ID, inboundHeaderProp.get(HEADER_NAME_CLIENT_ID)); } transactionProp.put(ENCRYPT_BASE_TYPE, httpProp.getProperty(ENCRYPT_BASE_TYPE, "")); //jwhong // //transactionProp.put(INBOUND_TOKEN, getHeaders(request).get(INBOUND_TOKEN)); // jwhong // transactionProp.put(INBOUND_TOKEN, getHeaders(request).get(INBOUND_TOKEN) != null ? getHeaders(request).get(INBOUND_TOKEN) : ""); //jwhong , null 이면 default로 "" put - String inboundToken = getHeaders(request).getOrDefault(INBOUND_TOKEN, "").toString(); + String inboundToken = inboundHeaderProp.getOrDefault(INBOUND_TOKEN, "").toString(); if (StringUtils.isNotBlank(inboundToken) && StringUtils.startsWith(inboundToken, "Bearer ")) { inboundToken = inboundToken.substring(7); } @@ -164,7 +152,7 @@ public class DJErpApiAdapterService extends HttpAdapterServiceSupport { transactionProp.put(HEADER_KEYS, relayRequestHeaderKeys); // SEED 컬럼암호하 시 Key로 사용함 - String seedkey = getHeaders(request).getOrDefault("x-obp-partnercode", "").toString(); + String seedkey = inboundHeaderProp.getOrDefault("x-obp-partnercode", "").toString(); ElinkTransactionContext.setSeedKey(seedkey); try { @@ -351,6 +339,10 @@ public class DJErpApiAdapterService extends HttpAdapterServiceSupport { // 위치변경 : 가공되지 않은 Body값을 저장하기 위하여 위쪽으로 이동. // transactionProp.put(INBOUND_REQUEST_MESSAGE, message); // 로컬 서비스 호출 ,encoding 처리 추가 + + ApiKeyExtractFilter apiKeyExtractor = new ApiKeyExtractFilter(); + apiKeyExtractor.doPreFilter(adptGrpName, adptName, reqObject, transactionProp, request, response); + String result = (String) service(adptGrpName, adptName, reqObject, transactionProp, request, response); // bypass만 필요 @@ -628,410 +620,6 @@ public class DJErpApiAdapterService extends HttpAdapterServiceSupport { return (String)resultMessage; } - - // jwhong decrypt - private String doPreDecryption(String eaiBody, Properties transactionProp, HttpServletRequest request) throws Exception { - String decryptAlgorithm = transactionProp.getProperty(ENCRYPT_ALGORITHM, ""); - String xElinkClientId = transactionProp.getProperty(HEADER_NAME_CLIENT_ID, ""); // 이 값이 OAuth의 client id 값이다 . http header 에 포함되어 온다. jwhong - String encryptBaseKeyType = transactionProp.getProperty(ENCRYPT_BASE_TYPE); - //String inboundToken2 = transactionProp.getProperty(INBOUND_TOKEN, ""); // 이건 token 값이 아니고 authorization 값이다 - String inboundToken = ""; - - String secretAES256Iv = transactionProp.getProperty("ENCRYPT_AES256_IV"); - String secretAES256Key = transactionProp.getProperty("ENCRYPT_AES256_KEY"); - - if ( decryptAlgorithm == "" ) { // 복호화 대상이 아님 - return eaiBody; - } - - inboundToken = JwtTokenExtractor(request); - xElinkClientId = JwtClientIdExtractor(inboundToken); // token에서 client id를 추출함 - - // 암호화 key를 token 내용으로 할지 client secret 내용으로 할지 결정함. adapter property에 정의함 - // ENCRYPT_ALGORITHM 을 설정했는데 ENCRYPT_BASE_TYPE을 설정안하면 Decrypt 수행시 Exception 발생함 - String clientSecret = ""; - if ("TOKEN".equalsIgnoreCase(encryptBaseKeyType)) { - clientSecret = inboundToken; - } else if ( "SECRETKEY".equalsIgnoreCase(encryptBaseKeyType)) { - OAuth2Manager manager = OAuth2Manager.getInstance(); - ClientDetails clientDetail = manager.getClientDeatilsStore().get(xElinkClientId); - - if ( clientDetail != null ) { // 여기서 not null 이라는 것은 apim oauth server에서 발급된 token 임. - // 즉 KJBank 에서 요청이 들어온 것이다. - clientSecret = clientDetail.getClientSecret(); - } // 그럼 업체에서 요청이 들어오면?? 업체도 apim oauth server에서 발급된 token을 사용하는것이다. - } - - byte[] decryptedMessage = null; - decryptedMessage = doInboundPreDecrypt(eaiBody, decryptAlgorithm,clientSecret,secretAES256Iv, secretAES256Key ); - - if (decryptedMessage == null) { - throw new Exception("[ApiAdapterService] Decrypt Error : Invalid encrypted message"); - } - - - String resultMessage = new String(decryptedMessage, StandardCharsets.UTF_8 ); - return resultMessage; - //return new String(decryptedMessage, StandardCharsets.UTF_8 ); - } - - - // jwhong encrypt - public String doPostEncryption(String eaiBody, Properties transactionProp, HttpServletRequest request) throws Exception { - - String decryptAlgorithm = transactionProp.getProperty(ENCRYPT_ALGORITHM, ""); - String xElinkClientId = transactionProp.getProperty(HEADER_NAME_CLIENT_ID, ""); // 이 값이 OAuth의 client id 값이다 . http header 에 포함되어 온다. jwhong - String encryptBaseKeyType = transactionProp.getProperty(ENCRYPT_BASE_TYPE); - String inboundToken = ""; - - String secretAES256Iv = transactionProp.getProperty("ENCRYPT_AES256_IV"); - String secretAES256Key = transactionProp.getProperty("ENCRYPT_AES256_KEY"); - - if ( decryptAlgorithm == "" ) { // 복호화 대상이 아님 - return eaiBody; - } - - inboundToken = JwtTokenExtractor(request); - xElinkClientId = JwtClientIdExtractor(inboundToken); // token에서 client id를 추출함 - - // 암호화 key를 token 내용으로 할지 client secret 내용으로 할지 결정함. adapter property에 정의함 - // ENCRYPT_ALGORITHM 을 설정했는데 ENCRYPT_BASE_TYPE을 설정안하면 Decrypt 수행시 Exception 발생함 - String clientSecret = ""; - if ("TOKEN".equalsIgnoreCase(encryptBaseKeyType)) { - clientSecret = inboundToken; - } else if ( "SECRETKEY".equalsIgnoreCase(encryptBaseKeyType)) { - OAuth2Manager manager = OAuth2Manager.getInstance(); - ClientDetails clientDetail = manager.getClientDeatilsStore().get(xElinkClientId); - - if ( clientDetail != null ) { - clientSecret = clientDetail.getClientSecret(); - } - } - - String encryptedMessage = null; - encryptedMessage = doInboundPostEncrypt(eaiBody, decryptAlgorithm,clientSecret,secretAES256Iv, secretAES256Key ); - - if (encryptedMessage == null) { - throw new Exception("[ApiAdapterService] Encrypt Error : Invalid PlainText message"); - } - - return encryptedMessage; - //String resultMessage = new String(encryptedMessage, StandardCharsets.UTF_8 ); - //return resultMessage; - } - - - private byte[] convertObjectToBytes(Object obj) throws IOException { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - ObjectOutputStream oos = new ObjectOutputStream(bos); - oos.writeObject(obj); - oos.flush(); - return bos.toByteArray(); - } - - private byte[] convertObjectToBase64Bytes(Object obj) throws IOException { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - ObjectOutputStream oos = new ObjectOutputStream(bos); - oos.writeObject(obj); - oos.flush(); - oos.close(); - - // 직렬화된 byte[] → Base64 문자열 → 다시 byte[]로 변환 - String base64String = Base64.getEncoder().encodeToString(bos.toByteArray()); - return base64String.getBytes(StandardCharsets.UTF_8); - } - - public static Object convertBytesToObject(byte[] bytes) throws IOException, ClassNotFoundException { - try (ByteArrayInputStream bis = new ByteArrayInputStream(bytes); - ObjectInputStream ois = new ObjectInputStream(bis)) { - return ois.readObject(); - } - } - - - private byte[] doInboundPreDecrypt(String eaiStringBody, String decryptAlgorithm, String clientSecretKey, String secretAES256Iv, String secretAES256Key) throws Exception { - - byte[] decryptedMessage = null; - //String eaiStringBody = new String(eaiBody, StandardCharsets.UTF_8); - - eaiStringBody = extractBodyMsg(decryptAlgorithm, eaiStringBody); - - if (eaiStringBody == null) { - throw new Exception("[ApiAdapterService] Cannot decrypt Error : input is plain text"); - } - - //test source - logger.debug("Base64 문자열: " + eaiStringBody); - logger.debug("Base64 문자열 길이: " + eaiStringBody.length()); - logger.debug("Base64 문자열 끝: " + eaiStringBody.substring(eaiStringBody.length() - 10)); - - // Base64 디코딩 테스트 - try { - byte[] decoded = Base64.getDecoder().decode(eaiStringBody); - logger.debug("디코딩 성공, 길이: " + decoded.length); - } catch (IllegalArgumentException e) { - logger.debug("Base64 디코딩 실패: " + e.getMessage()); - } - // end test source - - switch (decryptAlgorithm) { - case "AES128": - case "AES128-TOSS": - try { - String key128 = clientSecretKey.substring(22,38); //togetherEncoder 경우는 substring(44,60) 이네?? - //String decryptedStrMessage = AESCipher.decrypt(eaiStringBody, key128); - String decryptedStrMessage = AESCipher.decryptExistException(eaiStringBody, key128); - decryptedMessage = decryptedStrMessage.getBytes(StandardCharsets.UTF_8); - } catch (Exception e) { - e.printStackTrace(); - logger.error("HttpClientAdapterServiceRest] AES128 Decryption error=" + e.getMessage()); - } - break; - case "AES128-TOGETHER": - try { - String key128 = clientSecretKey.substring(44,60); //togetherEncoder 경우는 substring(44,60) 이네?? - //String decryptedStrMessage = AESCipher.decrypt(eaiStringBody, key128); - String decryptedStrMessage = AESCipher.decryptExistException(eaiStringBody, key128); - decryptedMessage = decryptedStrMessage.getBytes(StandardCharsets.UTF_8); - } catch (Exception e) { - e.printStackTrace(); - logger.error("HttpClientAdapterServiceRest] AES128 Decryption error=" + e.getMessage()); - } - break; - case "AES256": - try { - String key = clientSecretKey.substring(10,42); - String decryptedStrMessage = AES256Cipher.decrypt(eaiStringBody, key); - decryptedMessage = decryptedStrMessage.getBytes(StandardCharsets.UTF_8); - } catch (Exception e) { - e.printStackTrace(); - logger.error("HttpClientAdapterServiceRest] AES256 Decryption error=" + e.getMessage()); - } - break; - case "AES256-KAKAO": - try { - String decryptedStrMessage = AES256Cipher.decrypt(eaiStringBody, secretAES256Iv, secretAES256Key); - decryptedMessage = decryptedStrMessage.getBytes(StandardCharsets.UTF_8); - } catch (Exception e) { - e.printStackTrace(); - logger.error("HttpClientAdapterServiceRest] AES256 Decryption error=" + e.getMessage()); - } - break; - case "AES256-TOSS": - try { - String decryptedStrMessage = AES256Cipher.decrypt(eaiStringBody, secretAES256Iv, secretAES256Key); - decryptedMessage = decryptedStrMessage.getBytes(StandardCharsets.UTF_8); - } catch (Exception e) { - e.printStackTrace(); - logger.error("HttpClientAdapterServiceRest] AES256 Decryption error=" + e.getMessage()); - } - break; - - case "AES256GCM": - try { - byte[] key = AES256GCMCipher.generateKey(); - AES256GCMCipher cipher = new AES256GCMCipher(key); - byte[] aad = "metadata".getBytes(); - - String decryptedStrMessage = cipher.decrypt(eaiStringBody , aad); - decryptedMessage = decryptedStrMessage.getBytes(StandardCharsets.UTF_8); - - } catch (Exception e) { - e.printStackTrace(); - logger.error("HttpClientAdapterServiceRest] AES256GCM Encryption error=" + e.getMessage()); - } - break; - default: - try { - decryptedMessage = eaiStringBody.getBytes(); - } catch (Exception e) { - e.printStackTrace(); - logger.error("HttpClientAdapterServiceRest] doOutboundPostFilter Array copy error=" + e.getMessage()); - } - break; - } - - return decryptedMessage; - } - - private String extractBodyMsg(String decryptAlgorithm, String eaiStringBody) { - - String decryptedJsonKey = ""; - String decryptedBodyMessage = ""; - - decryptedJsonKey = getJsonKey(decryptAlgorithm); - - try { - JSONParser parser = new JSONParser(); - JSONObject jsonObject = (JSONObject) parser.parse(eaiStringBody); - decryptedBodyMessage = (String) jsonObject.get(decryptedJsonKey); - } catch (Exception e) { - e.printStackTrace(); - logger.error("HttpClient5AdapterServiceRest] extractBodyMsg error : " + e.getMessage()); - } - - return decryptedBodyMessage; - - } - - private String getJsonKey(String Algorithm) { - - String jsonKey = ""; - - switch (Algorithm) { - case "AES128": - case "AES128-TOSS": - jsonKey = "preScreeningRequest"; - break; - case "AES128-TOGETHER": - jsonKey = "obpTxData"; - break; - case "AES256": - jsonKey = "preScreeningRequest"; - break; - case "AES256-KAKAO": - jsonKey = "encrypted_data"; - break; - case "AES256-TOSS": - jsonKey = "encryptedData"; - break; - case "AES256-NICEON": - jsonKey = "enc_data"; - break; - case "AES256GCM": - break; - default: - break; - } - return jsonKey; - } - - private String doInboundPostEncrypt(String eaiStringBody, String encryptAlgorithm, String clientSecretKey, String secretAES256Iv, String secretAES256Key) { - - //byte[] encryptedMessage = null; - String encryptedStrMessage = null; - String encryptedJsonKey = ""; - - encryptedJsonKey = getJsonKey(encryptAlgorithm); - - switch (encryptAlgorithm) { - case "AES128": - case "AES128-TOSS": - try { - String key128 = clientSecretKey.substring(22,38); //togetherEncoder 경우는 substring(44,60) 이네?? - encryptedStrMessage = AESCipher.encrypt(eaiStringBody, key128); - //encryptedMessage = encryptedStrMessage.getBytes(StandardCharsets.UTF_8); - } catch (Exception e) { - e.printStackTrace(); - logger.error("HttpClientAdapterServiceRest] AES128 Encryption error=" + e.getMessage()); - } - break; - case "AES128-TOGETHER": - try { - String key128 = clientSecretKey.substring(44,60); //togetherEncoder 경우는 substring(44,60) 이네?? - encryptedStrMessage = AESCipher.encrypt(eaiStringBody, key128); - //encryptedMessage = encryptedStrMessage.getBytes(StandardCharsets.UTF_8); - } catch (Exception e) { - e.printStackTrace(); - logger.error("HttpClientAdapterServiceRest] AES128 Encryption error=" + e.getMessage()); - } - break; - case "AES256": - try { - String key = clientSecretKey.substring(10,42); - encryptedStrMessage = AES256Cipher.encrypt(eaiStringBody, key); - //encryptedMessage = encryptedStrMessage.getBytes(StandardCharsets.UTF_8); - } catch (Exception e) { - e.printStackTrace(); - logger.error("HttpClientAdapterServiceRest] AES256 Encryption error=" + e.getMessage()); - } - break; - case "AES256-KAKAO": - try { - encryptedStrMessage = AES256Cipher.encrypt(eaiStringBody, secretAES256Iv, secretAES256Key); - //encryptedMessage = encryptedStrMessage.getBytes(StandardCharsets.UTF_8); - } catch (Exception e) { - e.printStackTrace(); - logger.error("HttpClientAdapterServiceRest] AES256 Encryption error=" + e.getMessage()); - } - break; - case "AES256-TOSS": - try { - encryptedStrMessage = AES256Cipher.encrypt(eaiStringBody, secretAES256Iv, secretAES256Key); - //encryptedMessage = encryptedStrMessage.getBytes(StandardCharsets.UTF_8); - } catch (Exception e) { - e.printStackTrace(); - logger.error("HttpClientAdapterServiceRest] AES256 Encryption error=" + e.getMessage()); - } - break; - - case "AES256GCM": - try { - byte[] key = AES256GCMCipher.generateKey(); - AES256GCMCipher cipher = new AES256GCMCipher(key); - byte[] aad = "metadata".getBytes(); - - encryptedStrMessage = cipher.encrypt(eaiStringBody , aad); - //encryptedMessage = encryptedStrMessage.getBytes(StandardCharsets.UTF_8); - - } catch (Exception e) { - e.printStackTrace(); - logger.error("HttpClientAdapterServiceRest] AES256GCM Encryption error=" + e.getMessage()); - } - break; - default: - try { - encryptedStrMessage = eaiStringBody; - //encryptedMessage = eaiStringBody.getBytes(); - } catch (Exception e) { - e.printStackTrace(); - logger.error("HttpClientAdapterServiceRest] doOutboundPostFilter Array copy error=" + e.getMessage()); - } - break; - } - - Map map = new HashMap<>(); - map.put(encryptedJsonKey, encryptedStrMessage); - JSONObject json = new JSONObject(); - json.putAll(map); - return json.toJSONString(); - //return encryptedMessage; - } - - - - private String JwtTokenExtractor(HttpServletRequest request) { - - String Token = ""; - try { - Token = JwtAuthFilter.extractJWTToken(request); - } catch (Exception e) { - logger.error(e.getMessage()); - } - - logger.debug("Token is: " + Token); - return Token; - - } - - private String JwtClientIdExtractor(String inboundToken) { - - String tokenClientId =""; - try { - SignedJWT signedJWT = SignedJWT.parse(inboundToken); - tokenClientId = (String) signedJWT.getJWTClaimsSet().getClaim(PAYLOAD_PARAM_NAME_CLIENT_ID); - } catch (Exception e) { - logger.error(e.getMessage()); - tokenClientId = ""; - } - - logger.debug("Token Client ID: " + tokenClientId); - return tokenClientId; - - } - - // jwhong until here - private Map assignParameterMap(HttpServletRequest request, String adptGrpName, String adptName, Object requestBytes, Properties prop) {