09.10 변경분 반영

This commit is contained in:
Rinjae
2025-09-10 19:59:13 +09:00
parent aacc1a389e
commit 7e4b7e7931
4 changed files with 381 additions and 23 deletions
@@ -111,27 +111,27 @@ public class DBInboudSqlAdapterListener extends AdapterListenerSupport {
}
// TODO : Application Test Code.
// in real system, remove this block
else {
prop = new Properties();
prop.setProperty(DBAdapterKeys.RUN_CRONEXP, "0/10 * * * * ?");
prop.setProperty(DBAdapterKeys.RUN_INTVL, "1000");
prop.setProperty(DBAdapterKeys.RUN_RETRY, "N");
prop.setProperty(DBAdapterKeys.RUN_RETRYCNT, "2");
prop.setProperty(DBAdapterKeys.RUN_RETRYINTVL, "5000");
prop.setProperty(DBAdapterKeys.RUN_MSGTYPE, "JSON");
prop.setProperty(DBAdapterKeys.DRIVER, "oracle.jdbc.driver.OracleDriver");
prop.setProperty(DBAdapterKeys.URL, "jdbc:oracle:thin:@localhost:1521:XE");
prop.setProperty(DBAdapterKeys.USER, "eai");
prop.setProperty(DBAdapterKeys.PVAL, "eaiadmin");
// prop.setProperty(DBAdapterKeys.SELECT, "select * from APIJOBSCHE");
prop.setProperty(DBAdapterKeys.SELECT, "select * from EAIINF01 where RFLAG IS null ORDER BY UNID");
prop.setProperty(DBAdapterKeys.UPDATE, "update EAIINF01 set RFLAG = 'C' where UNID = '%UNID%' ");
debug("Start Scheduler");
adapterName = "_EAI_IN_DBQ_SyC";
}
// else {
// prop = new Properties();
//
// prop.setProperty(DBAdapterKeys.RUN_CRONEXP, "0/10 * * * * ?");
// prop.setProperty(DBAdapterKeys.RUN_INTVL, "1000");
// prop.setProperty(DBAdapterKeys.RUN_RETRY, "N");
// prop.setProperty(DBAdapterKeys.RUN_RETRYCNT, "2");
// prop.setProperty(DBAdapterKeys.RUN_RETRYINTVL, "5000");
// prop.setProperty(DBAdapterKeys.RUN_MSGTYPE, "JSON");
//
// prop.setProperty(DBAdapterKeys.DRIVER, "oracle.jdbc.driver.OracleDriver");
// prop.setProperty(DBAdapterKeys.URL, "jdbc:oracle:thin:@localhost:1521:XE");
// prop.setProperty(DBAdapterKeys.USER, "eai");
// prop.setProperty(DBAdapterKeys.PVAL, "eaiadmin");
//// prop.setProperty(DBAdapterKeys.SELECT, "select * from APIJOBSCHE");
// prop.setProperty(DBAdapterKeys.SELECT, "select * from EAIINF01 where RFLAG IS null ORDER BY UNID");
// prop.setProperty(DBAdapterKeys.UPDATE, "update EAIINF01 set RFLAG = 'C' where UNID = '%UNID%' ");
//
// debug("Start Scheduler");
// adapterName = "_EAI_IN_DBQ_SyC";
// }
// TODO : Application Test Code End.
prop.setProperty(DBAdapterKeys.ADAPTER_NAME, adapterName);
@@ -12,6 +12,7 @@ import com.eactive.eai.common.util.Logger;
import com.eactive.eai.data.entity.onl.authoutbound.OutboundOAuthCredential;
import com.eactive.eai.data.entity.onl.lifecycle.LifeCycle;
import com.eactive.eai.data.entity.onl.property.PropGroup;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -60,7 +61,9 @@ public class OutboundOAuthCredentialDao extends BaseDAO {
public void decryptClientSecret(OutboundOAuthCredentialVo vo) throws Exception {
String clientSecret = vo.getClientSecret();
String decrypted = Seed.decrypt(clientSecret);
vo.setClientSecret(decrypted);
if (StringUtils.isNotBlank(clientSecret)) {
String decrypted = Seed.decrypt(clientSecret);
vo.setClientSecret(decrypted);
}
}
}
@@ -2,6 +2,7 @@ package com.eactive.eai.authoutbound;
import lombok.Data;
import org.hibernate.annotations.Comment;
import org.mapstruct.Mapping;
import javax.persistence.Column;
import java.time.LocalDateTime;
@@ -29,4 +30,10 @@ public class OutboundOAuthCredentialVo {
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
private String contentType;
private String bodyJson;
private String headerJson;
}
@@ -0,0 +1,348 @@
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.annotation.JsonInclude;
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.entity.UrlEncodedFormEntity;
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.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 javax.net.ssl.SSLContext;
import java.io.IOException;
import java.nio.charset.Charset;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.*;
/**
* 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 HttpClientAccessTokenServiceWithDefault implements HttpClientAccessTokenServiceByDB {
public static Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT);
private static boolean testMode = TestModeChecker.isTestMode();
/**
* 1. 기능 : 토큰 발급에 사용 2. 처리 개요 : - 속성 정보를 설정 하고 토큰 발급 URL 호출 한다. 3. 주의사항
*
* @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 = oAuthCredentialVo.getContentType();
if(StringUtils.isBlank(contentType)){
contentType="application/x-www-form-urlencoded";
if(logger.isDebug()){
logger.debug("Content-Type not specified. Using default: 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");
if(logger.isDebug()) {
logger.debug(
"contentType:{}, uri:{}, charset:{}, transactionTimeout:{}, connectionTimeout:{}, useForwardProxy:{}, forwardProxyUrl:{}",
contentType, uri, encode, timeout, connectionTimeout, useForwardProxy, forwardProxyUrl);
}
// 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);
//HTTP HEADER Setting
httpPost.setHeader("Content-Type", contentType+"; charset=" + encode);
// 화면에서 추가 한 bodyJson을 Map으로 변환 후 합치기
String addHeaderJson = oAuthCredentialVo.getHeaderJson();
if (addHeaderJson != null && !addHeaderJson.isEmpty()) {
ObjectMapper headerJsonMapper = new ObjectMapper();
Map<String, Object> addHeaderMap = headerJsonMapper.readValue(addHeaderJson, Map.class);
for (Map.Entry<String, Object> entry : addHeaderMap.entrySet()) {
httpPost.setHeader(entry.getKey(), entry.getValue().toString());
}
}
//HTTP BODY Setting
// content Type 에 따라 전달 방법 분기
if(contentType.equals("application/json")){
//json body 개별 컬럼 setting
Map<String, Object> jsonMap = new HashMap<>();
jsonMap.put("client_id", oAuthCredentialVo.getClientId());
jsonMap.put("client_secret", oAuthCredentialVo.getClientSecret());
jsonMap.put("scope", oAuthCredentialVo.getScope());
jsonMap.put("grant_type", oAuthCredentialVo.getGrantType());
// 화면에서 추가 한 bodyJson을 Map으로 변환 후 합치기
String addBodyJson = oAuthCredentialVo.getBodyJson();
if (addBodyJson != null && !addBodyJson.isEmpty()) {
ObjectMapper bodyJsonMapper = new ObjectMapper();
Map<String, Object> addBodyMap = bodyJsonMapper.readValue(addBodyJson, Map.class);
jsonMap.putAll(addBodyMap);
}
ObjectMapper objMapper = new ObjectMapper();
objMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY); //빈값이 아닐 때만 param에 담기
String authJsonBody = objMapper.writeValueAsString(jsonMap);
httpPost.setEntity(new StringEntity(authJsonBody));
} else if (contentType.equals("application/x-www-form-urlencoded")){
//빈값이 아닐 때만 param에 담기
List<NameValuePair> formParams = new ArrayList<>();
addIfNotNull(formParams, "client_id", oAuthCredentialVo.getClientId());
addIfNotNull(formParams, "client_secret", oAuthCredentialVo.getClientSecret());
addIfNotNull(formParams, "scope", oAuthCredentialVo.getScope());
addIfNotNull(formParams, "grant_type", oAuthCredentialVo.getGrantType());
// 화면에서 추가 한 bodyJson을 Map으로 변환 후 추가
String addBodyJson = oAuthCredentialVo.getBodyJson();
if (addBodyJson != null && !addBodyJson.isEmpty()) {
ObjectMapper bodyJsonMapper = new ObjectMapper();
Map<String, Object> addBodyMap = bodyJsonMapper.readValue(addBodyJson, Map.class);
for (Map.Entry<String, Object> entry : addBodyMap.entrySet()) {
formParams.add(new BasicNameValuePair(entry.getKey(), entry.getValue().toString()));
}
}
httpPost.setEntity(new UrlEncodedFormEntity(formParams, charset));
}
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("contentType = [" + contentType + "]");
logger.debug("encode = [" + encode + "]");
logger.debug("bodyJson = ["+ oAuthCredentialVo.getBodyJson() + "]");
logger.debug("bodyHeader = ["+ oAuthCredentialVo.getHeaderJson() + "]");
try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
if (response.getCode() != 200) {
throw new Exception("OAuth token receive status fail value= " + response.getCode());
}
HttpEntity entity = response.getEntity();
String responseString = EntityUtils.toString(entity, encode);
logger.debug("oauthToken RECV = [" + responseString + "]");
if (StringUtils.isNotBlank(responseString)) {
OAuth2AccessTokenVO accessToken = new OAuth2AccessTokenVO();
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");
}
}
}
}
/**
* 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;
}
}
private void addIfNotNull(List<NameValuePair> params, String name, String value) {
if (StringUtils.isNotBlank(value)) {
params.add(new BasicNameValuePair(name, value));
}
}
}