From efb829403fbb83725de3b9efa2ec6225b47ba351 Mon Sep 17 00:00:00 2001 From: "Yunsam.Eo" Date: Sun, 14 Dec 2025 16:36:18 +0900 Subject: [PATCH 01/11] =?UTF-8?q?scope=EC=B2=B4=ED=81=AC=EB=A1=9C=EC=A7=81?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../http/dynamic/filter/ApiAuthFilter.java | 55 +++++++++---------- 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/src/main/java/com/eactive/eai/adapter/http/dynamic/filter/ApiAuthFilter.java b/src/main/java/com/eactive/eai/adapter/http/dynamic/filter/ApiAuthFilter.java index 569b64b..2babcee 100644 --- a/src/main/java/com/eactive/eai/adapter/http/dynamic/filter/ApiAuthFilter.java +++ b/src/main/java/com/eactive/eai/adapter/http/dynamic/filter/ApiAuthFilter.java @@ -109,6 +109,7 @@ public class ApiAuthFilter implements HttpAdapterFilter { return message; } + boolean isPassScope = false; switch (eaiMessage.getAuthType()){ case "oauth": try { @@ -127,10 +128,25 @@ public class ApiAuthFilter implements HttpAdapterFilter { OAuth2Manager manager = OAuth2Manager.getInstance(); HashSet scopeSet = manager.getApiScopeMap().get(apiId); - if (!verifyScope(scopeSet, signedJWT)) { + + String[] scopeArr = signedJWT.getJWTClaimsSet().getStringArrayClaim(PAYLOAD_PARAM_NAME_SCOPE); + if (scopeArr == null || scopeArr.length == 0) { throw new JwtAuthException(ERROR_AUTHORIZATION_FAIL, String.format("Insufficient [Scope](Allowed Scope=%s)", scopeSet.toString())); } + + for (String scope : scopeArr) { + if (StringUtils.equals(scope, "oob") || StringUtils.equals(scope, "public")) { + isPassScope = true; + } + } + + if (!isPassScope) { + if (!verifyScope(scopeSet, signedJWT)) { + throw new JwtAuthException(ERROR_AUTHORIZATION_FAIL, + String.format("Insufficient [Scope](Allowed Scope=%s)", scopeSet.toString())); + } + } // header 값으로 전송된 clientId와 토큰 소유 clientId check if (!verifyClientId(prop, signedJWT)) { @@ -144,26 +160,6 @@ public class ApiAuthFilter implements HttpAdapterFilter { throw new JwtAuthException(ERROR_AUTHENTICATION_FAIL, "Token invalid"); } break; -// case "bearer": -// try { -// String token = ApiAuthFilter.extractJWTToken(request); -// BearerTokenInfo CATokenInfo = CATokenStore.get(token); -// -// if (CATokenInfo == null) { -// throw new JwtAuthException(ERROR_AUTHENTICATION_FAIL, "Token invalid"); -// } -// -// if (CATokenInfo.isExpired()) { -// throw new JwtAuthException(ERROR_TOKEN_EXPIRED, "Access token expired"); -// } -// } catch (JwtAuthException e) { -// logger.debug(e.getMessage()); -// throw e; -// } catch (Exception e) { -// logger.error(e.getMessage()); -// throw new JwtAuthException(ERROR_AUTHENTICATION_FAIL, "Token invalid"); -// } -// break; case "api_key": String apiKeyName = PropManager.getInstance().getProperty("ApiConfig", "api.key.name", "x-api-key"); String apiKey = request.getHeader(apiKeyName); @@ -171,19 +167,22 @@ public class ApiAuthFilter implements HttpAdapterFilter { throw new JwtAuthException(ERROR_AUTHENTICATION_FAIL, "Invalid or missing API key \""+apiKeyName+"\" in Http Header"); } prop.setProperty(HttpAdapterServiceSupport.PROPERTIES_NAME_CLIENT_ID, apiKey); + isPassScope = true; break; case "none": return message; } - String clientId = prop.getProperty(HttpAdapterServiceSupport.PROPERTIES_NAME_CLIENT_ID); - ClientVO clientVO = OAuth2Manager.getInstance().getClientVO(clientId); - if(clientVO == null){ - throw new JwtAuthException(ERROR_AUTHENTICATION_FAIL, "Unregistered APP"); - } + if (isPassScope) { + String clientId = prop.getProperty(HttpAdapterServiceSupport.PROPERTIES_NAME_CLIENT_ID); + ClientVO clientVO = OAuth2Manager.getInstance().getClientVO(clientId); + if(clientVO == null){ + throw new JwtAuthException(ERROR_AUTHENTICATION_FAIL, "Unregistered APP"); + } - if(clientVO.getApiMap().containsKey(eaiSvcCd) == false){ - throw new JwtAuthException(ERROR_AUTHENTICATION_FAIL, "Unregistered API ["+eaiSvcCd+"] in ["+clientVO.getClientName()+"] APP"); + if(clientVO.getApiMap().containsKey(eaiSvcCd) == false){ + throw new JwtAuthException(ERROR_AUTHENTICATION_FAIL, "Unregistered API ["+eaiSvcCd+"] in ["+clientVO.getClientName()+"] APP"); + } } return message; From c08024d9bb2dcc07208b307c43cf8cc4121b5993 Mon Sep 17 00:00:00 2001 From: "Yunsam.Eo" Date: Sun, 14 Dec 2025 16:38:08 +0900 Subject: [PATCH 02/11] =?UTF-8?q?KJB=EC=9A=A9=20HmacSHA256=ED=95=84?= =?UTF-8?q?=ED=84=B0=20=EC=B6=94=EA=B0=80,=20Custom=20Filter=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=EA=B0=80=EB=8A=A5=ED=95=98=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EC=88=98=EC=A0=95.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dynamic/HttpAdapterServiceSupport.java | 11 +- .../filter/HmacSha256VerifyFilterKjb.java | 149 ++++++++++++++++++ .../filter/HttpAdapterFilterFactoryKjb.java | 64 ++++++++ .../dynamic/filter/HttpAdapterFilterType.java | 1 + 4 files changed, 218 insertions(+), 7 deletions(-) create mode 100644 src/main/java/com/eactive/eai/adapter/http/dynamic/filter/HmacSha256VerifyFilterKjb.java create mode 100644 src/main/java/com/eactive/eai/adapter/http/dynamic/filter/HttpAdapterFilterFactoryKjb.java diff --git a/src/main/java/com/eactive/eai/adapter/http/dynamic/HttpAdapterServiceSupport.java b/src/main/java/com/eactive/eai/adapter/http/dynamic/HttpAdapterServiceSupport.java index 0077de4..8be66b3 100644 --- a/src/main/java/com/eactive/eai/adapter/http/dynamic/HttpAdapterServiceSupport.java +++ b/src/main/java/com/eactive/eai/adapter/http/dynamic/HttpAdapterServiceSupport.java @@ -9,14 +9,11 @@ import org.apache.commons.lang3.StringUtils; import com.eactive.eai.adapter.AdapterGroupVO; import com.eactive.eai.adapter.AdapterManager; -import com.eactive.eai.adapter.AdapterPropManager; -import com.eactive.eai.adapter.AdapterVO; import com.eactive.eai.adapter.RequestDispatcher; import com.eactive.eai.adapter.http.client.HttpClientAdapterServiceKey; import com.eactive.eai.adapter.http.dynamic.filter.HttpAdapterFilter; -import com.eactive.eai.adapter.http.dynamic.filter.HttpAdapterFilterFactory; +import com.eactive.eai.adapter.http.dynamic.filter.HttpAdapterFilterFactoryKjb; import com.eactive.eai.adapter.http.dynamic.filter.HttpAdapterFilterType; -import com.eactive.eai.data.entity.onl.adapter.AdapterProp; public abstract class HttpAdapterServiceSupport implements HttpAdapterService, HttpAdapterServiceKey { protected long slowTranTime = 2000L; @@ -65,7 +62,7 @@ public abstract class HttpAdapterServiceSupport implements HttpAdapterService, H if (!"HTC".equals(group.getType())) { String allowIp = prop.getProperty(ALLOW_IP,""); if (StringUtils.isNotBlank(allowIp)) { - HttpAdapterFilter adapterFilter = HttpAdapterFilterFactory.createFilter(HttpAdapterFilterType.ADAPTERALLOWIPLISTFILTER.toString()); + HttpAdapterFilter adapterFilter = HttpAdapterFilterFactoryKjb.createFilter(HttpAdapterFilterType.ADAPTERALLOWIPLISTFILTER.toString()); if (adapterFilter != null) { message = adapterFilter.doPreFilter(adptGrpName, adptName, message, prop, request, response); } @@ -83,7 +80,7 @@ public abstract class HttpAdapterServiceSupport implements HttpAdapterService, H continue; } - HttpAdapterFilter adapterFilter = HttpAdapterFilterFactory.createFilter(filterName.trim()); + HttpAdapterFilter adapterFilter = HttpAdapterFilterFactoryKjb.createFilter(filterName.trim()); if (adapterFilter != null) { message = adapterFilter.doPreFilter(adptGrpName, adptName, message, prop, request, response); } else { @@ -106,7 +103,7 @@ public abstract class HttpAdapterServiceSupport implements HttpAdapterService, H continue; } - HttpAdapterFilter adapterFilter = HttpAdapterFilterFactory.createFilter(filterName.trim()); + HttpAdapterFilter adapterFilter = HttpAdapterFilterFactoryKjb.createFilter(filterName.trim()); if (adapterFilter != null) { resultMessage = adapterFilter.doPostFilter(adptGrpName, adptName, resultMessage, prop, request, response); diff --git a/src/main/java/com/eactive/eai/adapter/http/dynamic/filter/HmacSha256VerifyFilterKjb.java b/src/main/java/com/eactive/eai/adapter/http/dynamic/filter/HmacSha256VerifyFilterKjb.java new file mode 100644 index 0000000..a908c2d --- /dev/null +++ b/src/main/java/com/eactive/eai/adapter/http/dynamic/filter/HmacSha256VerifyFilterKjb.java @@ -0,0 +1,149 @@ +package com.eactive.eai.adapter.http.dynamic.filter; + +import java.io.UnsupportedEncodingException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.Base64; +import java.util.Properties; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +//import org.apache.commons.net.util.Base64; +import org.springframework.http.HttpStatus; +import org.apache.commons.lang3.StringUtils; + +import com.eactive.eai.adapter.http.dynamic.HttpAdapterServiceKey; +import com.eactive.eai.adapter.http.dynamic.HttpAdapterServiceSupport; +import com.eactive.eai.authserver.service.OAuth2Manager; +import com.eactive.eai.authserver.vo.ClientVO; +import com.eactive.eai.common.util.Logger; + +public class HmacSha256VerifyFilterKjb implements HttpAdapterFilter { + + static Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER); + + public HmacSha256VerifyFilterKjb() { + super(); + } + + @Override + public Object doPreFilter(String adptGrpName, String adptName, Object message, Properties prop, + HttpServletRequest request, HttpServletResponse response) throws Exception { + + String inboundMethod = prop.getProperty(HttpAdapterServiceKey.INBOUND_METHOD); + String inboundUri = prop.getProperty(HttpAdapterServiceKey.INBOUND_URI); + String inboundBody = prop.getProperty(HttpAdapterServiceKey.INBOUND_REQUEST_MESSAGE); + + try { + String xObpPartnercode = request.getHeader("x-obp-partnercode"); + String xObpSignatureUrl = request.getHeader("x-obp-signature-url"); + String xObpSignatureBody = request.getHeader("x-obp-signature-body"); + if (StringUtils.isBlank(xObpPartnercode) + || StringUtils.isBlank(xObpSignatureUrl) + || StringUtils.isBlank(xObpSignatureBody)) { + throw new JwtAuthException( + String.valueOf(HttpStatus.UNAUTHORIZED.value()), + "Can not find mandatory Http Header"); + } + + if (StringUtils.isNotBlank(prop.getProperty(HttpAdapterServiceKey.INBOUND_QUERY_STRING))) { + inboundUri = inboundUri + "?" + prop.getProperty(HttpAdapterServiceKey.INBOUND_QUERY_STRING); + inboundBody = ""; + } + String chkUrl = inboundMethod + "&" + inboundUri; + String chkBody = inboundBody; + String clientSecret = assignClientSecret(prop, request); + + String macUrl = calculateHMAC(chkUrl, clientSecret); + String macBody = calculateHMAC(chkBody, clientSecret); + + if (!StringUtils.equals(xObpSignatureUrl, macUrl)) { + throw new JwtAuthException( + String.valueOf(HttpStatus.UNAUTHORIZED.value()), + "Signature verifying failed : x-obp-signature-url"); + } + + if (!StringUtils.equals(xObpSignatureBody, macBody)) { + throw new JwtAuthException( + String.valueOf(HttpStatus.UNAUTHORIZED.value()), + "Signature verifying failed : x-obp-signature-body"); + } + + } catch (JwtAuthException e) { + logger.debug(e.getMessage()); + throw e; + } catch (Exception e) { + logger.error(e.getMessage()); + throw new JwtAuthException( + String.valueOf(HttpStatus.UNAUTHORIZED.value()), + "Signature verifying failed(unkown)"); + } + + return message; + } + + public static String calculateHMAC(String data, String key) + throws NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException { + + Mac mac = Mac.getInstance("HmacSHA256"); + mac.init(new SecretKeySpec(key.getBytes(), "HmacSHA256")); + return Base64.getEncoder().encodeToString(mac.doFinal(data.getBytes("UTF-8"))); + } + + private String assignClientSecret(Properties prop, HttpServletRequest request) throws Exception { + String clientId = assignClientId(prop, request); + if (StringUtils.isBlank(clientId)) { + throw new JwtAuthException( + String.valueOf(HttpStatus.UNAUTHORIZED.value()), + "Can not find clientId info"); + } + + ClientVO client = (ClientVO) OAuth2Manager.getInstance().getClientDeatilsStore().get(clientId); + if (client == null) { + throw new JwtAuthException( + String.valueOf(HttpStatus.UNAUTHORIZED.value()), + "Can not find client info"); + } + + return client.getClientSecret(); + } + + private String assignClientId(Properties prop, HttpServletRequest request) { + String clientId = null; + try { + // 이전 filter에서 셋팅한 clientId 확보 + clientId = prop.getProperty(HttpAdapterServiceSupport.PROPERTIES_NAME_CLIENT_ID); + + // 이전 filter에서 셋팅한 값이 없다면 header 정보에서 확보 + if (StringUtils.isBlank(clientId)) { + clientId = request.getHeader(HttpAdapterServiceSupport.HEADER_NAME_CLIENT_ID); + } + } catch (Exception e) { + logger.error(e.getMessage()); + } + + return clientId; + } + + @Override + public Object doPostFilter(String adptGrpName, String adptName, Object resultMessage, Properties prop, + HttpServletRequest request, HttpServletResponse response) throws Exception { + + String outboundBody = (String) resultMessage; + + try { + String clientSecret = assignClientSecret(prop, request); + String macBody = calculateHMAC(outboundBody, clientSecret); + + response.addHeader("x-obp-signature-body", macBody); + } catch (Exception e) { + logger.error(e.getMessage()); + } + + return resultMessage; + } + +} diff --git a/src/main/java/com/eactive/eai/adapter/http/dynamic/filter/HttpAdapterFilterFactoryKjb.java b/src/main/java/com/eactive/eai/adapter/http/dynamic/filter/HttpAdapterFilterFactoryKjb.java new file mode 100644 index 0000000..977f92f --- /dev/null +++ b/src/main/java/com/eactive/eai/adapter/http/dynamic/filter/HttpAdapterFilterFactoryKjb.java @@ -0,0 +1,64 @@ +package com.eactive.eai.adapter.http.dynamic.filter; + +import java.util.concurrent.ConcurrentHashMap; + +import com.eactive.eai.common.util.Logger; + +public class HttpAdapterFilterFactoryKjb extends HttpAdapterFilterFactory { + static Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER); + static String basePackage = "com.eactive.eai.custom.adapter.http.dynamic.filter"; + static private ConcurrentHashMap h = new ConcurrentHashMap(); + + public HttpAdapterFilterFactoryKjb() { + super(); + } + + @SuppressWarnings("rawtypes") + public static HttpAdapterFilter createFilter(String type) { + if (h.containsKey(type)) { + return (HttpAdapterFilter) h.get(type); + } + + HttpAdapterFilter filter = null; + switch (HttpAdapterFilterType.getValue(type)) { + case APIAUTHFILTER: + filter = new ApiAuthFilter(); + break; + case JWTAUTHFILTER: + filter = new JwtAuthFilter(); + break; + case IPWHITELISTFILTER: + filter = new IpWhiteListFilter(); + break; + case ADAPTERALLOWIPLISTFILTER: + filter = new AdapterAllowIPListFilter(); + break; + case HMAC_SHA256: + filter = new HmacSha256VerifyFilterKjb(); + break; + default: + filter = classForName(type); + if (filter == null) { + filter = classForName(basePackage + "." + type); + } + break; + } + + if (filter != null) { + h.put(type, filter); + return filter; + } else { + return null; + } + } + + private static HttpAdapterFilter classForName(String type) { + try { + Class cl = Class.forName(type); + return (HttpAdapterFilter) cl.newInstance(); + } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) { + logger.error("Cannot create a HttpAdapterFilter. - {}", type); + return null; + } + } +} diff --git a/src/main/java/com/eactive/eai/adapter/http/dynamic/filter/HttpAdapterFilterType.java b/src/main/java/com/eactive/eai/adapter/http/dynamic/filter/HttpAdapterFilterType.java index 6231c74..c522614 100644 --- a/src/main/java/com/eactive/eai/adapter/http/dynamic/filter/HttpAdapterFilterType.java +++ b/src/main/java/com/eactive/eai/adapter/http/dynamic/filter/HttpAdapterFilterType.java @@ -5,6 +5,7 @@ public enum HttpAdapterFilterType { JWTAUTHFILTER, IPWHITELISTFILTER, ADAPTERALLOWIPLISTFILTER, + HMAC_SHA256, UNKNOWN; public static HttpAdapterFilterType getValue(String type) { From 73454018e2f2944ae2a4e531e3498e0f5dd51b9a Mon Sep 17 00:00:00 2001 From: "Yunsam.Eo" Date: Mon, 15 Dec 2025 18:06:41 +0900 Subject: [PATCH 03/11] =?UTF-8?q?ApiScopeMap=20Loading=20=EC=98=A4?= =?UTF-8?q?=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/eactive/eai/authserver/dao/ScopeDAO.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/eactive/eai/authserver/dao/ScopeDAO.java b/src/main/java/com/eactive/eai/authserver/dao/ScopeDAO.java index 83676f6..3cae28a 100644 --- a/src/main/java/com/eactive/eai/authserver/dao/ScopeDAO.java +++ b/src/main/java/com/eactive/eai/authserver/dao/ScopeDAO.java @@ -31,9 +31,15 @@ public class ScopeDAO extends BaseDAO { Map> clientEntitymap = new HashMap<>(); scopeEntities.forEach(scopeEntity -> { - HashSet set = new HashSet<>(); - String scopeId = scopeEntity.getId().getBzwksvckeyname() + scopeEntity.getId().getScopeid(); - clientEntitymap.put(scopeId, set); + String apiId = scopeEntity.getId().getBzwksvckeyname(); + HashSet scopeSet = clientEntitymap.get(apiId); + if (scopeSet == null) { + scopeSet = new HashSet(); + clientEntitymap.put(apiId, scopeSet); + } + String scopeId = scopeEntity.getId().getScopeid(); + clientEntitymap.get(apiId).add(scopeId); +// clientEntitymap.put(scopeId, set); }); return clientEntitymap; From 7dd17f0db9a23118ac2ac4b06998f3c6a442e4f1 Mon Sep 17 00:00:00 2001 From: jaewohong Date: Tue, 16 Dec 2025 11:07:05 +0900 Subject: [PATCH 04/11] =?UTF-8?q?Async-Sync=20=EC=8B=9C=20400=EC=97=90=20?= =?UTF-8?q?=EB=8C=80=ED=95=9C=20=EC=9D=91=EB=8B=B5=20500=20=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=20=EC=B6=9C=EB=A0=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../eactive/eai/outbound/DefaultProcess.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/main/java/com/eactive/eai/outbound/DefaultProcess.java b/src/main/java/com/eactive/eai/outbound/DefaultProcess.java index 691e07a..65e10d7 100644 --- a/src/main/java/com/eactive/eai/outbound/DefaultProcess.java +++ b/src/main/java/com/eactive/eai/outbound/DefaultProcess.java @@ -37,6 +37,7 @@ import com.eactive.eai.message.manager.StandardMessageManager; import com.eactive.eai.message.mapper.MessageMapper; import com.eactive.eai.message.service.InterfaceMapper; import com.ext.eai.common.stdmessage.STDMessageKeys; +import com.eactive.eai.common.message.ServiceMessage; // jwhong public abstract class DefaultProcess extends Process { protected static Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT); @@ -252,6 +253,13 @@ public abstract class DefaultProcess extends Process { } // 송신결과확인 checkSendResult(); + // 400 Async 에 대한 ack 오는것 500 으로 log에 저장, jwhong for kjbank + if(isAsyncSyncPattern()){ + StandardMessage standardMessage = this.resEaiMsg.getStandardMessage(); + standardMessage.setBizData(this.resObject, this.outboundCharset); + this.logPssSno = 500; + logSenderCtrlSendRes(); + } // SA if (isSyncAsyncPattern()) { try { @@ -474,6 +482,18 @@ public abstract class DefaultProcess extends Process { return false; } } + + // jwhong + public boolean isAsyncSyncPattern() { + ServiceMessage firstService = this.reqEaiMsg.getSvcMsg(0); + ServiceMessage secondService = this.reqEaiMsg.getSvcMsg(1); + if (EAIMessageKeys.ASYNC_SVC.equals(this.reqEaiMsg.getSvcTsmtUsgTp()) + && EAIMessageKeys.SYNC_SVC.equals(firstService.getPsvItfTp()) + && EAIMessageKeys.ASYNC_SVC.equals(secondService.getPsvItfTp())){ + return true; + } + return false; + } public boolean isSyncAsyncPattern() { if (!MessageUtil.checkRspErrCd(this.resEaiMsg.getRspErrCd())) { From 5357eb8f635bcaccf6a76453664a825a73ff0d02 Mon Sep 17 00:00:00 2001 From: jaewohong Date: Tue, 16 Dec 2025 11:08:36 +0900 Subject: [PATCH 05/11] =?UTF-8?q?Niceon=20token=EB=B0=9C=EC=83=9D=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../impl/HttpClient5AdapterServiceRest.java | 87 ++++++++++++------- ...entAccessTokenServiceWithBase64Header.java | 14 +-- 2 files changed, 65 insertions(+), 36 deletions(-) diff --git a/src/main/java/com/eactive/eai/adapter/http/client/impl/HttpClient5AdapterServiceRest.java b/src/main/java/com/eactive/eai/adapter/http/client/impl/HttpClient5AdapterServiceRest.java index 86a47aa..fe84003 100644 --- a/src/main/java/com/eactive/eai/adapter/http/client/impl/HttpClient5AdapterServiceRest.java +++ b/src/main/java/com/eactive/eai/adapter/http/client/impl/HttpClient5AdapterServiceRest.java @@ -306,7 +306,8 @@ public class HttpClient5AdapterServiceRest extends HttpClient5AdapterServiceSupp accessToken = (OAuth2AccessTokenVO) tokenManager.getAccessTokenVO(vo.getAdapterGroupName()); // 토큰이 없거나 만료 됐으면 재발급 - if (accessToken == null || accessToken.isExpired()) { + //if (accessToken == null || accessToken.isExpired()) { + if (accessToken == null || accessToken.isExpired() || accessToken.getAccessToken() == null ) { // jwhong String oldToken = accessToken == null ? null : accessToken.getAccessToken(); accessToken = (OAuth2AccessTokenVO) tokenManager.retryAccessTokenVO(vo.getAdapterGroupName(), prop, oldToken); @@ -321,6 +322,9 @@ public class HttpClient5AdapterServiceRest extends HttpClient5AdapterServiceSupp String encryptAlgorithm = properties.getProperty("ENCRYPT_ALGORITHM"); // jwhong if (useAdapterToken && "AES256-NICEON".equals(encryptAlgorithm)) { String niceToken = accessToken.getAccessToken(); + if ( niceToken == null || niceToken == "") { + throw new Exception("NiceOn Token is empty, toke failed to be issued, TOKEN = [" + niceToken + "]"); + } String clientId = JwtClientIdExtractor(niceToken); long currentTime = System.currentTimeMillis(); auth = niceToken + ":" + currentTime + ":" + clientId ; @@ -827,46 +831,67 @@ public class HttpClient5AdapterServiceRest extends HttpClient5AdapterServiceSupp // map에 담기 Map map = new HashMap<>(); for (String entry : entries) { - String[] kv = entry.split("=", 2); - if (kv.length == 2) { - map.put(kv[0].trim(), kv[1].trim()); - } + String[] kv = entry.split("=", 2); + if (kv.length == 2) { + map.put(kv[0].trim(), kv[1].trim()); + } } for (String k : map.keySet()) { - if (k.equalsIgnoreCase("authorization")) { - authorization = map.get(k); - break; - } + if (k.equalsIgnoreCase("authorization")) { + authorization = map.get(k); + break; + } } - } + } } catch (Exception e) { - e.printStackTrace(); + e.printStackTrace(); } - + String jwtToken = ""; - + if (authorization != null && authorization.startsWith("Bearer ")) { - jwtToken = authorization.substring(7); // "Bearer " 이후의 JWT만 추출 + jwtToken = authorization.substring(7); // "Bearer " 이후의 JWT만 추출 } - + /* 업체정보 */ - String clientId = JwtClientIdExtractor(jwtToken); - method.setHeader("clientId", clientId); - - /* APIM 거래추적자 */ - String uuid = tempProp.getProperty(TransactionContextKeys.TRANSACTION_UUID, ""); - method.setHeader("traceId", uuid); - - /* GUID */ - String guid = tempProp.getProperty(TransactionContextKeys.GUID, ""); - method.setHeader("guid", guid); - - /* 최초요청시간 */ - String receivedTimestamp = tempProp.getProperty(HttpAdapterServiceKey.INBOUND_REQUESTED_TIME, ""); - method.setHeader("receivedTimestamp", receivedTimestamp); - - return jwtToken; + String clientId = JwtClientIdExtractor(jwtToken); + method.setHeader("clientId", clientId); + + /* APIM 거래추적자 */ + String uuid = tempProp.getProperty(TransactionContextKeys.TRANSACTION_UUID, ""); + method.setHeader("traceId", uuid); + + /* GUID */ + String guid = tempProp.getProperty(TransactionContextKeys.GUID, ""); + method.setHeader("guid", guid); + + /* 최초요청시간 */ + String receivedTimestamp = tempProp.getProperty(HttpAdapterServiceKey.INBOUND_REQUESTED_TIME, ""); + method.setHeader("receivedTimestamp", receivedTimestamp); + + return jwtToken; } + + private static Map parseInboundHeader(String str) { + java.util.HashMap map = new java.util.HashMap<>(); + if (str == null || str.isEmpty()) return map; + + // 중괄호 제거 + str = str.trim(); + if (str.startsWith("{") && str.endsWith("}")) { + str = str.substring(1, str.length() - 1); + } + + // key=value 쌍 분리 + String[] pairs = str.split(","); + for (String pair : pairs) { + String[] kv = pair.split("=", 2); + if (kv.length == 2) { + map.put(kv[0].trim(), kv[1].trim()); + } + } + return map; + } @SuppressWarnings("deprecation") private void assignPostBody(Object eaiBody, String contentType, String charset, HttpUriRequestBase method, String adapterName, String niceAuth, String inboundToken) { diff --git a/src/main/java/com/eactive/eai/authoutbound/client/impl/HttpClientAccessTokenServiceWithBase64Header.java b/src/main/java/com/eactive/eai/authoutbound/client/impl/HttpClientAccessTokenServiceWithBase64Header.java index 295c2f3..07f7864 100644 --- a/src/main/java/com/eactive/eai/authoutbound/client/impl/HttpClientAccessTokenServiceWithBase64Header.java +++ b/src/main/java/com/eactive/eai/authoutbound/client/impl/HttpClientAccessTokenServiceWithBase64Header.java @@ -114,9 +114,7 @@ public class HttpClientAccessTokenServiceWithBase64Header implements HttpClientA "uri:{}, charset:{}, transactionTimeout:{}, connectionTimeout:{}, useForwardProxy:{}, forwardProxyUrl:{}", uri, encode, timeout, connectionTimeout, useForwardProxy, forwardProxyUrl); - - - + // mTLS config with default connection parameters boolean useMtls = StringUtils.equalsIgnoreCase(adapterProp.getProperty(HttpClientAdapterServiceKey.USE_MTLS), "Y"); @@ -237,17 +235,19 @@ public class HttpClientAccessTokenServiceWithBase64Header implements HttpClientA logger.debug("contentType = [" + contentType + "]"); logger.debug("encode = [" + encode + "]"); + OAuth2AccessTokenVO accessToken = new OAuth2AccessTokenVO(); try (CloseableHttpResponse response = httpClient.execute(httpPost)) { if (response.getCode() != 200) { throw new Exception("OAuth token receive status fail value= " + response.getCode()); } + + logger.info("HttpClientAccessTokenServiceWithBase64Header==>" + response.getCode()); HttpEntity entity = response.getEntity(); String responseString = EntityUtils.toString(entity, encode); - logger.debug("oauthToken RECV = [" + responseString + "]"); + logger.debug("Base64Header oauthToken RECV = [" + responseString + "]"); if (StringUtils.isNotBlank(responseString)) { - OAuth2AccessTokenVO accessToken = new OAuth2AccessTokenVO(); ObjectMapper objectMapper = new ObjectMapper(); JsonNode responseJSON = objectMapper.readTree(responseString); @@ -272,6 +272,10 @@ public class HttpClientAccessTokenServiceWithBase64Header implements HttpClientA } else { throw new Exception("oauth token return null"); } + } catch (Exception e) { + e.printStackTrace(); + logger.error("[HttpClientAccessTokenServiceWithBase64Header] retrieve accessToken exception :" + e.getMessage()); + return accessToken; } } } From 037f857a5f79fa7ad7f63d14979fb744cfc59708 Mon Sep 17 00:00:00 2001 From: jaewohong Date: Tue, 16 Dec 2025 11:08:54 +0900 Subject: [PATCH 06/11] =?UTF-8?q?bucket=20=EC=9C=A0=EB=9F=89=EC=A0=9C?= =?UTF-8?q?=EC=96=B4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/eactive/eai/common/inflow/InflowControlDAO.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/com/eactive/eai/common/inflow/InflowControlDAO.java b/src/main/java/com/eactive/eai/common/inflow/InflowControlDAO.java index 8ff7b3a..9cbd12e 100644 --- a/src/main/java/com/eactive/eai/common/inflow/InflowControlDAO.java +++ b/src/main/java/com/eactive/eai/common/inflow/InflowControlDAO.java @@ -26,6 +26,7 @@ import com.eactive.eai.data.entity.onl.inflow.InflowControlLogId; @Service @Transactional public class InflowControlDAO extends BaseDAO { + private static Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT); @Autowired @@ -227,4 +228,5 @@ public class InflowControlDAO extends BaseDAO { vo.setActivate(!"0".equals(group.getUseYn())); return vo; } + } \ No newline at end of file From 9890f889a41f7a967bad67efb4091b4393141aed Mon Sep 17 00:00:00 2001 From: jaewohong Date: Tue, 16 Dec 2025 17:55:42 +0900 Subject: [PATCH 07/11] =?UTF-8?q?NiceOn=20token=20=EA=B4=80=EB=A0=A8?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../impl/HttpClient5AdapterServiceRest.java | 2 +- ...entAccessTokenServiceWithBase64Header.java | 3 +- ...entAccessTokenServiceWithBase64NiceOn.java | 298 ++++++++++++++++++ 3 files changed, 300 insertions(+), 3 deletions(-) create mode 100644 src/main/java/com/eactive/eai/authoutbound/client/impl/HttpClientAccessTokenServiceWithBase64NiceOn.java diff --git a/src/main/java/com/eactive/eai/adapter/http/client/impl/HttpClient5AdapterServiceRest.java b/src/main/java/com/eactive/eai/adapter/http/client/impl/HttpClient5AdapterServiceRest.java index fe84003..2491d51 100644 --- a/src/main/java/com/eactive/eai/adapter/http/client/impl/HttpClient5AdapterServiceRest.java +++ b/src/main/java/com/eactive/eai/adapter/http/client/impl/HttpClient5AdapterServiceRest.java @@ -323,7 +323,7 @@ public class HttpClient5AdapterServiceRest extends HttpClient5AdapterServiceSupp if (useAdapterToken && "AES256-NICEON".equals(encryptAlgorithm)) { String niceToken = accessToken.getAccessToken(); if ( niceToken == null || niceToken == "") { - throw new Exception("NiceOn Token is empty, toke failed to be issued, TOKEN = [" + niceToken + "]"); + throw new Exception("NiceOn Token is empty, TOKEN failed to be issued, TOKEN = [" + niceToken + "]"); } String clientId = JwtClientIdExtractor(niceToken); long currentTime = System.currentTimeMillis(); diff --git a/src/main/java/com/eactive/eai/authoutbound/client/impl/HttpClientAccessTokenServiceWithBase64Header.java b/src/main/java/com/eactive/eai/authoutbound/client/impl/HttpClientAccessTokenServiceWithBase64Header.java index 07f7864..30ef683 100644 --- a/src/main/java/com/eactive/eai/authoutbound/client/impl/HttpClientAccessTokenServiceWithBase64Header.java +++ b/src/main/java/com/eactive/eai/authoutbound/client/impl/HttpClientAccessTokenServiceWithBase64Header.java @@ -96,8 +96,7 @@ public class HttpClientAccessTokenServiceWithBase64Header implements HttpClientA if (!UrlUtils.isAbsoluteUrl(uri)) { uri = appendPath(adapterUrl, uri); } - //String contentType = "application/json;"; // comment by jwhong - String contentType = "application/x-www-form-urlencoded"; // 광주은행 나이스지키미 + String contentType = "application/json;"; Charset charset; if (StringUtils.isNotBlank(encode)) { diff --git a/src/main/java/com/eactive/eai/authoutbound/client/impl/HttpClientAccessTokenServiceWithBase64NiceOn.java b/src/main/java/com/eactive/eai/authoutbound/client/impl/HttpClientAccessTokenServiceWithBase64NiceOn.java new file mode 100644 index 0000000..51d0382 --- /dev/null +++ b/src/main/java/com/eactive/eai/authoutbound/client/impl/HttpClientAccessTokenServiceWithBase64NiceOn.java @@ -0,0 +1,298 @@ + +package com.eactive.eai.authoutbound.client.impl; + +import com.eactive.eai.adapter.AdapterGroupVO; +import com.eactive.eai.adapter.AdapterManager; +import com.eactive.eai.adapter.http.client.HttpClientAdapterServiceKey; +import com.eactive.eai.adapter.http.secure.HttpClient5SSLContextFactory; +import com.eactive.eai.authoutbound.OutboundOAuthCredentialVo; +import com.eactive.eai.authoutbound.client.HttpClientAccessTokenServiceByDB; +import com.eactive.eai.common.util.Logger; +import com.eactive.eai.httpouttlsinfo.HttpOutTlsInfoManager; +import com.eactive.eai.httpouttlsinfo.HttpOutTlsInfoVO; +import com.eactive.eai.util.TestModeChecker; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.openbanking.eai.common.token.AccessTokenVO; +import com.openbanking.eai.common.token.OAuth2AccessTokenVO; +import org.apache.commons.lang3.StringUtils; +import org.apache.hc.client5.http.classic.methods.HttpPost; +import org.apache.hc.client5.http.config.RequestConfig; +import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.hc.client5.http.impl.classic.HttpClients; +import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager; +import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder; +import org.apache.hc.client5.http.ssl.NoopHostnameVerifier; +import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory; +import org.apache.hc.client5.http.ssl.TrustAllStrategy; +import org.apache.hc.core5.http.ContentType; +import org.apache.hc.core5.http.HttpEntity; +import org.apache.hc.core5.http.HttpHost; +import org.apache.hc.core5.http.NameValuePair; +import org.apache.hc.core5.http.io.entity.EntityUtils; +import org.apache.hc.core5.http.io.entity.StringEntity; +import org.apache.hc.core5.http.message.BasicNameValuePair; +import org.apache.hc.core5.ssl.SSLContextBuilder; +import org.apache.hc.core5.util.Timeout; +import org.springframework.security.web.util.UrlUtils; +import tcseed.base64; + +import javax.net.ssl.SSLContext; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; +import java.util.*; +import org.apache.hc.client5.http.entity.UrlEncodedFormEntity; + +/** + * 1. 기능 : HTTP 웹 컴포넌트를 POST 방식으로 호출할 수 있는 기능을 제공한다. 2. 처리 개요 : * - 2009.11.24 + * retry 로직 제거 : 요청 3. 주의사항 + * + * @author : + * @version : v 1.0.0 + * @see : WLIAdapterFactory.java, WLIAdapter.java, WLIDefaultAdapter.java + * @since : + * + */ +public class HttpClientAccessTokenServiceWithBase64NiceOn implements HttpClientAccessTokenServiceByDB { + + public static Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT); + + private static boolean testMode = TestModeChecker.isTestMode(); + + /** + * 1. 기능 : 법인검증 토큰 발급에 사용 2. 처리 개요 : - 속성 정보를 설정 하고 토큰 발급 URL 호출 한다. 3. 주의사항 + * - JSON 방식 + * - ( Header Authorization Basic ) 으로 구성 + * @param adapterProp Http Adapter 속성 정보 + * @return 반환 된 AccessTokenVO + * @exception Exception 수동 시스템 간 통신 중 발생 + **/ + public AccessTokenVO execute(String name, Properties adapterProp, OutboundOAuthCredentialVo oAuthCredentialVo) + throws Exception { + AdapterGroupVO gvo = AdapterManager.getInstance().getAdapterGroupVO(name); + String adapterUrl = adapterProp.getProperty("URL"); + String encode = gvo.getMessageEncode(); + String timeoutTemp = adapterProp.getProperty("HTTP_TIME_OUT"); + if (StringUtils.isBlank(timeoutTemp)) { + timeoutTemp = "30000"; + } + String connectionTimeoutTemp = adapterProp.getProperty("CONNECTION_TIMEOUT"); + if (StringUtils.isBlank(connectionTimeoutTemp)) { + connectionTimeoutTemp = "30000"; + } + + int timeout = Integer.parseInt(timeoutTemp); + int connectionTimeout = Integer.parseInt(connectionTimeoutTemp); + long currentTime = System.currentTimeMillis(); + + String uri = oAuthCredentialVo.getUrl(); + + if (!UrlUtils.isAbsoluteUrl(uri)) { + uri = appendPath(adapterUrl, uri); + } + String contentType = "application/x-www-form-urlencoded"; // 광주은행 나이스지키미 + + Charset charset; + if (StringUtils.isNotBlank(encode)) { + charset = Charset.forName(encode); + } else { + charset = Charset.defaultCharset(); + encode = charset.toString(); + } + + boolean useForwardProxy = StringUtils.equalsIgnoreCase(adapterProp.getProperty("FORWARD_PROXY_USE_YN"), "Y"); + String forwardProxyUrl = adapterProp.getProperty("FORWARD_PROXY_URL"); + + logger.debug( + "uri:{}, charset:{}, transactionTimeout:{}, connectionTimeout:{}, useForwardProxy:{}, forwardProxyUrl:{}", + uri, encode, timeout, connectionTimeout, useForwardProxy, forwardProxyUrl); + + List formParams = new ArrayList<>(); + formParams.add(new BasicNameValuePair("client_id", oAuthCredentialVo.getClientId())); + formParams.add(new BasicNameValuePair("client_secret", oAuthCredentialVo.getClientSecret())); + formParams.add(new BasicNameValuePair("scope", oAuthCredentialVo.getScope())); + formParams.add(new BasicNameValuePair("grant_type", oAuthCredentialVo.getGrantType())); + + // mTLS config with default connection parameters + boolean useMtls = StringUtils.equalsIgnoreCase(adapterProp.getProperty(HttpClientAdapterServiceKey.USE_MTLS), + "Y"); + AdapterGroupVO adapterGroup = AdapterManager.getInstance().getAdapterGroup(name); + String clientId = adapterGroup.getClientId(); + + if (logger.isInfo()) { + logger.info("MTLS adapterGroupName. : {}, useMtls. : {}, clientId : {}", name, useMtls, clientId); + } + + int maxTotalConnections = HttpClientAdapterServiceKey.DEFAULT_MAX_TOTAL_CONNECTIONS; + int maxHostConnections = HttpClientAdapterServiceKey.DEFAULT_MAX_CONNECTION_PER_HOST; + + HttpOutTlsInfoVO mtlsInfo = null; + SSLContext sslContext = null; + PoolingHttpClientConnectionManagerBuilder cmBuilder = PoolingHttpClientConnectionManagerBuilder.create(); + PoolingHttpClientConnectionManager connectionManager = null; + + try { + if (useMtls) { + HttpOutTlsInfoManager tlsManager = HttpOutTlsInfoManager.getInstance(); + if (StringUtils.isNotEmpty(clientId)) { + mtlsInfo = tlsManager.getHttpOutTlsInfo(clientId); + } + } + + if (useMtls && mtlsInfo != null) { + String storeType = mtlsInfo.getStoreType(); + String keyStoreInfo = mtlsInfo.getKeystoreInfo(); + String keyStorePassword = mtlsInfo.getKeystorePassword(); + String trustStoreInfo = mtlsInfo.getTruststoreInfo(); + String trustStorePassword = mtlsInfo.getTruststorePassword(); + + String[] tlsVersions = null; + String[] cipherSuites = null; + + if (StringUtils.isAnyEmpty(keyStoreInfo, keyStorePassword)) { + throw new Exception("mTLS keyStore config error"); + } + + boolean skipTrust = false; + if (StringUtils.isAnyEmpty(trustStoreInfo, trustStorePassword)) { + if (logger.isWarn()) + logger.warn("Skip trustStore validation adapterGroupName : " + name); + skipTrust = true; + } + + sslContext = HttpClient5SSLContextFactory.createMTLSContextFromContent(storeType, keyStoreInfo, + keyStorePassword, trustStoreInfo, trustStorePassword, skipTrust, tlsVersions, cipherSuites); + + SSLConnectionSocketFactory sslSocketFactory = null; + if (testMode) { + sslSocketFactory = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE); + } else { + sslSocketFactory = new SSLConnectionSocketFactory(sslContext); + } + cmBuilder.setSSLSocketFactory(sslSocketFactory); + } else { + sslContext = SSLContextBuilder.create().loadTrustMaterial(new TrustAllStrategy()).build(); + SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext); + cmBuilder.setSSLSocketFactory(csf); + } + } catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException | CertificateException + | IOException | UnrecoverableKeyException e) { + throw new RuntimeException(e); + } catch (Exception e) { + throw new RuntimeException(e); + } + + connectionManager = cmBuilder.build(); + connectionManager.setMaxTotal(maxTotalConnections); + connectionManager.setDefaultMaxPerRoute(maxHostConnections); + + try (CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(connectionManager).build();) { + HttpPost httpPost = new HttpPost(uri); + httpPost.setEntity(new UrlEncodedFormEntity(formParams, charset)); + httpPost.setHeader("Content-Type", contentType+" charset=" + encode); + + //HTTP HEADER에 client id, client secret를 base64Encode 한 후 추가 + String cId = oAuthCredentialVo.getClientId(); + String cSecret = oAuthCredentialVo.getClientSecret(); + String authValue = cId + ":" + cSecret; + String encodedAuth = Base64.getEncoder().encodeToString(authValue.getBytes(StandardCharsets.UTF_8)); + httpPost.setHeader("Authorization","Basic "+ encodedAuth); + + + RequestConfig.Builder requestConfigBuilder = RequestConfig.custom(); + + if (useForwardProxy) { + java.net.URL url = new java.net.URL(forwardProxyUrl); + + // 프로토콜, 호스트, 포트 추출 + String protocol = url.getProtocol(); + String host = url.getHost(); + int port = url.getPort(); + HttpHost proxy = new HttpHost(protocol, host, port); + requestConfigBuilder.setProxy(proxy); + } + + RequestConfig requestConfig = requestConfigBuilder + .setConnectTimeout(Timeout.ofMilliseconds(connectionTimeout)) + .setResponseTimeout(Timeout.ofMilliseconds(timeout)).build(); + httpPost.setConfig(requestConfig); + + logger.debug("uri = [" + uri + "]"); + logger.debug("oauthClientId = [" + oAuthCredentialVo.getClientId() + "]"); + logger.debug("oauthClientSecret = [" + oAuthCredentialVo.getClientSecret() + "]"); + logger.debug("oauthScope = [" + oAuthCredentialVo.getScope() + "]"); + logger.debug("oauthGrantType = [" + oAuthCredentialVo.getGrantType() + "]"); + logger.debug("oauthauthValue = [" + authValue + "]"); + logger.debug("oauthauthEncodedAuth = [" + encodedAuth + "]"); + logger.debug("contentType = [" + contentType + "]"); + logger.debug("encode = [" + encode + "]"); + + OAuth2AccessTokenVO accessToken = new OAuth2AccessTokenVO(); + try (CloseableHttpResponse response = httpClient.execute(httpPost)) { + if (response.getCode() != 200) { + throw new Exception("NiceOn token receive status fail value= " + response.getCode()); + } + + logger.info("HttpClientAccessTokenServiceWithBase64NiceOn==>" + response.getCode()); + + HttpEntity entity = response.getEntity(); + String responseString = EntityUtils.toString(entity, encode); + logger.debug("Base64NiceOn oauthToken RECV = [" + responseString + "]"); + + if (StringUtils.isNotBlank(responseString)) { + + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode responseJSON = objectMapper.readTree(responseString); + + if (responseJSON.has("access_token") && responseJSON.has("token_type") + && responseJSON.has("expires_in") && responseJSON.has("scope")) { + accessToken.setAccessToken(responseJSON.get("access_token").asText()); + accessToken.setTokenType(responseJSON.get("token_type").asText()); + + long expiresIn = responseJSON.get("expires_in").asLong(); + accessToken.setExpiration(new Date(currentTime + expiresIn * 1000L)); + accessToken.setScope(responseJSON.get("scope").asText()); + } + + if (responseJSON.has("client_use_code")) { + accessToken.setClientUseCode(responseJSON.get("client_use_code").asText()); + } + + logger.debug("oauthToken =" + accessToken.toString()); + + return accessToken; + } else { + throw new Exception("oauth token return null"); + } + } catch (Exception e) { + e.printStackTrace(); + logger.error("[HttpClientAccessTokenServiceWithBase64NiceOn] retrieve accessToken exception :" + e.getMessage()); + return accessToken; + } + } + } + + /** + * URL에 경로를 추가합니다. 중복되는 슬래시를 방지합니다. + * + * @param baseUrl 기본 URL + * @param pathToAdd 추가할 경로 + * @return 완성된 URL 문자열 + */ + public static String appendPath(String baseUrl, String pathToAdd) { + if (!baseUrl.endsWith("/") && !pathToAdd.startsWith("/")) { + return baseUrl + "/" + pathToAdd; + } else if (baseUrl.endsWith("/") && pathToAdd.startsWith("/")) { + return baseUrl + pathToAdd.substring(1); + } else { + return baseUrl + pathToAdd; + } + } +} From 02b9717890b47c2eb8a43d5d02ce5a154ea23af3 Mon Sep 17 00:00:00 2001 From: jaewohong Date: Wed, 17 Dec 2025 10:20:38 +0900 Subject: [PATCH 08/11] =?UTF-8?q?NICEON=20Token=20=ED=9A=8D=EB=93=9D=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../impl/HttpClientAccessTokenServiceWithBase64NiceOn.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/eactive/eai/authoutbound/client/impl/HttpClientAccessTokenServiceWithBase64NiceOn.java b/src/main/java/com/eactive/eai/authoutbound/client/impl/HttpClientAccessTokenServiceWithBase64NiceOn.java index 51d0382..d960c82 100644 --- a/src/main/java/com/eactive/eai/authoutbound/client/impl/HttpClientAccessTokenServiceWithBase64NiceOn.java +++ b/src/main/java/com/eactive/eai/authoutbound/client/impl/HttpClientAccessTokenServiceWithBase64NiceOn.java @@ -120,6 +120,9 @@ public class HttpClientAccessTokenServiceWithBase64NiceOn implements HttpClientA formParams.add(new BasicNameValuePair("scope", oAuthCredentialVo.getScope())); formParams.add(new BasicNameValuePair("grant_type", oAuthCredentialVo.getGrantType())); + String postParameters = "client_id=" + oAuthCredentialVo.getClientId() + "&client_secret=" + oAuthCredentialVo.getClientSecret() + "&scope=" + oAuthCredentialVo.getScope() + "&grant_type=" + oAuthCredentialVo.getGrantType(); + + // mTLS config with default connection parameters boolean useMtls = StringUtils.equalsIgnoreCase(adapterProp.getProperty(HttpClientAdapterServiceKey.USE_MTLS), "Y"); @@ -195,7 +198,8 @@ public class HttpClientAccessTokenServiceWithBase64NiceOn implements HttpClientA try (CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(connectionManager).build();) { HttpPost httpPost = new HttpPost(uri); - httpPost.setEntity(new UrlEncodedFormEntity(formParams, charset)); + //httpPost.setEntity(new UrlEncodedFormEntity(formParams, charset)); + httpPost.setEntity(new StringEntity(postParameters, StandardCharsets.UTF_8)); httpPost.setHeader("Content-Type", contentType+" charset=" + encode); //HTTP HEADER에 client id, client secret를 base64Encode 한 후 추가 From 68aac38f11f28d91defb94d4c9d068561f86ca04 Mon Sep 17 00:00:00 2001 From: jaewohong Date: Thu, 18 Dec 2025 09:36:53 +0900 Subject: [PATCH 09/11] =?UTF-8?q?Niceon=20=EC=95=94=ED=98=B8=ED=99=94=20ke?= =?UTF-8?q?y=20=EC=83=9D=EC=84=B1=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../impl/HttpClient5AdapterServiceRest.java | 50 +++++++++++++++---- .../http/dynamic/HttpAdapterServiceKey.java | 1 + ...entAccessTokenServiceWithBase64NiceOn.java | 48 ++++++++++-------- .../eai/common/token/OAuth2AccessTokenVO.java | 10 ++++ 4 files changed, 78 insertions(+), 31 deletions(-) diff --git a/src/main/java/com/eactive/eai/adapter/http/client/impl/HttpClient5AdapterServiceRest.java b/src/main/java/com/eactive/eai/adapter/http/client/impl/HttpClient5AdapterServiceRest.java index 2491d51..50eb1f9 100644 --- a/src/main/java/com/eactive/eai/adapter/http/client/impl/HttpClient5AdapterServiceRest.java +++ b/src/main/java/com/eactive/eai/adapter/http/client/impl/HttpClient5AdapterServiceRest.java @@ -91,7 +91,7 @@ import com.kjbank.encrypt.exchange.crypto.AES256GCMCipher; import com.kjbank.encrypt.exchange.crypto.ECDHESA256Cipher; import com.eactive.eai.authserver.service.OAuth2Manager; import java.util.Set; - +import java.util.HashSet; /** * 1. 기능 : EAI HTTP OutBound 용 어댑터로 수동 시스템의 HTTP 웹 컴포넌트를 GET/POST 방식으로 호출할 수 있는 @@ -325,9 +325,10 @@ public class HttpClient5AdapterServiceRest extends HttpClient5AdapterServiceSupp if ( niceToken == null || niceToken == "") { throw new Exception("NiceOn Token is empty, TOKEN failed to be issued, TOKEN = [" + niceToken + "]"); } - String clientId = JwtClientIdExtractor(niceToken); + String clientId = accessToken.getClientId(); // HttpClientAccessTokenServiceWithBase64NiceOn 서 넣어줌 long currentTime = System.currentTimeMillis(); auth = niceToken + ":" + currentTime + ":" + clientId ; + logger.debug("HttpClientAdapterServiceRest] NiceOn auth = [" + auth + "]"); auth = Base64.getEncoder().encodeToString(auth.getBytes("UTF-8")); setNiceOnAuthHeaders(method, auth); @@ -853,21 +854,50 @@ public class HttpClient5AdapterServiceRest extends HttpClient5AdapterServiceSupp jwtToken = authorization.substring(7); // "Bearer " 이후의 JWT만 추출 } + + String headerRelay = PropManager.getInstance().getProperty("HEADER_RELAY_ADAPTER","adapter.name"); + Set values = new HashSet<>(Arrays.asList(headerRelay.split(","))); + String inputAdapter = tempProp.getProperty(HttpAdapterServiceKey.OUT_ADAPTER_GROUP_NAME, ""); + + String[] parts = inputAdapter.split("_"); + String target = parts[1]; // adapter name의 앞의 3자리 + String adapterName = target.substring(0, 3); + + if (values.contains(adapterName)) { + + /* 업체정보 */ + String clientId = JwtClientIdExtractor(jwtToken); + method.setHeader("clientId", clientId); + + /* APIM 거래추적자 */ + String uuid = tempProp.getProperty(TransactionContextKeys.TRANSACTION_UUID, ""); + method.setHeader("traceId", uuid); + + /* GUID */ + String guid = tempProp.getProperty(TransactionContextKeys.GUID, ""); + method.setHeader("guid", guid); + + /* 최초요청시간 */ + String receivedTimestamp = tempProp.getProperty(HttpAdapterServiceKey.INBOUND_REQUESTED_TIME, ""); + method.setHeader("receivedTimestamp", receivedTimestamp); + } + + /* 업체정보 */ - String clientId = JwtClientIdExtractor(jwtToken); - method.setHeader("clientId", clientId); + //String clientId = JwtClientIdExtractor(jwtToken); + //method.setHeader("clientId", clientId); /* APIM 거래추적자 */ - String uuid = tempProp.getProperty(TransactionContextKeys.TRANSACTION_UUID, ""); - method.setHeader("traceId", uuid); + //String uuid = tempProp.getProperty(TransactionContextKeys.TRANSACTION_UUID, ""); + //method.setHeader("traceId", uuid); /* GUID */ - String guid = tempProp.getProperty(TransactionContextKeys.GUID, ""); - method.setHeader("guid", guid); + //String guid = tempProp.getProperty(TransactionContextKeys.GUID, ""); + //method.setHeader("guid", guid); /* 최초요청시간 */ - String receivedTimestamp = tempProp.getProperty(HttpAdapterServiceKey.INBOUND_REQUESTED_TIME, ""); - method.setHeader("receivedTimestamp", receivedTimestamp); + //String receivedTimestamp = tempProp.getProperty(HttpAdapterServiceKey.INBOUND_REQUESTED_TIME, ""); + //method.setHeader("receivedTimestamp", receivedTimestamp); return jwtToken; } diff --git a/src/main/java/com/eactive/eai/adapter/http/dynamic/HttpAdapterServiceKey.java b/src/main/java/com/eactive/eai/adapter/http/dynamic/HttpAdapterServiceKey.java index 9fdc548..cbc6ad4 100644 --- a/src/main/java/com/eactive/eai/adapter/http/dynamic/HttpAdapterServiceKey.java +++ b/src/main/java/com/eactive/eai/adapter/http/dynamic/HttpAdapterServiceKey.java @@ -80,6 +80,7 @@ public interface HttpAdapterServiceKey { static final String INBOUND_REQUESTED_TIME = "INBOUND_REQUESTED_TIME"; // jwhong static final String GUID = "GUID"; // jwhong static final String API_SERVICE_CODE = "API_SERVICE_CODE"; // jwhong + static final String OUT_ADAPTER_GROUP_NAME = "OUT_ADAPTER_GROUP_NAME"; // jwhong //응답 처리 용 표준 전문 오브젝트 static final String STANDARD_MESSAGE_OBJECT = "STANDARD_MESSAGE_OBJECT"; diff --git a/src/main/java/com/eactive/eai/authoutbound/client/impl/HttpClientAccessTokenServiceWithBase64NiceOn.java b/src/main/java/com/eactive/eai/authoutbound/client/impl/HttpClientAccessTokenServiceWithBase64NiceOn.java index d960c82..88cf398 100644 --- a/src/main/java/com/eactive/eai/authoutbound/client/impl/HttpClientAccessTokenServiceWithBase64NiceOn.java +++ b/src/main/java/com/eactive/eai/authoutbound/client/impl/HttpClientAccessTokenServiceWithBase64NiceOn.java @@ -115,12 +115,11 @@ public class HttpClientAccessTokenServiceWithBase64NiceOn implements HttpClientA uri, encode, timeout, connectionTimeout, useForwardProxy, forwardProxyUrl); List formParams = new ArrayList<>(); - formParams.add(new BasicNameValuePair("client_id", oAuthCredentialVo.getClientId())); - formParams.add(new BasicNameValuePair("client_secret", oAuthCredentialVo.getClientSecret())); - formParams.add(new BasicNameValuePair("scope", oAuthCredentialVo.getScope())); + //formParams.add(new BasicNameValuePair("client_id", oAuthCredentialVo.getClientId())); + //formParams.add(new BasicNameValuePair("client_secret", oAuthCredentialVo.getClientSecret())); formParams.add(new BasicNameValuePair("grant_type", oAuthCredentialVo.getGrantType())); - - String postParameters = "client_id=" + oAuthCredentialVo.getClientId() + "&client_secret=" + oAuthCredentialVo.getClientSecret() + "&scope=" + oAuthCredentialVo.getScope() + "&grant_type=" + oAuthCredentialVo.getGrantType(); + formParams.add(new BasicNameValuePair("scope", oAuthCredentialVo.getScope())); + //String postParameters = "grant_type=" + oAuthCredentialVo.getGrantType() + "&scope=" + oAuthCredentialVo.getScope(); // mTLS config with default connection parameters @@ -198,9 +197,9 @@ public class HttpClientAccessTokenServiceWithBase64NiceOn implements HttpClientA try (CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(connectionManager).build();) { HttpPost httpPost = new HttpPost(uri); - //httpPost.setEntity(new UrlEncodedFormEntity(formParams, charset)); - httpPost.setEntity(new StringEntity(postParameters, StandardCharsets.UTF_8)); - httpPost.setHeader("Content-Type", contentType+" charset=" + encode); + httpPost.setEntity(new UrlEncodedFormEntity(formParams, charset)); + //httpPost.setEntity(new StringEntity(postParameters, StandardCharsets.UTF_8)); + httpPost.setHeader("Content-Type", contentType+";charset=" + encode); // 중간에 ; 주어야 함 //HTTP HEADER에 client id, client secret를 base64Encode 한 후 추가 String cId = oAuthCredentialVo.getClientId(); @@ -228,32 +227,38 @@ public class HttpClientAccessTokenServiceWithBase64NiceOn implements HttpClientA .setResponseTimeout(Timeout.ofMilliseconds(timeout)).build(); httpPost.setConfig(requestConfig); - logger.debug("uri = [" + uri + "]"); - logger.debug("oauthClientId = [" + oAuthCredentialVo.getClientId() + "]"); - logger.debug("oauthClientSecret = [" + oAuthCredentialVo.getClientSecret() + "]"); - logger.debug("oauthScope = [" + oAuthCredentialVo.getScope() + "]"); - logger.debug("oauthGrantType = [" + oAuthCredentialVo.getGrantType() + "]"); - logger.debug("oauthauthValue = [" + authValue + "]"); + logger.debug("uri = [" + uri + "]"); + logger.debug("oauthClientId = [" + oAuthCredentialVo.getClientId() + "]"); + logger.debug("oauthClientSecret = [" + oAuthCredentialVo.getClientSecret() + "]"); + logger.debug("oauthScope = [" + oAuthCredentialVo.getScope() + "]"); + logger.debug("oauthGrantType = [" + oAuthCredentialVo.getGrantType() + "]"); + logger.debug("oauthauthValue = [" + authValue + "]"); logger.debug("oauthauthEncodedAuth = [" + encodedAuth + "]"); - logger.debug("contentType = [" + contentType + "]"); - logger.debug("encode = [" + encode + "]"); + logger.debug("contentType = [" + contentType + "]"); + logger.debug("encode = [" + encode + "]"); OAuth2AccessTokenVO accessToken = new OAuth2AccessTokenVO(); try (CloseableHttpResponse response = httpClient.execute(httpPost)) { if (response.getCode() != 200) { - throw new Exception("NiceOn token receive status fail value= " + response.getCode()); + throw new Exception("NiceOn token receive status fail value== " + response.getCode()); } logger.info("HttpClientAccessTokenServiceWithBase64NiceOn==>" + response.getCode()); HttpEntity entity = response.getEntity(); String responseString = EntityUtils.toString(entity, encode); - logger.debug("Base64NiceOn oauthToken RECV = [" + responseString + "]"); + logger.debug("Base64NiceOn oauthToken RECV == [" + responseString + "]"); if (StringUtils.isNotBlank(responseString)) { ObjectMapper objectMapper = new ObjectMapper(); - JsonNode responseJSON = objectMapper.readTree(responseString); + JsonNode responseJSONRtn = objectMapper.readTree(responseString); + JsonNode responseJSON = null; + String gwRsltCd = responseJSONRtn.get("dataHeader").get("GW_RSLT_CD").asText(); + + if ("1200".equals(gwRsltCd)) { + responseJSON = responseJSONRtn.get("dataBody"); + } if (responseJSON.has("access_token") && responseJSON.has("token_type") && responseJSON.has("expires_in") && responseJSON.has("scope")) { @@ -263,13 +268,14 @@ public class HttpClientAccessTokenServiceWithBase64NiceOn implements HttpClientA long expiresIn = responseJSON.get("expires_in").asLong(); accessToken.setExpiration(new Date(currentTime + expiresIn * 1000L)); accessToken.setScope(responseJSON.get("scope").asText()); + accessToken.setClientId(cId); } if (responseJSON.has("client_use_code")) { accessToken.setClientUseCode(responseJSON.get("client_use_code").asText()); } - logger.debug("oauthToken =" + accessToken.toString()); + logger.debug("NiceOn oauthToken =" + accessToken.toString()); return accessToken; } else { @@ -277,7 +283,7 @@ public class HttpClientAccessTokenServiceWithBase64NiceOn implements HttpClientA } } catch (Exception e) { e.printStackTrace(); - logger.error("[HttpClientAccessTokenServiceWithBase64NiceOn] retrieve accessToken exception :" + e.getMessage()); + logger.error("[HttpClientAccessTokenServiceWithBase64NiceOn] retrieve accessToken exception :" + e.getMessage()); return accessToken; } } diff --git a/src/main/java/com/openbanking/eai/common/token/OAuth2AccessTokenVO.java b/src/main/java/com/openbanking/eai/common/token/OAuth2AccessTokenVO.java index 78d4fb5..920121c 100644 --- a/src/main/java/com/openbanking/eai/common/token/OAuth2AccessTokenVO.java +++ b/src/main/java/com/openbanking/eai/common/token/OAuth2AccessTokenVO.java @@ -24,6 +24,7 @@ public class OAuth2AccessTokenVO implements AccessTokenVO, Serializable private Date expiration; private String scope; private String clientUseCode; + private String clientId; public OAuth2AccessTokenVO() { @@ -35,6 +36,7 @@ public class OAuth2AccessTokenVO implements AccessTokenVO, Serializable this.expiration = oAuth2AccessTokenVO.getExpiration(); this.scope = oAuth2AccessTokenVO.getScope(); this.clientUseCode = oAuth2AccessTokenVO.getClientUseCode(); + this.clientId = oAuth2AccessTokenVO.getClientId(); } public String getAccessToken() { @@ -92,6 +94,14 @@ public class OAuth2AccessTokenVO implements AccessTokenVO, Serializable public void setClientUseCode(String clientUseCode) { this.clientUseCode = clientUseCode; } + + public String getClientId() { + return clientId; + } + + public void setClientId(String clientId) { + this.clientId = clientId; + } public String toString() { return ReflectionToStringBuilder.toString(this, ToStringStyle.DEFAULT_STYLE); From c48a30acb46aaa0cfa0b46ed212ebf6e83c9ff5f Mon Sep 17 00:00:00 2001 From: "Yunsam.Eo" Date: Thu, 18 Dec 2025 15:30:03 +0900 Subject: [PATCH 10/11] =?UTF-8?q?doPostFilter=20:=20response=20Header=20Ke?= =?UTF-8?q?y=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../adapter/http/dynamic/filter/HmacSha256VerifyFilterKjb.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/com/eactive/eai/adapter/http/dynamic/filter/HmacSha256VerifyFilterKjb.java b/src/main/java/com/eactive/eai/adapter/http/dynamic/filter/HmacSha256VerifyFilterKjb.java index a908c2d..4cd3bcf 100644 --- a/src/main/java/com/eactive/eai/adapter/http/dynamic/filter/HmacSha256VerifyFilterKjb.java +++ b/src/main/java/com/eactive/eai/adapter/http/dynamic/filter/HmacSha256VerifyFilterKjb.java @@ -19,6 +19,7 @@ import com.eactive.eai.adapter.http.dynamic.HttpAdapterServiceKey; import com.eactive.eai.adapter.http.dynamic.HttpAdapterServiceSupport; import com.eactive.eai.authserver.service.OAuth2Manager; import com.eactive.eai.authserver.vo.ClientVO; +import com.eactive.eai.common.TransactionContextKeys; import com.eactive.eai.common.util.Logger; public class HmacSha256VerifyFilterKjb implements HttpAdapterFilter { @@ -139,6 +140,8 @@ public class HmacSha256VerifyFilterKjb implements HttpAdapterFilter { String macBody = calculateHMAC(outboundBody, clientSecret); response.addHeader("x-obp-signature-body", macBody); + response.addHeader("x-obp-partnercode", request.getHeader("x-obp-partnercode")); + response.addHeader("x-obp-txid", prop.getProperty(TransactionContextKeys.TRANSACTION_UUID,"")); } catch (Exception e) { logger.error(e.getMessage()); } From 204953227c9765216ae8e056f0f4bbab832849b5 Mon Sep 17 00:00:00 2001 From: "Yunsam.Eo" Date: Thu, 18 Dec 2025 15:31:23 +0900 Subject: [PATCH 11/11] =?UTF-8?q?getApiScopeRelationsByApiId=20return?= =?UTF-8?q?=EA=B0=92=20=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95(=EC=98=A4?= =?UTF-8?q?=ED=98=95=EC=84=9D=EC=9D=B4=EC=82=AC=20=EA=B0=80=EC=9D=B4?= =?UTF-8?q?=EB=93=9C)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/eactive/eai/authserver/dao/ScopeDAO.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/eactive/eai/authserver/dao/ScopeDAO.java b/src/main/java/com/eactive/eai/authserver/dao/ScopeDAO.java index 3cae28a..d24d681 100644 --- a/src/main/java/com/eactive/eai/authserver/dao/ScopeDAO.java +++ b/src/main/java/com/eactive/eai/authserver/dao/ScopeDAO.java @@ -54,7 +54,7 @@ public class ScopeDAO extends BaseDAO { ScopeEntity scopeEntity = scopeLoader.findByIdBzwksvckeyname(apiId); - scopeSet.add(scopeEntity.getId().getBzwksvckeyname()); + scopeSet.add(scopeEntity.getId().getScopeid()); return scopeSet; } catch (Exception e) { throw new DAOException(ExceptionUtil.getErrorCode(e, "RECEAIAUC001"));