diff --git a/src/main/java/com/eactive/eai/custom/authoutbound/client/impl/DJBErpNsmapiAccessTokenService.java b/src/main/java/com/eactive/eai/custom/authoutbound/client/impl/DJBErpNsmapiAccessTokenService.java new file mode 100644 index 0000000..a710761 --- /dev/null +++ b/src/main/java/com/eactive/eai/custom/authoutbound/client/impl/DJBErpNsmapiAccessTokenService.java @@ -0,0 +1,297 @@ + +package com.eactive.eai.custom.authoutbound.client.impl; + +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.Date; +import java.util.Properties; + +import javax.net.ssl.SSLContext; + +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.HttpEntity; +import org.apache.hc.core5.http.HttpHost; +import org.apache.hc.core5.http.io.entity.EntityUtils; +import org.apache.hc.core5.ssl.SSLContextBuilder; +import org.apache.hc.core5.util.Timeout; +import org.springframework.security.web.util.UrlUtils; + +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; + +/** + * 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 DJBErpNsmapiAccessTokenService 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/json;"; + + 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); + + + // 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("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); + + //json body setting +// Map jsonMap = new HashMap<>(); +// jsonMap.put("scope",oAuthCredentialVo.getScope()); +// jsonMap.put("grant_type",oAuthCredentialVo.getGrantType()); +// ObjectMapper objMapper = new ObjectMapper(); +// String authJsonBody = objMapper.writeValueAsString(jsonMap); + + try (CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(connectionManager).build();) { + HttpPost httpPost = new HttpPost(uri); +// httpPost.setEntity(new StringEntity(authJsonBody)); + 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)); + + String encodedAuth = oAuthCredentialVo.getClientId(); + 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 = [" + 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("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("Base64Header oauthToken RECV = [" + responseString + "]"); + + if (StringUtils.isNotBlank(responseString)) { + + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode responseJSON = objectMapper.readTree(responseString); + + if (responseJSON.has("data")) { + JsonNode data = responseJSON.path("data"); + if(data.has("token")) { + String token = data.path("token").asText(); + accessToken.setAccessToken(token); + } else { + throw new Exception("oauth token return null"); + } + } else { + throw new Exception("oauth token return null"); + } +// accessToken.setExpiration(new Date(currentTime + oAuthCredentialVo.getIntervalSec() * 1000L)); + accessToken.setExpiration(new Date(currentTime + 30_000L)); + + logger.debug("oauthToken =" + accessToken.toString()); + + return accessToken; + } else { + throw new Exception("oauth token return null"); + } + } catch (Exception e) { + e.printStackTrace(); + logger.error("[HttpClientAccessTokenServiceWithBase64Header] 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; + } + } +}