8 Commits

Author SHA1 Message Date
curry772 6fd9caa7d4 오류응답 디폴트 형식(ERROR_MESSAGE_DEFAULT_FORMAT) 프러퍼티 설정 가능하도록 수정
- 프러퍼티 그룹 : MessageUtil
- 프러퍼티 : ERROR_MESSAGE_DEFAULT_FORMAT
2026-04-17 09:43:32 +09:00
curry772 68cf1dff16 Authorization 오류 메시지 출력 변경 - 실제 사용하는 토큰헤더이름사용하도록 변경 2026-04-17 09:42:13 +09:00
curry772 bebf5fa36d ApiConfig token.header.name 프러퍼티를 추가하여 token 헤더 프러퍼티 추가 2026-04-15 17:39:59 +09:00
curry772 dd9fc6fbdb BearerToken 간헐적 파싱 못하는 경우 발생에 대한 방어로직 2026-04-09 08:59:38 +09:00
curry772 71686fb387 RFC 2616 §13.5.1 (Hop-by-hop 헤더 목록) 오류 정정, Trailers -> Trailer 2026-04-08 14:38:34 +09:00
curry772 bc8b549a33 HTTP Header 로그 off 기능 - -Duse.http.header.log=y 옵션 추가시 로깅 2026-04-08 14:16:59 +09:00
curry772 56ac438265 fix: 비-mTLS https 연결 시 testMode에서 hostname 검증 우회 적용
TrustAllStrategy 사용 시에도 DefaultHostnameVerifier가 적용되어
외부 testmaster 인증서 hostname mismatch 오류 발생.
testMode(-Duse.test.trust=y)일 때 NoopHostnameVerifier 적용하여 해결.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 09:57:51 +09:00
curry772 791e362a74 feature: DJErp OAuth bypass 지원을 위한 공통 모듈 개선
- UnkownMessageLogUtils 추가: unknown 메시지 발생 시 DB 에러 로그 기록
- HttpAdapterServiceSupport: transactionId 추출을 try 블록 외부로 이동,
  예외 분기 처리(HttpStatusException/JwtAuthException/Exception) 및 UnkownMessageLogUtils 연동
- ApiAuthFilter: eaiSvcCd 강제 덮어쓰기 로직 주석처리 (STDMessage 조회값 우선 사용)
- MessageUtil: ERROR_MESSAGE_DEFAULT_FORMAT을 표준 OAuth2 에러 형식으로 변경

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 00:01:38 +09:00
9 changed files with 242 additions and 28 deletions
+2 -2
View File
@@ -86,8 +86,8 @@ dependencies {
api 'org.java-websocket:Java-WebSocket:1.3.9'
api 'javax.cache:cache-api:1.1.1'
api 'org.apache.ignite:ignite-slf4j:2.16.0'
api 'org.apache.ignite:ignite-kubernetes:2.16.0'
api 'org.apache.ignite:ignite-slf4j:2.14.0'
api 'org.apache.ignite:ignite-kubernetes:2.14.0'
// ignite 2.17 - JDK 11 이상 필요
// api 'org.apache.ignite:ignite-slf4j:2.17.0'
// api 'org.apache.ignite:ignite-kubernetes:2.17.0'
@@ -53,6 +53,8 @@ public abstract class HttpClient5AdapterServiceSupport implements HttpClientAdap
private static boolean testMode = TestModeChecker.isTestMode();
private static boolean httpHeaderLogMode = HttpAdapterExtraLogUtil.isHttpHeaderMode();
public synchronized void close() {
if(connectionManager != null) {
connectionManager.close();
@@ -366,8 +368,20 @@ public abstract class HttpClient5AdapterServiceSupport implements HttpClientAdap
}
else {
sslContext = SSLContextBuilder.create().loadTrustMaterial(new TrustAllStrategy()).build();
SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext);
cmBuilder.setSSLSocketFactory(csf);
SSLConnectionSocketFactory sslSocketFactory = null;
if(testMode) {
// Hostname verifier 비활성화 (테스트용)
sslSocketFactory = new SSLConnectionSocketFactory(
sslContext
, NoopHostnameVerifier.INSTANCE
);
}
else {
sslSocketFactory = new SSLConnectionSocketFactory(
sslContext
);
}
cmBuilder.setSSLSocketFactory(sslSocketFactory);
}
} catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException
@@ -384,6 +398,11 @@ public abstract class HttpClient5AdapterServiceSupport implements HttpClientAdap
HttpRequestInterceptor requestLogInterceptor = (request, entity, context) -> {
if (!httpHeaderLogMode) {
logger.info("httpHeader logging off - use.http.header.log");
return;
}
try {
String uuid = (String) context.getAttribute(TransactionContextKeys.TRANSACTION_UUID);
String contextAdapterGroupName = (String) context.getAttribute(HttpClientAdapterServiceKey.ADAPTER_GROUP_NAME);
@@ -403,7 +422,12 @@ public abstract class HttpClient5AdapterServiceSupport implements HttpClientAdap
};
HttpResponseInterceptor responseLogInterceptor = (response, entity, context) -> {
try{
if (!httpHeaderLogMode) {
logger.info("httpHeader logging off - use.http.header.log");
return;
}
try{
HttpRequest request = (HttpRequest)context.getAttribute("http.request");
String url = request.getUri().toString();
String uuid = (String) context.getAttribute(TransactionContextKeys.TRANSACTION_UUID);
@@ -10,29 +10,35 @@ import org.apache.commons.lang3.StringUtils;
import com.eactive.eai.adapter.AdapterGroupVO;
import com.eactive.eai.adapter.AdapterManager;
import com.eactive.eai.adapter.RequestDispatcher;
import com.eactive.eai.adapter.http.HttpStatusException;
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.HttpAdapterFilterFactoryKjb;
import com.eactive.eai.adapter.http.dynamic.filter.HttpAdapterFilterType;
import com.eactive.eai.adapter.http.dynamic.filter.JwtAuthException;
import com.eactive.eai.common.util.Logger;
public abstract class HttpAdapterServiceSupport implements HttpAdapterService, HttpAdapterServiceKey {
private static final String LOG_PREFIX = "HttpAdapterServiceSupport] ";
protected long slowTranTime = 2000L;
public static final String HEADER_NAME_CLIENT_ID = "x-elink-client-id";
public static final String PROPERTIES_NAME_CLIENT_ID = "clientId";
static Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);
public Object service(String adptGrpName, String adptName, Object message, Properties prop,
HttpServletRequest request, HttpServletResponse response) throws Exception {
String transactionId = request.getHeader(HttpClientAdapterServiceKey.TRANSACTION_ID);
if (StringUtils.isNotBlank(transactionId)) {
prop.put(HttpClientAdapterServiceKey.TRANSACTION_ID, transactionId);
}
try {
String clientId = request.getHeader(HEADER_NAME_CLIENT_ID);
if (StringUtils.isNotBlank(clientId)) {
prop.put(PROPERTIES_NAME_CLIENT_ID, clientId);
}
String transactionId = request.getHeader(HttpClientAdapterServiceKey.TRANSACTION_ID);
if (StringUtils.isNotBlank(transactionId)) {
prop.put(HttpClientAdapterServiceKey.TRANSACTION_ID, transactionId);
}
String instanceId = request.getHeader(HttpClientAdapterServiceKey.INSTANCE_ID);
if (StringUtils.isNotBlank(instanceId)) {
prop.put(HttpClientAdapterServiceKey.INSTANCE_ID, instanceId);
@@ -40,9 +46,26 @@ public abstract class HttpAdapterServiceSupport implements HttpAdapterService, H
} catch (Exception e) {
}
message = doPreFilters(adptGrpName, adptName, message, prop, request, response);
Object obj = RequestDispatcher.getRequestDispatcher(adptGrpName).handle(adptName, message, prop);
obj = doPostFilters(adptGrpName, adptName, obj, prop, request, response);
Object obj = null;
try {
message = doPreFilters(adptGrpName, adptName, message, prop, request, response);
obj = RequestDispatcher.getRequestDispatcher(adptGrpName).handle(adptName, message, prop);
obj = doPostFilters(adptGrpName, adptName, obj, prop, request, response);
} catch (HttpStatusException e) { // inbound error
logger.warn(LOG_PREFIX + transactionId + "-" + adptGrpName + "-" + adptName + ">>" + e.getMessage(), e);
throw e;
} catch (JwtAuthException e) { // filter error
logger.error(LOG_PREFIX + transactionId + "-" + adptGrpName + "-" + adptName + ">>" + e.getMessage(), e);
UnkownMessageLogUtils.logUnkownMessage(adptGrpName, adptName, message, prop, request, response,
"RECEAIIRP202", e);
throw e;
} catch (Exception e) { // filter error
logger.error(LOG_PREFIX + transactionId + "-" + adptGrpName + "-" + adptName + ">>" + e.getMessage(), e);
UnkownMessageLogUtils.logUnkownMessage(adptGrpName, adptName, message, prop, request, response,
"RECEAIIRP201", e);
throw e;
}
if (obj == null) {
return null;
} else if (obj instanceof byte[]) {
@@ -0,0 +1,149 @@
package com.eactive.eai.adapter.http.dynamic;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.Properties;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import com.eactive.eai.adapter.AdapterGroupVO;
import com.eactive.eai.adapter.AdapterManager;
import com.eactive.eai.adapter.http.client.HttpClientAdapterServiceKey;
import com.eactive.eai.common.exception.ExceptionUtil;
import com.eactive.eai.common.server.EAIServerManager;
import com.eactive.eai.common.util.DatetimeUtil;
import com.eactive.eai.common.util.InboundErrorLogger;
import com.eactive.eai.common.util.Logger;
import com.eactive.eai.common.util.UUIDGenerator;
import com.eactive.eai.inbound.error.InboundErrorInfoVO;
import com.eactive.eai.inbound.error.InboundErrorKeys;
import com.eactive.eai.util.HexaConverter;
public class UnkownMessageLogUtils {
static Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT);
private UnkownMessageLogUtils() {
throw new IllegalStateException("Utility class");
}
public static void logUnkownMessage(String adptGrpName, String adptName, Object message, Properties prop,
HttpServletRequest request, HttpServletResponse response, String errCode, Exception e) {
try {
EAIServerManager eaiServerManager = EAIServerManager.getInstance();
String serverName = eaiServerManager.getLocalServerName();
String txId = prop.getProperty(HttpClientAdapterServiceKey.TRANSACTION_ID);
if(StringUtils.isEmpty(txId)) {
StringBuffer sb = new StringBuffer();
String instanceid1 = serverName.substring(0,2);
String instanceid2 = serverName.substring(serverName.length()-2, serverName.length());
String instid = instanceid1 + instanceid2;
txId = instid + UUIDGenerator.getUUID();
}
String clientId = prop.getProperty("clientId");
String hexClientId = clientId != null ? HexaConverter.bytesToHexa(clientId.getBytes()) : "";
String errorMsg = ExceptionUtil.make(e, errCode);
StringBuffer sb = new StringBuffer();
sb.append(errorMsg).append("\n").append("Remote Addr : ").append(request.getRemoteAddr()).append("\n").append("Request URI : ")
.append(request.getRequestURI()).append("\n").append("clientId=").append(clientId).append(",hexClientId=").append(hexClientId).append(",txProp=").append(prop).append("\n").append("Exception : ").append(e.getMessage());
UnkownMessageLogUtils.logUnknownMessage(txId, adptGrpName, adptName,
"", "", false, errCode, sb.toString(), System.currentTimeMillis(),
serverName, InboundErrorKeys.IN_UNKNOWN, message);
} catch (Exception ex) {
logger.warn("unkown log fail.", ex);
}
}
// public static void logUnkownMessage(String uuid, String adapterGroupName, String adapterName, String errCode,
// String errMsg, Object message) {
// UnkownMessageLogUtils.logUnknownMessage(uuid, adapterGroupName, adapterName,
// "", "", false, errCode, errMsg, System.currentTimeMillis(),
// EAIServerManager.getInstance().getInstId(), InboundErrorKeys.IN_UNKNOWN, message);
// }
private static void logUnknownMessage(String uuid, String adapterGroupName, String adapterName,
String bzwkSvcKeyName, String eaiSvcCd, boolean isTasStarted, String errCode, String errMsg, long errTm,
String svcInstNm, String errDstCd, Object message) {
String msg = null;
if(errDstCd == null || errDstCd.length() ==0) {
errDstCd = "ND";
}
if(isTasStarted) {
errDstCd = InboundErrorKeys.TAS_START;
}
InboundErrorInfoVO errorInfoVO = new InboundErrorInfoVO();
errorInfoVO.setEaiSvcSno(uuid); // EAI서비스일련번호
errorInfoVO.setAdptBwkGrpNm(adapterGroupName); // 어댑터업무그룹명
errorInfoVO.setEaiSvcCd(eaiSvcCd);
errorInfoVO.setErrCd(errCode); // 에러코드
errorInfoVO.setErrTxt(errMsg); // 에러내용
errorInfoVO.setErrTm(DatetimeUtil.getCurrentTime(errTm)); // 에러발생시각
errorInfoVO.setErrDstcd(errDstCd);
errorInfoVO.setBzwkSvcKeyName(bzwkSvcKeyName);
if(message == null) {
msg = "null";
}
else {
if(message instanceof byte[]) {
AdapterManager adapterManager = AdapterManager.getInstance();
AdapterGroupVO srcAdapterGrpVO = adapterManager.getAdapterGroupVO(adapterGroupName);
// TAS 거래일 경우 NULL일 수 있음
if(srcAdapterGrpVO == null) {
msg = new String((byte[])message);
}
else {
// String srcAdapterMsgType = srcAdapterGrpVO.getMessageType();
// if( MessageUtil.isUTF8(srcAdapterMsgType) ) {
// try {
// msg = new String((byte[])message, UJSONMessage.encode);
// } catch (UnsupportedEncodingException e) {
// logger.warn("msg encode error", e);
// msg = new String((byte[])message); }
// }
// else {
// msg = new String((byte[])message);
// }
String charset = StringUtils.defaultIfBlank(srcAdapterGrpVO.getMessageEncode(), Charset.defaultCharset().name());
try {
msg = new String((byte[])message, charset);
} catch (UnsupportedEncodingException e) {
logger.warn("msg encode error", e);
msg = new String((byte[])message);
}
}
}
else if(message instanceof String) {
msg = (String)message;
}
else {
msg = "invaild message ["+message.getClass().getName()+"] "+message.toString();
}
}
errorInfoVO.setBwkDataTxt(msg); // 업무데이터내용
errorInfoVO.setEaiSvrInstNm(svcInstNm); // EAI서버인스턴스명
InboundErrorLogger.error(errorInfoVO);
if (logger.isError()) {
logger.error("======================================================================" );
logger.error(" RequestDispatcher] GET UNKNOWN MESSAGE");
logger.error(" ADAPTER GROUP NAME [" + adapterGroupName + "]");
logger.error(" EAISVCNAME [" + eaiSvcCd + "]");
logger.error(" ADAPTER NAME [" + adapterName + "]");
logger.error("======================================================================" );
}
}
}
@@ -104,7 +104,8 @@ public class ApiAuthFilter implements HttpAdapterFilter {
*/
StandardMessage standardMessage = STDMessageManager.getInstance().getSTDMessage(apiId);
String eaiSvcCd = StandardMessageManager.getInstance().getMapper().getEaiSvcCode(standardMessage);
if(!StringUtils.equals(eaiSvcCd, prop.getProperty("API_SERVICE_CODE"))) eaiSvcCd = prop.getProperty("API_SERVICE_CODE");
// if (StringUtils.isNotEmpty(eaiSvcCd) && !StringUtils.equals(eaiSvcCd, ))
// eaiSvcCd = prop.getProperty("API_SERVICE_CODE");
EAIMessage eaiMessage = EAIMessageManager.getInstance().getEAIMessage(eaiSvcCd);
if(StringUtils.isBlank(eaiMessage.getAuthType())){
@@ -305,7 +306,8 @@ public class ApiAuthFilter implements HttpAdapterFilter {
}
public static String extractBearerToken(HttpServletRequest request) throws JwtAuthException {
String authorization = request.getHeader("Authorization");
String tokenHeaderName = PropManager.getInstance().getProperty("ApiConfig", "token.header.name", "Authorization");
String authorization = request.getHeader(tokenHeaderName);
if (StringUtils.isBlank(authorization)) {
// QueryString으로 전달된 access_token 파라미터 확인
String tokenParamName = PropManager.getInstance().getProperty("ApiConfig", "token.param.name", "access_token");
@@ -313,13 +315,13 @@ public class ApiAuthFilter implements HttpAdapterFilter {
if (StringUtils.isNotBlank(queryToken)) {
return queryToken.trim();
}
throw new JwtAuthException(ERROR_AUTHENTICATION_FAIL, "No have header info: Authorization");
throw new JwtAuthException(ERROR_AUTHENTICATION_FAIL, "No have header info: " + tokenHeaderName);
}
String[] components = authorization.split("\\s");
String[] components = authorization.trim().split("\\s+", 2);
if (components.length != 2) {
throw new JwtAuthException(ERROR_AUTHENTICATION_FAIL, "Malformat [Authorization] content");
throw new JwtAuthException(ERROR_AUTHENTICATION_FAIL, "Malformat [" + tokenHeaderName + "] content");
}
if (!StringUtils.equalsIgnoreCase(components[0], "Bearer")) {
@@ -45,7 +45,7 @@ public class HttpAdapterServiceBypass extends HttpAdapterServiceSupport {
public static final String HTTP_STATUS = "HTTP_STATUS";
public static final String PROPERTIES_NAME_HTTP_REQUEST_METHOD = "httpRequestMethod";
public static final String[] HOP_BY_HOP_HEADERS = new String[] { "Connection", "Keep-Alive", "Proxy-Authenticate",
"Proxy-Authorization", "TE", "Trailers", "Transfer-Encoding", "Upgrade" };
"Proxy-Authorization", "TE", "Trailers", "Trailer", "Transfer-Encoding", "Upgrade" };
protected boolean doPreserveCookies = false;
protected boolean doPreserveCookiePath = false;
@@ -21,6 +21,10 @@ import org.apache.commons.lang3.StringUtils;
public class HttpAdapterExtraLogUtil {
static Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT);
public static boolean isHttpHeaderMode() {
return StringUtils.equals(System.getProperty("use.http.header.log", "n"), "y");
}
public static void insertHttpAdapterExtraLog(String guid, int serviceProcessNumber, String adapterGroupName, String adapterName, Map<Object, Object> headers, String url, String httpMethod){
insertHttpAdapterExtraLog(guid, serviceProcessNumber, adapterGroupName, adapterName, headers, url, httpMethod, 0);
@@ -23,6 +23,7 @@ import org.w3c.dom.Document;
import com.eactive.eai.common.EAIKeys;
import com.eactive.eai.common.message.MessageType;
import com.eactive.eai.common.monitor.EAIServiceMonitor;
import com.eactive.eai.common.property.PropManager;
import com.eactive.eai.transformer.message.ISO8583Message;
import com.eactive.eai.transformer.message.ISO8583MessageFactory;
import com.eactive.eai.transformer.util.IConv;
@@ -35,7 +36,8 @@ public final class MessageUtil {
static Logger logger = LoggerFactory.getLogger(MessageUtil.class);
//public static final String ERROR_MESSAGE_DEFAULT_FORMAT = "{\"error\":{\"code\":\"%s\",\"message\":\"%s\"}}";
public static final String ERROR_MESSAGE_DEFAULT_FORMAT = "{\"code\":\"%s\",\"message\":\"%s\"}";
// public static final String ERROR_MESSAGE_DEFAULT_FORMAT = "{\"code\":\"%s\",\"message\":\"%s\"}";
public static final String ERROR_MESSAGE_DEFAULT_FORMAT = "{\"error\":\"%s\",\"error_description\":\"%s\"}";
public static final String ERROR_CODE_AP_ERROR = "E.GW.AP_ERROR";
public static final String ERROR_CODE_AUTH_FAIL = "E.GW.AUTH_FAIL";
@@ -516,7 +518,8 @@ public final class MessageUtil {
public static String makeJsonErrorMessage(String code, String msg, String errorFormat) {
if (StringUtils.isBlank(errorFormat)) {
errorFormat = ERROR_MESSAGE_DEFAULT_FORMAT;
errorFormat = PropManager.getInstance().getProperty("MessageUtil",
"ERROR_MESSAGE_DEFAULT_FORMAT", ERROR_MESSAGE_DEFAULT_FORMAT);
}
// errorFormat에 %s가 1개만 있으면 msg만, 2개 이상이면 code, msg 전달
@@ -4,6 +4,9 @@ import java.util.Calendar;
import java.util.Properties;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.eactive.eai.common.TransactionContextKeys;
import com.eactive.eai.common.context.ElinkTransactionContext;
import com.eactive.eai.common.util.*;
@@ -96,7 +99,9 @@ public class RequestProcessor extends RequestProcessorSupport {
//isPEAIServer = eaiServerManager.isPEAIServer();
instid = eaiServerManager.getGroupInstId();
}
private static boolean httpHeaderLogMode = HttpAdapterExtraLogUtil.isHttpHeaderMode();
private ProcessVO setVO(Properties prop,long msgRcvTm) throws RequestProcessorException{
String actionName = prop.getProperty(Processor.REQUEST_ACTION);
String adapterGroupName = prop.getProperty(Processor.ADAPTER_GROUP_NAME);
@@ -422,12 +427,16 @@ public class RequestProcessor extends RequestProcessorSupport {
// INBOUND_HEADER NULL인 경우는 HTTP가 아닌것으로 간주함.
if(prop.get(HttpClientAdapterServiceKey.INBOUND_HEADER) != null) {
HttpAdapterExtraLogUtil.insertHttpAdapterExtraLog(vo.getUuid(), 100,
prop.getProperty(HttpClientAdapterServiceKey.ADAPTER_GROUP_NAME),
prop.getProperty(HttpClientAdapterServiceKey.ADAPTER_NAME),
(Properties) prop.get(HttpClientAdapterServiceKey.INBOUND_HEADER),
prop.getProperty(HttpClientAdapterServiceKey.INBOUND_EXTURI)+"?"+prop.getProperty(HttpClientAdapterServiceKey.INBOUND_EXTPARAMS),
prop.getProperty(HttpClientAdapterServiceKey.INBOUND_METHOD));
if (!httpHeaderLogMode) {
logger.info("httpHeader logging off - use.http.header.log");
} else {
HttpAdapterExtraLogUtil.insertHttpAdapterExtraLog(vo.getUuid(), 100,
prop.getProperty(HttpClientAdapterServiceKey.ADAPTER_GROUP_NAME),
prop.getProperty(HttpClientAdapterServiceKey.ADAPTER_NAME),
(Properties) prop.get(HttpClientAdapterServiceKey.INBOUND_HEADER),
prop.getProperty(HttpClientAdapterServiceKey.INBOUND_EXTURI)+"?"+prop.getProperty(HttpClientAdapterServiceKey.INBOUND_EXTPARAMS),
prop.getProperty(HttpClientAdapterServiceKey.INBOUND_METHOD));
}
}
EAIMessage eaiMsg = null;