2 Commits

Author SHA1 Message Date
curry772 91f91c18ca refactor: DJErpApiAdapterService 코드 정리
- 미사용 메서드 12개 제거: doPreDecryption, doPostEncryption, doInboundPreDecrypt,
  doInboundPostEncrypt, extractBodyMsg, getJsonKey, JwtTokenExtractor,
  JwtClientIdExtractor, convertObjectToBytes, convertObjectToBase64Bytes,
  convertBytesToObject, assignApiId
- 미사용 필드 제거: encryptResponseApply, PAYLOAD_PARAM_NAME_CLIENT_ID,
  JSON_CONTENT_TYPE, JSON_FIELD_NAME, FILE_GROUP_NAME, UPLOAD_ROOT_PATH
- callApi 내 미사용 지역변수 제거: AdapterPropManager manager = null
- e.printStackTrace() 15건 제거 (삭제된 메서드 내부)
- == "" 버그 2건 제거 (삭제된 메서드 내부)
- 미사용 import 14개 제거
- jwhong 개발자 주석 및 죽은 주석 블록 제거

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-30 17:28:20 +09:00
curry772 840136b8ca feat: DJErp 어댑터 URL 매핑을 properties 외부화 및 동적 등록으로 변경
- DJErpAdapterMappingRegistrar 신규 추가: ContextRefreshedEvent 시점에
  dj.erp.adapter.paths(쉼표 구분)를 읽어 callApi 메서드를 동적으로 등록
  OAuth2의 FrameworkEndpointHandlerMapping과 충돌하지 않도록 exact type 비교로 선택
- DJErpApiAdapterController: 하드코딩된 @RequestMapping 제거 (Registrar에 위임)
- DJErpApiAdapterService: @Primary 추가 (Root/Servlet Context 중복 스캔 시 충돌 방지)
- applicationContext-jpa.xml: Root Context에서 @Controller/@RestController 제외 필터 추가
- authserver-servlet.xml: context:property-placeholder 및 DJErpAdapterMappingRegistrar 빈 선언 추가
- env.D/T/P.properties: dj.erp.adapter.paths 속성 추가

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-30 16:54:23 +09:00
164 changed files with 11968 additions and 4808 deletions
-2
View File
@@ -239,5 +239,3 @@ gradle-app.setting
# Claude Code
.claude/
/작업내역*.md
/설계서*.md
/분석서*.md
Vendored
-165
View File
@@ -1,165 +0,0 @@
pipeline {
agent { label 'djb-vm' }
triggers {
pollSCM('H/10 * * * *')
}
options {
timestamps()
disableConcurrentBuilds()
buildDiscarder(logRotator(numToKeepStr: '20', artifactNumToKeepStr: '20'))
}
environment {
JAVA_HOME = '/apps/opts/jdk8'
JAVA_HOME_TOMCAT = '/apps/opts/jdk17'
GRADLE_HOME = '/apps/opts/gradle-8.7'
GRADLE_USER_HOME = '/apps/opts/gradle-home'
NODE_HOME = '/apps/opts/node-v24'
PATH = "/apps/opts/jdk8/bin:/apps/opts/gradle-8.7/bin:/apps/opts/node-v24/bin:/apps/opts/bin:${env.PATH}"
GIT_SSH_COMMAND = 'ssh -o StrictHostKeyChecking=accept-new'
CATALINA_BASE = '/prod/eapim/apigw'
CATALINA_HOME = '/prod/eapim/apache-tomcat-9.0.116'
CATALINA_PID = '/prod/eapim/apigw/logs/catalina.pid'
DEPLOY_HTTP_PORT = '39110'
}
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('Checkout modules') {
steps {
sh '''
set -eu
for REPO in elink-online-core elink-online-transformer elink-online-common elink-online-emsclient elink-online-core-jpa; do
if [ ! -e "$REPO/.git" ]; then
rm -rf "$REPO"
git clone --depth=1 --branch master \
"ssh://git@172.30.1.50:2222/djb-eapim/$REPO.git" \
"$REPO"
else
git -C "$REPO" fetch --depth=1 origin master
git -C "$REPO" reset --hard origin/master
git -C "$REPO" clean -fdx
fi
done
'''
}
}
stage('Verify toolchain') {
steps {
sh '''
set -eu
java -version
gradle --version
'''
}
}
stage('Build WAR') {
steps {
sh 'gradle clean build -x test --no-daemon'
}
post {
success {
sh '''
set -eu
cd build/libs
sha1sum eapim-online.war > SHA1SUMS
sha256sum eapim-online.war > SHA256SUMS
'''
archiveArtifacts artifacts: 'build/libs/eapim-online.war,build/libs/SHA1SUMS,build/libs/SHA256SUMS', fingerprint: true
}
}
}
stage('Stop Tomcat') {
steps {
sh '''
set +e
systemctl --user stop eapim-apigw 2>/dev/null
if [ -f "$CATALINA_PID" ] && kill -0 "$(cat "$CATALINA_PID")" 2>/dev/null; then
JAVA_HOME="$JAVA_HOME_TOMCAT" "$CATALINA_HOME/bin/shutdown.sh" 30 -force || true
for i in $(seq 1 30); do
[ -f "$CATALINA_PID" ] && kill -0 "$(cat "$CATALINA_PID")" 2>/dev/null || break
sleep 1
done
fi
rm -f "$CATALINA_PID"
'''
}
}
stage('Deploy ROOT.war') {
steps {
sh '''
set -eu
DEPLOY_DIR="$CATALINA_BASE/webapps"
rm -rf "$DEPLOY_DIR/ROOT" "$DEPLOY_DIR/ROOT.war"
cp build/libs/eapim-online.war "$DEPLOY_DIR/ROOT.war"
ls -la "$DEPLOY_DIR"
'''
}
}
stage('Start Tomcat and readiness') {
steps {
sh '''
set -eu
LOG="$CATALINA_BASE/logs/catalina.$(date +%Y-%m-%d).log"
BEFORE=0
[ -f "$LOG" ] && BEFORE=$(stat -c %s "$LOG")
systemctl --user start eapim-apigw
PROBE_URL="http://localhost:$DEPLOY_HTTP_PORT/"
DEADLINE=$(($(date +%s) + 300))
STATUS=000
while [ "$(date +%s)" -lt "$DEADLINE" ]; do
STATUS=$(curl -sS -o /dev/null -w '%{http_code}' --max-time 3 \
"$PROBE_URL" 2>/dev/null || echo "000")
case "$STATUS" in
200|302|401|403|404)
echo "Readiness OK ($PROBE_URL HTTP $STATUS)"
break
;;
esac
if [ -f "$LOG" ] && tail -c +$((BEFORE+1)) "$LOG" | grep -qE "Application listener .* Exception|SEVERE.*startup.Catalina"; then
echo "Startup error detected in log"
tail -c +$((BEFORE+1)) "$LOG" | tail -80
exit 1
fi
sleep 2
done
case "$STATUS" in
200|302|401|403|404) ;;
*)
echo "Readiness probe failed within 300s, last HTTP status: $STATUS"
echo "--- last 300 lines of catalina log ---"
[ -f "$LOG" ] && tail -c +$((BEFORE+1)) "$LOG" | tail -300
echo "--- systemd unit status ---"
systemctl --user status eapim-apigw --no-pager || true
echo "--- listening ports ---"
ss -tlnp 2>/dev/null | grep -E ":$DEPLOY_HTTP_PORT\\b" || echo "(port $DEPLOY_HTTP_PORT not listening)"
exit 1
;;
esac
echo "--- key boot log lines ---"
tail -c +$((BEFORE+1)) "$LOG" | grep -E "Server startup|Deployment of web" | head -10 || true
'''
}
}
}
}
@@ -20,7 +20,12 @@
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
">
<context:component-scan base-package="com.eactive.eai" />
<context:component-scan base-package="com.eactive.eai">
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
<context:exclude-filter type="annotation"
expression="org.springframework.web.bind.annotation.RestController"/>
</context:component-scan>
<bean id="entityManagerFactory" primary="true"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
+8 -3
View File
@@ -17,6 +17,10 @@
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:property-placeholder
location="/WEB-INF/properties/*.${eai.systemmode}.properties"
ignore-unresolvable="true" />
<context:component-scan
base-package="com.eactive.eai.authserver" />
<context:component-scan
@@ -25,13 +29,14 @@
base-package="com.eactive.eai.adapter" />
<context:component-scan
base-package="com.eactive.eai.manage" />
<context:component-scan
base-package="com.eactive.eai.custom.inflow" />
<context:annotation-config />
<mvc:annotation-driven />
<mvc:default-servlet-handler />
<mvc:interceptors>
<bean class="com.eactive.eai.adapter.interceptor.HttpResponseLoggingInterceptor"/>
</mvc:interceptors>
<bean class="com.eactive.eai.adapter.controller.DJErpAdapterMappingRegistrar">
<property name="paths" value="${dj.erp.adapter.paths}" />
</bean>
</beans>
@@ -18,5 +18,8 @@ eai.rmiserviceport=30112
eai.systemmode=D
# EAI FEP MCI GW ...
eai.systemtype=API
eai.tableowner=AGWAPP
eai.tableowner=AGWADM
eai.server.extractor=[0,2],[0,2],[-2]
# DJErp API Adapter request mapping paths (comma-separated)
dj.erp.adapter.paths=/dj/{path:^(?!oauth).*$},/dj/{path:^(?!oauth).*$}/**
@@ -12,3 +12,6 @@ hibernate.dialect=org.hibernate.dialect.Oracle12cDialect
eai.systemtype=API
eai.server.extractor=[0,2],[0,2],[-2]
# DJErp API Adapter request mapping paths (comma-separated)
dj.erp.adapter.paths=/dj/{path:^(?!oauth).*$},/dj/{path:^(?!oauth).*$}/**
@@ -18,3 +18,6 @@ eai.systemmode=D
eai.systemtype=API
eai.tableowner=dapigw
eai.server.extractor=[0,2],[0,2],[-2]
# DJErp API Adapter request mapping paths (comma-separated)
dj.erp.adapter.paths=/dj/{path:^(?!oauth).*$},/dj/{path:^(?!oauth).*$}/**
@@ -1,2 +1,2 @@
#\uae30\uc874 ENV \ud56d\ubaa9 \uc678 \uae30\ud0c0 \uc2dc\uc2a4\ud15c \ud504\ub85c\ud37c\ud2f0\ub85c \uc815\uc758\uac00 \ud544\uc694\ud55c \ud544\ub4dc\ub97c \uc815\uc758
transformer.validation=true
transformer.validation=false
+1 -2
View File
@@ -19,7 +19,6 @@
<url-pattern>/agent/*</url-pattern>
<url-pattern>/common/*</url-pattern>
<url-pattern>/management/*</url-pattern>
<url-pattern>/manage/*</url-pattern>
<url-pattern>/mgr/*</url-pattern>
</filter-mapping>
@@ -30,7 +29,7 @@
<filter-mapping>
<filter-name>ApiRequestBodyFilter</filter-name>
<url-pattern>/api/*</url-pattern> <!-- 필요한 URL 패턴 -->
<url-pattern>/dj/*</url-pattern> <!-- 필요한 URL 패턴 -->
<url-pattern>/mapi/*</url-pattern> <!-- 필요한 URL 패턴 -->
</filter-mapping>
+26
View File
@@ -0,0 +1,26 @@
# comment start with #
# level; // 0 : root ~ n
# type; // 0: Message, 1: Field, 2:Group 3, Array, 4 : BIZ DATA
# size // 0 : variable, > 0 fixed
# fieldType; // 0: ELEMENT, 1: ATTRIBUTE,
# length;
# dataType; // 0: String, 1: Number
#name,level,type,size,fieldType,length,dataType,refPath,refValue,value
ElinkHeader ,0,0,1,0, 0,0,,,
Header ,1,2,1,1, 0,0,,,
StndCicsTrncd ,2,1,1,1,10,0,,,
StndIntnlStndTelgmLen,2,1,1,1,10,11,,,0
StndTranBaseYmd ,2,1,1,1,10,0,,,
Common ,1,2,0,0, 0,0,Header.StndCicsTrncd,JI6H,null
TranInfo ,2,2,1,0, 0,0,,,
StndCicsTrncd ,3,1,1,0, 3,0,,,
StndTelgmRecvTranCd ,3,1,1,0,10,0,,,
StndPrcssRtdTranCd ,3,1,1,0,10,0,,,
Array ,1,3,0,0, 3,0,,,
Item1 ,2,1,1,0,10,0,,,JI6H
Group ,2,3,1,0, 1,0,,,
gitem1 ,3,1,1,0,10,1,,,726
gitem2 ,3,1,1,0,10,0,,,20220905
FArray ,1,4,0,0,10,0,,,
bizData ,1,9,1,0, 0,0,,,
zzData ,1,1,1,0, 2,10,,,ZZ
1 # comment start with #
2 # level; // 0 : root ~ n
3 # type; // 0: Message, 1: Field, 2:Group 3, Array, 4 : BIZ DATA
4 # size // 0 : variable, > 0 fixed
5 # fieldType; // 0: ELEMENT, 1: ATTRIBUTE,
6 # length;
7 # dataType; // 0: String, 1: Number
8 #name,level,type,size,fieldType,length,dataType,refPath,refValue,value
9 ElinkHeader ,0,0,1,0, 0,0,,,
10 Header ,1,2,1,1, 0,0,,,
11 StndCicsTrncd ,2,1,1,1,10,0,,,
12 StndIntnlStndTelgmLen,2,1,1,1,10,11,,,0
13 StndTranBaseYmd ,2,1,1,1,10,0,,,
14 Common ,1,2,0,0, 0,0,Header.StndCicsTrncd,JI6H,null
15 TranInfo ,2,2,1,0, 0,0,,,
16 StndCicsTrncd ,3,1,1,0, 3,0,,,
17 StndTelgmRecvTranCd ,3,1,1,0,10,0,,,
18 StndPrcssRtdTranCd ,3,1,1,0,10,0,,,
19 Array ,1,3,0,0, 3,0,,,
20 Item1 ,2,1,1,0,10,0,,,JI6H
21 Group ,2,3,1,0, 1,0,,,
22 gitem1 ,3,1,1,0,10,1,,,726
23 gitem2 ,3,1,1,0,10,0,,,20220905
24 FArray ,1,4,0,0,10,0,,,
25 bizData ,1,9,1,0, 0,0,,,
26 zzData ,1,1,1,0, 2,10,,,ZZ
+41
View File
@@ -0,0 +1,41 @@
#-----------------------------------------------------------------------------
# comment start with #
# level; // 0 : root ~ n
# type; // 0: Message, 1: Field, 2:Group 3, Grid, 4 : Field Array, 9 : BIZ DATA
# size // 0 : variable, > 0 fixed
# fieldType; // 0: ELEMENT, 1: ATTRIBUTE,
# length;
# dataType; // 0: String, 1: Number,10: ZZ String, 11: LL Number
#-----------------------------------------------------------------------------
# name ,level ,type ,size ,fieldType ,length ,dataType ,,refPath, refValue,value
#-----------------------------------------------------------------------------
# name ,level ,type ,size ,fieldType ,length ,dataType ,refPath, refValue, value
STD_HNCPHEADER_REQ ,0 ,0 ,1 ,0 ,0 ,0 , , ,
msg_len ,1 ,1 ,1 ,0 ,6 ,1 , , ,199
global_id ,1 ,1 ,1 ,0 ,29 ,0 , , ,GIDNO00000000001111
service_id ,1 ,1 ,1 ,0 ,10 ,0 , , ,ELINKSVC01
service_cmt ,1 ,1 ,1 ,0 ,100 ,0 , , ,ELINK TEST Service
service_tn ,1 ,1 ,1 ,0 ,1 ,0 , , ,0
Session_id ,1 ,1 ,1 ,0 ,100 ,0 , , ,SESSIONID001
encrypt_yn ,1 ,1 ,1 ,0 ,2 ,0 , , ,NO
http_url ,1 ,1 ,1 ,0 ,100 ,0 , , ,
screen_id ,1 ,1 ,1 ,0 ,13 ,0 , , ,SCREEN0000001
scr_no ,1 ,1 ,1 ,0 ,30 ,0 , , ,000000000100000000020000000003
scr_dsc ,1 ,1 ,1 ,0 ,100 ,0 , , ,
usr_id ,1 ,1 ,1 ,0 ,10 ,0 , , ,USER000001
ip_addr ,1 ,1 ,1 ,0 ,47 ,0 , , ,
ip_mac ,1 ,1 ,1 ,0 ,12 ,0 , , ,
com_code ,1 ,1 ,1 ,0 ,4 ,0 , , ,EACT
head_code ,1 ,1 ,1 ,0 ,7 ,0 , , ,EACT001
dept_cd ,1 ,1 ,1 ,0 ,7 ,0 , , ,ELINK01
conn_tp ,1 ,1 ,1 ,0 ,2 ,0 , , ,CH
Inter_chn ,1 ,1 ,1 ,0 ,2 ,0 , , ,EA
recv_tm ,1 ,1 ,1 ,0 ,17 ,0 , , ,20220928000000000
send_tm ,1 ,1 ,1 ,0 ,17 ,0 , , ,
return_gubun ,1 ,1 ,1 ,0 ,1 ,0 , , ,S
return_value ,1 ,1 ,1 ,0 ,1 ,0 , , ,
msg_id ,1 ,1 ,1 ,0 ,7 ,0 , , ,
msg_nm ,1 ,1 ,1 ,0 ,250 ,0 , , ,
filler ,1 ,1 ,1 ,0 ,125 ,0 , , ,
bizData ,1 ,9 ,0 ,0 ,0 ,0 , , ,
1 #-----------------------------------------------------------------------------
2 # comment start with #
3 # level; // 0 : root ~ n
4 # type; // 0: Message, 1: Field, 2:Group 3, Grid, 4 : Field Array, 9 : BIZ DATA
5 # size // 0 : variable, > 0 fixed
6 # fieldType; // 0: ELEMENT, 1: ATTRIBUTE,
7 # length;
8 # dataType; // 0: String, 1: Number,10: ZZ String, 11: LL Number
9 #-----------------------------------------------------------------------------
10 # name ,level ,type ,size ,fieldType ,length ,dataType ,,refPath, refValue,value
11 #-----------------------------------------------------------------------------
12 # name ,level ,type ,size ,fieldType ,length ,dataType ,refPath, refValue, value
13 STD_HNCPHEADER_REQ ,0 ,0 ,1 ,0 ,0 ,0 , , ,
14 msg_len ,1 ,1 ,1 ,0 ,6 ,1 , , ,199
15 global_id ,1 ,1 ,1 ,0 ,29 ,0 , , ,GIDNO00000000001111
16 service_id ,1 ,1 ,1 ,0 ,10 ,0 , , ,ELINKSVC01
17 service_cmt ,1 ,1 ,1 ,0 ,100 ,0 , , ,ELINK TEST Service
18 service_tn ,1 ,1 ,1 ,0 ,1 ,0 , , ,0
19 Session_id ,1 ,1 ,1 ,0 ,100 ,0 , , ,SESSIONID001
20 encrypt_yn ,1 ,1 ,1 ,0 ,2 ,0 , , ,NO
21 http_url ,1 ,1 ,1 ,0 ,100 ,0 , , ,
22 screen_id ,1 ,1 ,1 ,0 ,13 ,0 , , ,SCREEN0000001
23 scr_no ,1 ,1 ,1 ,0 ,30 ,0 , , ,000000000100000000020000000003
24 scr_dsc ,1 ,1 ,1 ,0 ,100 ,0 , , ,
25 usr_id ,1 ,1 ,1 ,0 ,10 ,0 , , ,USER000001
26 ip_addr ,1 ,1 ,1 ,0 ,47 ,0 , , ,
27 ip_mac ,1 ,1 ,1 ,0 ,12 ,0 , , ,
28 com_code ,1 ,1 ,1 ,0 ,4 ,0 , , ,EACT
29 head_code ,1 ,1 ,1 ,0 ,7 ,0 , , ,EACT001
30 dept_cd ,1 ,1 ,1 ,0 ,7 ,0 , , ,ELINK01
31 conn_tp ,1 ,1 ,1 ,0 ,2 ,0 , , ,CH
32 Inter_chn ,1 ,1 ,1 ,0 ,2 ,0 , , ,EA
33 recv_tm ,1 ,1 ,1 ,0 ,17 ,0 , , ,20220928000000000
34 send_tm ,1 ,1 ,1 ,0 ,17 ,0 , , ,
35 return_gubun ,1 ,1 ,1 ,0 ,1 ,0 , , ,S
36 return_value ,1 ,1 ,1 ,0 ,1 ,0 , , ,
37 msg_id ,1 ,1 ,1 ,0 ,7 ,0 , , ,
38 msg_nm ,1 ,1 ,1 ,0 ,250 ,0 , , ,
39 filler ,1 ,1 ,1 ,0 ,125 ,0 , , ,
40 bizData ,1 ,9 ,0 ,0 ,0 ,0 , , ,
@@ -0,0 +1,11 @@
layout.file.type=CSV
layout.file.path=./resources/standard-layout-sample.csv
mapper.class=com.eactive.eai.message.test.TestInterfaceMapper
mapper.definition=./resources/standard-message-mapping-config.properties
reader.JSON=com.eactive.eai.message.parser.JsonReader
reader.JSN=com.eactive.eai.message.parser.JsonReader
reader.UJN=com.eactive.eai.message.parser.JsonReader
reader.XML=com.eactive.eai.message.parser.XmlReader
reader.UXL=com.eactive.eai.message.parser.XmlReader
reader.ASC=com.eactive.eai.message.parser.FlatReader
reader.FLAT=com.eactive.eai.message.parser.FlatReader
@@ -0,0 +1,27 @@
# layout : StandardMessage layout definition
#layout.file.type=CSV
layout.file.type=DB
#layout.file.path=standard-layout-sbi.csv
#layout.file.path=standard-layout-elk.csv
#layout.filter.FLAT=com.eactive.eai.custom.message.FlatMessageFilterSBI
layout.filter.FLAT=com.eactive.eai.message.filter.FlatMessageFilter
# mapper : StandardMessage's fields getter/setter interface
#mapper.class=com.eactive.eai.custom.message.InterfaceMapperSBI
mapper.class=com.eactive.eai.message.service.DefaultInterfaceMapper
#mapper.definition=standard-message-mapping-config-sbi.properties
#mapper.definition=standard-message-mapping-config-elk.properties
mapper.definition=standard-message-mapping-config-elk.properties
# StandardMessage Coordinator implementation
#message.coordinator.class=com.eactive.eai.custom.message.StandardMessageCoordinatorSBI
# RequestProcessor implementation
#requestProcessor.class=com.eactive.eai.inbound.processor.RequestProcessor
# reader : parsing input data to StandardMessage
reader.JSON=com.eactive.eai.message.parser.JsonReader
reader.JSN=com.eactive.eai.message.parser.JsonReader
reader.UJN=com.eactive.eai.message.parser.JsonReader
reader.XML=com.eactive.eai.message.parser.XmlReader
reader.UXL=com.eactive.eai.message.parser.XmlReader
reader.ASC=com.eactive.eai.message.parser.FlatReader
reader.FLAT=com.eactive.eai.message.parser.FlatReader
encode.flat=euc-kr
encode.xml=utf-8
@@ -0,0 +1,16 @@
# layout : StandardMessage layout definition
layout.file.type=DB
layout.db.testLayoutName=
layout.file.path=/development/intellij/elink-4.5/elink-online-multi/resources/standard-layout.csv
# mapper : StandardMessage's fields getter/setter interface
mapper.class=com.eactive.eai.message.service.DefaultInterfaceMapper
mapper.definition=/development/intellij/elink-4.5/elink-online-multi/resources/standard-message-mapping-config-elk.properties
# reader : parsing input data to StandardMessage
reader.JSON=com.eactive.eai.message.parser.JsonReader
reader.JSN=com.eactive.eai.message.parser.JsonReader
reader.UJN=com.eactive.eai.message.parser.JsonReader
reader.XML=com.eactive.eai.message.parser.XmlReader
reader.UXL=com.eactive.eai.message.parser.XmlReader
reader.ASC=com.eactive.eai.message.parser.FlatReader
reader.FLAT=com.eactive.eai.message.parser.FlatReader
@@ -0,0 +1,26 @@
REQ_SYS_CODE=commonHeader.firstReqSystemCode
CHANNEL_TYPE_CD=
CHANNEL_DETAIL_CD=
SEND_TIME=
GUID=commonHeader.globalId
SYSTEM_TYPE=
RECV_TIME=
RETURN_VALUE=
GUID_ORG=
GUID_SEQ=commonHeader.traceSeqNo
SERVICE_ID=commonHeader.serviceCode
RETURN_SERVICE_ID=
INTERFACE_ID=commonHeader.serviceCode
SESSION_ID=
SEND_RECV_DIVISION=commonHeader.reqResTypeCode
IN_EX_DIVISION=
OPERATION_ENV=
FIRST_REQ_IP=commonHeader.firstReqSystemIp
RECOVER_YN=
INST_CODE=
TIMEOUT=
ERROR_CODE=commonHeader.msgList[0].resultCode
ERROR_MSG=commonHeader.msgList[0].resultMsg
USER_ID=
RESPONSE_TYPE=
SYNC_ASYNC_TYPE=
@@ -0,0 +1,29 @@
REQ_SYS_CODE=systemHeader.DMND_SYS_CD
CHANNEL_TYPE_CD=stdcommon.chnlTycd
CHANNEL_DETAIL_CD=stdcommon.chnlDtlsClcd
SEND_TIME=stdheader.send_tm
GUID=stdheader.global_id
SYSTEM_TYPE=stdheader.Inter_chn
RECV_TIME=stdheader.recv_tm
RETURN_TYPE=stdheader.return_gubun
RETURN_VALUE=stdheader.return_value
GUID_ORG=stdcommon.ortrGuid
GUID_SEQ=stdheader.global_id
#SERVICE_ID=stdheader.service_id
SERVICE_ID=service_id
RETURN_SERVICE_ID=stdcommon.procsRsltRcmsSrvcId
INTERFACE_ID=stdheader.service_id
SESSION_ID=stdheader.Session_id
#SEND_RECV_DIVISION=stdheader.return_gubun
SEND_RECV_DIVISION=service_tn
IN_EX_DIVISION=stdcommon.hmabDvcd
OPERATION_ENV=stdcommon.sysOprtEnvDvcd
FIRST_REQ_IP=stdheader.ip_addr
RECOVER_YN=stdcommon.ortrRestrYn
INST_CODE=stdheader.com_code
TIMEOUT=stdheader.gnrzTmotTktm
ERROR_CODE=stdheader.msg_id
ERROR_MSG=stdheader.msg_nm
USER_ID=stdheader.usr_id
RESPONSE_TYPE=stdcommon.procsRsltDvcd
SYNC_ASYNC_TYPE=stdcommon.txSynzDvcd
@@ -0,0 +1,27 @@
REQ_SYS_CODE=Inter_chn
CHANNEL_TYPE_CD=
CHANNEL_DETAIL_CD=conn_tp
SEND_TIME=send_tm
GUID=global_id
SYSTEM_TYPE=service_tn
RECV_TIME=recv_tm
RETURN_TYPE=return_gubun
RETURN_VALUE=return_value
GUID_ORG=stdcommon.ortrGuid
GUID_SEQ=stdheader.global_id
SERVICE_ID=service_id
RETURN_SERVICE_ID=stdcommon.procsRsltRcmsSrvcId
INTERFACE_ID=stdheader.service_id
SESSION_ID=Session_id
SEND_RECV_DIVISION=service_tn
IN_EX_DIVISION=stdcommon.hmabDvcd
OPERATION_ENV=stdcommon.sysOprtEnvDvcd
FIRST_REQ_IP=ip_addr
RECOVER_YN=stdcommon.ortrRestrYn
INST_CODE=stdheader.com_code
TIMEOUT=stdheader.gnrzTmotTktm
ERROR_CODE=msg_id
ERROR_MSG=msg_nm
USER_ID=usr_id
RESPONSE_TYPE=stdcommon.procsRsltDvcd
SYNC_ASYNC_TYPE=stdcommon.txSynzDvcd
@@ -0,0 +1,26 @@
REQ_SYS_CODE=Inter_chn
CHANNEL_TYPE_CD=
CHANNEL_DETAIL_CD=conn_tp
SEND_TIME=send_tm
GUID=global_id
SYSTEM_TYPE=service_tn
RECV_TIME=recv_tm
RETURN_VALUE=return_value
GUID_ORG=stdcommon.ortrGuid
GUID_SEQ=stdheader.global_id
SERVICE_ID=service_id
RETURN_SERVICE_ID=stdcommon.procsRsltRcmsSrvcId
INTERFACE_ID=stdheader.service_id
SESSION_ID=Session_id
SEND_RECV_DIVISION=service_tn
IN_EX_DIVISION=return_gubun
OPERATION_ENV=stdcommon.sysOprtEnvDvcd
FIRST_REQ_IP=ip_addr
RECOVER_YN=stdcommon.ortrRestrYn
INST_CODE=stdheader.com_code
TIMEOUT=stdheader.gnrzTmotTktm
ERROR_CODE=msg_id
ERROR_MSG=msg_nm
USER_ID=usr_id
RESPONSE_TYPE=stdcommon.procsRsltDvcd
SYNC_ASYNC_TYPE=stdcommon.txSynzDvcd
@@ -21,9 +21,6 @@ import com.eactive.eai.adapter.http.dynamic.HttpDynamicInAdapterManager;
import com.eactive.eai.adapter.http.dynamic.HttpDynamicInAdapterUri;
import com.eactive.eai.common.stdmessage.STDMessageManager;
import com.eactive.eai.common.stdmessage.STDMsgInfoAddOnVO;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
public class ApiRequestBodyFilter extends OncePerRequestFilter {
@@ -35,12 +32,17 @@ public class ApiRequestBodyFilter extends OncePerRequestFilter {
HttpServletResponse response,
FilterChain filterChain)
throws ServletException, IOException {
System.out.println("ApiRequestBodyFilter : " + request.getRequestURI());
if (!isTarget(request)) {
filterChain.doFilter(request, response);
return;
}
String encoding = getInboundGroupAdapterEncoding(request);
//원본 바디를 byte[]로 읽기 (개행 포함 그대로) 왜? 뒤에 필터에서 HMAC값 계산할수도 있어서 변조되면 안됨. 개행문자도 그대로 가야함.
byte[] rawBody = readBodyAsBytes(request);
@@ -52,16 +54,14 @@ public class ApiRequestBodyFilter extends OncePerRequestFilter {
byte[] finalBody = rawBody;
try {
if(isJsonArray(rawBody)) {
String encoding = getInboundGroupAdapterEncoding(request);
String bodyStr = new String(rawBody, encoding);
Object json = JSONValue.parse(bodyStr);
if (json instanceof JSONArray) {
bodyStr = DJB_ROOTLESS_ARRAY + bodyStr + "}";
finalBody = bodyStr.getBytes(encoding);
}
}
String bodyStr = new String(rawBody, encoding);
Object json = JSONValue.parse(bodyStr);
if (json instanceof JSONArray) {
bodyStr = DJB_ROOTLESS_ARRAY + bodyStr + "}";
finalBody = bodyStr.getBytes(encoding);
}
} catch (Exception e) {
// JSON 파싱 실패 시 원본 유지
@@ -74,14 +74,6 @@ public class ApiRequestBodyFilter extends OncePerRequestFilter {
filterChain.doFilter(wrapped, response);
}
public static boolean isJsonArray(byte[] json) {
try (JsonParser parser = new JsonFactory().createParser(json)) {
return parser.nextToken() == JsonToken.START_ARRAY;
} catch (Exception e) {
return false;
}
}
private boolean isTarget(HttpServletRequest request) {
String uri = request.getRequestURI();
@@ -0,0 +1,91 @@
package com.eactive.eai.adapter.controller;
import java.lang.reflect.Method;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import com.eactive.eai.common.util.Logger;
/**
* properties 파일의 dj.erp.adapter.paths 값을 읽어
* DJErpApiAdapterController 의 callApi 메서드를 동적으로 URL에 등록한다.
*
* 경로는 쉼표로 구분하여 여러 개를 지정할 수 있다.
* 예) /dj/{path:^(?!oauth).*$},/dj/{path:^(?!oauth).*$}/**
*/
public class DJErpAdapterMappingRegistrar implements ApplicationListener<ContextRefreshedEvent> {
static Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);
@Autowired
private DJErpApiAdapterController djErpApiAdapterController;
private String paths;
private boolean registered = false;
public void setPaths(String paths) {
this.paths = paths;
}
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
if (registered) {
return;
}
if (StringUtils.isBlank(paths)) {
logger.warn("DJErpAdapterMappingRegistrar] dj.erp.adapter.paths 가 설정되지 않았습니다.");
return;
}
// OAuth2의 FrameworkEndpointHandlerMapping 을 제외하고 MVC 기본 매핑만 선택
Map<String, RequestMappingHandlerMapping> candidates =
event.getApplicationContext().getBeansOfType(RequestMappingHandlerMapping.class);
RequestMappingHandlerMapping requestMappingHandlerMapping = candidates.values().stream()
.filter(m -> m.getClass() == RequestMappingHandlerMapping.class)
.findFirst()
.orElse(null);
if (requestMappingHandlerMapping == null) {
logger.warn("DJErpAdapterMappingRegistrar] RequestMappingHandlerMapping 을 찾을 수 없습니다.");
return;
}
try {
Method callApi = DJErpApiAdapterController.class.getDeclaredMethod(
"callApi", HttpServletRequest.class, HttpServletResponse.class);
RequestMappingInfo.BuilderConfiguration options = new RequestMappingInfo.BuilderConfiguration();
options.setPatternParser(requestMappingHandlerMapping.getPatternParser());
for (String path : paths.split(",")) {
path = path.trim();
if (path.isEmpty()) {
continue;
}
RequestMappingInfo mappingInfo = RequestMappingInfo
.paths(path)
.options(options)
.build();
requestMappingHandlerMapping.registerMapping(mappingInfo, djErpApiAdapterController, callApi);
logger.warn("DJErpAdapterMappingRegistrar] 등록 완료: " + path);
}
registered = true;
} catch (NoSuchMethodException e) {
logger.error("DJErpAdapterMappingRegistrar] callApi 메서드를 찾을 수 없습니다.", e);
}
}
}
@@ -1,6 +1,7 @@
package com.eactive.eai.adapter.controller;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
@@ -8,46 +9,46 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
// jwhong
import org.json.simple.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.bind.annotation.RestController;
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.handler.AdapterErrorMessageHandler;
import com.eactive.eai.adapter.handler.AdapterErrorMessageHandlerFactory;
import com.eactive.eai.adapter.http.HttpStatusException;
import com.eactive.eai.adapter.http.client.HttpClientAdapterServiceKey;
import com.eactive.eai.adapter.http.dynamic.HttpAdapterServiceKey;
import com.eactive.eai.adapter.http.dynamic.HttpAdapterServiceSupport;
import com.eactive.eai.adapter.http.dynamic.HttpDynamicInAdapterManager;
import com.eactive.eai.adapter.http.dynamic.HttpDynamicInAdapterUri;
import com.eactive.eai.adapter.http.dynamic.filter.FilterException;
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.JwtAuthException;
import com.eactive.eai.adapter.service.DJBApiAdapterService;
import com.eactive.eai.adapter.service.DJErpApiAdapterService;
import com.eactive.eai.common.TransactionContextKeys;
import com.eactive.eai.common.exception.ExceptionUtil;
import com.eactive.eai.common.server.EAIServerManager;
import com.eactive.eai.common.stdmessage.STDMessageManager;
import com.eactive.eai.common.stdmessage.STDMsgInfoAddOnVO;
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.MessageUtil;
import com.eactive.eai.common.util.TxFileLogger;
import com.eactive.eai.common.util.TxSiftContext;
import com.eactive.eai.common.util.UUIDGenerator;
import com.eactive.eai.inbound.error.InboundErrorInfoVO;
import com.eactive.eai.util.HexaConverter;
@RestController
public class DJBApiAdapterController implements HttpAdapterServiceKey {
public class DJErpApiAdapterController implements HttpAdapterServiceKey {
static Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);
@Autowired
DJBApiAdapterService service;
DJErpApiAdapterService service;
/**
* DJErp OAuth 조기 적용을 위한 Controller
@@ -55,17 +56,6 @@ public class DJBApiAdapterController implements HttpAdapterServiceKey {
* @See com.eactive.eai.authserver.config.AuthorizationServerConfig.configure(AuthorizationServerEndpointsConfigurer
* endpoints)
*/
@RequestMapping(value = {
"/dj/{path:^(?!oauth).*$}", "/dj/{path:^(?!oauth).*$}/**",
"/kp/{path:^(?!oauth).*$}", "/kp/{path:^(?!oauth).*$}/**",
"/kkb/{path:^(?!oauth).*$}", "/kkb/{path:^(?!oauth).*$}/**",
"/nf/{path:^(?!oauth).*$}", "/nf/{path:^(?!oauth).*$}/**",
"/bsd/{path:^(?!oauth).*$}", "/bsd/{path:^(?!oauth).*$}/**",
"/tss/{path:^(?!oauth).*$}", "/tss/{path:^(?!oauth).*$}/**",
"/tsb/{path:^(?!oauth).*$}", "/tsb/{path:^(?!oauth).*$}/**",
"/shb/{path:^(?!oauth).*$}", "/shb/{path:^(?!oauth).*$}/**",
"/tst/{path:^(?!oauth).*$}", "/tst/{path:^(?!oauth).*$}/**",
})
public ResponseEntity<String> callApi(HttpServletRequest servletRequest, HttpServletResponse servletResponse)
throws Exception {
// /ONLWeb/api/v1/public/getUserInfo.svc
@@ -109,21 +99,49 @@ public class DJBApiAdapterController implements HttpAdapterServiceKey {
MediaType mediaType = MediaType.valueOf("application/json;charset=" + encode);
String errorResponseFormat = httpProp.getProperty(ERROR_RESPONSE_FORMAT);
ResponseEntity<String> responseEntity = null;
String responseType = httpProp.getProperty(RESPONSE_TYPE, "SYNC");
ResponseEntity responseEntity = null;
Properties transactionProp = new Properties();
String uuid = UUIDGenerator.getUUID().toString().replaceAll("-", "");
transactionProp.setProperty(TransactionContextKeys.TRANSACTION_UUID, uuid);
TxSiftContext.begin(uuid);
// jwhong, put api received time, eaiSvcCode
transactionProp.put(INBOUND_REQUESTED_TIME, receivedTimeStr);
String responseData = "";
// ** jwhong TSEAIHS04의 api full path와 비교하여 adapter를 가져온다
// String bzwkSvcKeyName = ""; // Adapter Group별 Action Class에 따라 구성이 달라짐. )
// // _AGW_IN_RST_SyS:POST/account/{acc_no}
// String adapterGrpName = ""; // Adapter Group명 ) _AGW_IN_RST_SyS : API_PATH(/api/test)
String apiSvcCode = ""; // eaiSvcCd ) LONNCHCON00005S2
String apiFullPathKey = ""; // ) POST|/api/test/account/list, POST|/api/test/account/{acc_no}
Map<String, String> pathVariables = null;
try {
responseData = service.callApi(servletRequest, servletResponse, httpProp, adapterGroupVO, adapterVO,
transactionProp);
// PathVariable(ex:/api/test/account/{acc_no}) 대응 DAO조회 제거.
// /api/test/account/1234567 호출시 /api/test/account/{acc_no} API 식별.
// String methodAndUri = getRequestRuledPath(servletRequest, apiUri, adapterVO, transactionProp);
String methodAndUri = servletRequest.getMethod() + "|" + apiUri;
STDMessageManager manager = STDMessageManager.getInstance();
STDMsgInfoAddOnVO stdMsgInfo = manager.getStdMsgInfoAddOn(methodAndUri);
// bzwkSvcKeyName = stdMsgInfo.getBzwksvckeyname();
apiSvcCode = stdMsgInfo.getEaiSvcCd();
transactionProp.put(API_SERVICE_CODE, apiSvcCode);
// adapterGrpName = stdMsgInfo.gAdapterGroupName();
apiFullPathKey = stdMsgInfo.getApiFullPath();
if (STDMessageManager.isPathVariable(apiFullPathKey)) {
pathVariables = new AntPathMatcher().extractUriTemplateVariables(apiFullPathKey, methodAndUri);
}
// adptUri = findAdptUri(apiUri, HttpDynamicInAdapterManager.getInstance(), adapterGrpName);
} catch (Exception e) {
// adptUri = null;
}
// ***
if (pathVariables != null) {
transactionProp.put(INBOUND_PATH_VARIABLES, pathVariables);
}
try {
String responseData = service.callApi(servletRequest, servletResponse, httpProp, adapterGroupVO, adapterVO,
transactionProp);
servletResponse.addHeader("traceId",
transactionProp.getProperty(TransactionContextKeys.TRANSACTION_UUID)); // uuid를 response header에
int httpStatus = servletResponse.getStatus();
@@ -132,33 +150,42 @@ public class DJBApiAdapterController implements HttpAdapterServiceKey {
} else {
responseEntity = ResponseEntity.ok().contentType(mediaType).body(responseData);
}
} catch (Exception e) {
logger.error(adapterGroupName + "-" + adapterName + ">>" + e.getMessage(), e);
String errorMsg = null;
errorMsg = genErrorResponseMessage(adapterGroupName, adapterName,
transactionProp, e, adptMsgType, encode, errorResponseFormat);
if (e instanceof FilterException) {
FilterException e1 = (FilterException) e;
this.logError(servletRequest, uuid, adapterGroupName, e1.getCode(), errorMsg, transactionProp, e);
logger.error("ApiAdapterController] " + adapterGroupName + "-" + adapterName + ">>" + e.getMessage());
responseEntity = ResponseEntity.status(e1.getStatus()).contentType(mediaType).body(errorMsg);
} else if (e instanceof JwtAuthException) {
JwtAuthException e1 = (JwtAuthException) e;
this.logError(servletRequest, uuid, adapterGroupName, e1.getCode(), errorMsg, transactionProp, e);
logger.error("ApiAdapterController] " + adapterGroupName + "-" + adapterName + ">>" + e.getMessage(), e);
responseEntity = ResponseEntity.status(HttpStatus.UNAUTHORIZED).contentType(mediaType).body(errorMsg);
} else if (e instanceof HttpStatusException) {
logger.error("ApiAdapterController] " + adapterGroupName + "-" + adapterName + ">>" + e.getMessage());
if( e instanceof HttpStatusException ) {
logger.warn("ApiAdapterController] " + adapterGroupName + "-" + adapterName + ">>" + e.getMessage());
HttpStatusException e1 = (HttpStatusException) e;
errorMsg = MessageUtil.makeErrorMessageByMessageType(adptMsgType, encode,
MessageUtil.ERROR_CODE_AP_ERROR, e1.getMessage(), errorResponseFormat);
responseEntity = ResponseEntity.status(e1.getStatus()).contentType(mediaType).body(errorMsg);
} else {
logger.error(adapterGroupName + "-" + adapterName + ">>" + e.getMessage(), e);
responseEntity = ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).contentType(mediaType).body(errorMsg);
}
responseData = errorMsg;
else if( e instanceof JwtAuthException )
{
logger.error(adapterGroupName + "-" + adapterName + ">>" + e.getMessage(), e);
errorMsg = MessageUtil.makeErrorMessageByMessageType(adptMsgType, encode,
MessageUtil.ERROR_CODE_AUTH_FAIL, e.getMessage(), errorResponseFormat);
responseEntity = ResponseEntity.status(HttpStatus.UNAUTHORIZED).contentType(mediaType).body(errorMsg);
}
else
{
logger.error(adapterGroupName + "-" + adapterName + ">>" + e.getMessage(), e);
errorMsg = MessageUtil.makeErrorMessageByMessageType(adptMsgType, encode,
MessageUtil.ERROR_CODE_AP_ERROR, e.getMessage(), errorResponseFormat);
responseEntity = ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).contentType(mediaType)
.body(errorMsg);
}
String postFilterNames = httpProp.getProperty("POST_FILTERS","");
if( postFilterNames.contains("HMAC_SHA256") ) {
HttpAdapterFilter filter = HttpAdapterFilterFactoryKjb.createFilter("HMAC_SHA256");
filter.doPostFilter(adapterGroupName, postFilterNames, errorMsg, transactionProp, servletRequest, servletResponse);
}
} finally {
/**
@@ -166,39 +193,31 @@ public class DJBApiAdapterController implements HttpAdapterServiceKey {
* 추후 좋은방법이 생길경우 개선 요망
*/
try {
TxFileLogger.logTxFile(transactionProp, responseData, "[IN_SEND]");
servletRequest.setAttribute(TransactionContextKeys.TRANSACTION_PROP, transactionProp);
servletRequest.setAttribute(TransactionContextKeys.TRANSACTION_PROP, transactionProp);
if (!"ASYNC".equals(responseType)) {
String uuid = transactionProp.getProperty(TransactionContextKeys.TRANSACTION_UUID);
String url = transactionProp.getProperty(HttpClientAdapterServiceKey.INBOUND_EXTURI);
String method = transactionProp.getProperty(HttpClientAdapterServiceKey.INBOUND_METHOD);
int httpStatusCode = servletResponse.getStatus();
Map<Object, Object> headerMap = new HashMap<>();
// 응답 헤더 로깅
for (String headerName : servletResponse.getHeaderNames()) {
String headerValue = servletResponse.getHeader(headerName);
logger.debug(String.format("httpHeader logging headerKey=%s, headerValue=%s",
headerName, headerValue));
headerMap.put(headerName, headerValue);
}
//HttpAdapterExtraLogUtil.insertHttpAdapterExtraLog(uuid, 400, adapterGroupName,adapterName, headerMap, url, method, httpStatusCode);
}
} catch (Exception e) {
logger.warn("http header db logging fail.", e);
} finally {
TxSiftContext.end();
}
}
return responseEntity;
}
private String genErrorResponseMessage(String adapterGroupName, String adapterName,
Properties callProp, Throwable e, String adptMsgType, String encode, String errorResponseFormat) {
String errorHandlerClass = AdapterPropManager.getInstance().getProperty(adapterName, "ERR_MSG_HANDLER");
if (StringUtils.isNotBlank(errorHandlerClass)) {
if (logger.isInfo())
logger.info("ExceptionHandler] errorSyncSend ERR_MSG_HANDLER=" + errorHandlerClass);
AdapterErrorMessageHandler handler = AdapterErrorMessageHandlerFactory.createHandler(errorHandlerClass);
if (handler != null) {
Object resposne;
try {
resposne = handler.generateNonStandardInboundErrorResponseMessage(adapterGroupName, adapterName,
callProp, null, null, e);
return (String)resposne;
} catch (Exception e1) {
logger.warn("AdapterErrorMessageHandler error.", e);
// handler 실패시 아래에서 처리한
}
}
}
return MessageUtil.makeErrorMessageByMessageType(adptMsgType, encode,
MessageUtil.ERROR_CODE_AP_ERROR, e.getMessage(), errorResponseFormat);
}
/**
* url에서 뒤쪽 /이후를 제거하면서 찾는다.<br>
@@ -246,72 +265,53 @@ public class DJBApiAdapterController implements HttpAdapterServiceKey {
}
private void logError(HttpServletRequest request) {
try {
String errorCode = "RECEAIIRP010";
String[] msgArgs = new String[1];
msgArgs[0] = request.getRequestURI();
EAIServerManager eaiServerManager = EAIServerManager.getInstance();
String serverName = eaiServerManager.getLocalServerName();
InboundErrorInfoVO errorInfoVO = new InboundErrorInfoVO();
String instanceid1 = serverName.substring(0, 2);
String instanceid2 = serverName.substring(serverName.length() - 2, serverName.length());
String instid = instanceid1 + instanceid2;
String uuid = instid + UUIDGenerator.getUUID();
this.logError(request, uuid, "HTTP_IN_NO_URI", errorCode,
ExceptionUtil.make(new Exception("cat not find uri"), errorCode, msgArgs), null, null);
} catch (Throwable t) {
logger.warn("inbound logging failed", t);
}
}
private void logError(HttpServletRequest request, String uuid, String adapterGroupName, String errorCode,
String errorMsg, Properties prop, Exception e) {
try {
EAIServerManager eaiServerManager = EAIServerManager.getInstance();
String serverName = eaiServerManager.getLocalServerName();
InboundErrorInfoVO errorInfoVO = new InboundErrorInfoVO();
errorInfoVO.setEaiSvcSno(uuid); // EAI서비스일련번호
errorInfoVO.setAdptBwkGrpNm(adapterGroupName); // 어댑터업무그룹명
errorInfoVO.setErrCd(errorCode); // 에러코드
errorInfoVO.setErrTxt(errorMsg); // 에러내용
errorInfoVO.setErrTm(DatetimeUtil.getCurrentTime(new Date().getTime())); // 에러발생시각
errorInfoVO.setErrDstcd(" ");
StringBuffer sb = new StringBuffer();
String clientId = prop.getProperty(HttpAdapterServiceSupport.PROPERTIES_NAME_CLIENT_ID);
String hexClientId = clientId != null ? HexaConverter.bytesToHexa(clientId.getBytes()) : "";
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");
if (e != null)
sb.append("Exception : ").append(e.getMessage());
EAIServerManager eaiServerManager = EAIServerManager.getInstance();
String serverName = eaiServerManager.getLocalServerName();
String uuid = null;
StringBuffer sb = new StringBuffer();
errorInfoVO.setBwkDataTxt(sb.toString()); // 업무데이터내용
errorInfoVO.setEaiSvrInstNm(serverName); // EAI서버인스턴스명
InboundErrorLogger.error(errorInfoVO);
} catch (Throwable t) {
logger.warn("inbound logging failed", t);
}
String instanceid1 = serverName.substring(0, 2);
String instanceid2 = serverName.substring(serverName.length() - 2, serverName.length());
String instid = instanceid1 + instanceid2;
uuid = instid + UUIDGenerator.getUUID();
String[] msgArgs = new String[1];
msgArgs[0] = request.getRequestURI();
String errorCode = "RECEAIIRP010";
String errorMsg = ExceptionUtil.make(new Exception("cat not find uri"), errorCode, msgArgs);
errorInfoVO.setEaiSvcSno(uuid); // EAI서비스일련번호
errorInfoVO.setAdptBwkGrpNm("HTTP_IN_NO_URI"); // 어댑터업무그룹명
errorInfoVO.setErrCd(errorCode); // 에러코드
errorInfoVO.setErrTxt(errorMsg); // 에러내용
errorInfoVO.setErrTm(DatetimeUtil.getCurrentTime(new Date().getTime())); // 에러발생시각
errorInfoVO.setErrDstcd(" ");
sb.append("Remote Addr : ").append(request.getRemoteAddr()).append("\n").append("Request URI : ")
.append(request.getRequestURI());
errorInfoVO.setBwkDataTxt(sb.toString()); // 업무데이터내용
errorInfoVO.setEaiSvrInstNm(serverName); // EAI서버인스턴스명
InboundErrorLogger.error(errorInfoVO);
}
private String makeResponseBodyMsg() {
Map<String, Object> dataMap = new HashMap<>();
dataMap.put("result", 1);
Map<String, Object> map = new HashMap<>();
map.put("code", "200");
map.put("data", dataMap);
map.put("message", "정상 처리 되었습니다.");
JSONObject json = new JSONObject();
json.putAll(map);
return json.toJSONString();
}
// private String makeResponseBodyMsg() {
//
// Map<String, Object> dataMap = new HashMap<>();
// dataMap.put("result", 1);
// Map<String, Object> map = new HashMap<>();
//
// map.put("code", "200");
// map.put("data", dataMap);
// map.put("message", "정상 처리 되었습니다.");
//
// JSONObject json = new JSONObject();
// json.putAll(map);
//
// return json.toJSONString();
// }
/*
* private String makeResponseBodyMsg(String asyncMsgStyle) {
@@ -1,787 +0,0 @@
package com.eactive.eai.adapter.service;
import java.io.IOException;
import java.io.StringWriter;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.StopWatch;
import org.apache.mina.common.ByteBuffer;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.util.AntPathMatcher;
import com.eactive.eai.adapter.AdapterGroupVO;
import com.eactive.eai.adapter.AdapterVO;
import com.eactive.eai.adapter.Keys;
import com.eactive.eai.adapter.http.HttpMemoryLogger;
import com.eactive.eai.adapter.http.HttpMethodType;
import com.eactive.eai.adapter.http.dynamic.HttpAdapterServiceSupport;
import com.eactive.eai.adapter.http.dynamic.filter.ApiKeyExtractFilter;
import com.eactive.eai.adapter.http.filter.AbstractCryptoFilter;
import com.eactive.eai.common.context.ElinkTransactionContext;
import com.eactive.eai.common.exception.ExceptionUtil;
import com.eactive.eai.common.message.MessageType;
import com.eactive.eai.common.util.CommonLib;
import com.eactive.eai.common.util.JSONUtils;
import com.eactive.eai.common.util.Logger;
import com.eactive.eai.common.util.TxFileLogger;
import com.eactive.eai.common.util.XMLUtils;
import com.eactive.eai.inbound.action.ActionFactory;
import com.eactive.eai.inbound.action.RequestAction;
import com.eactive.eai.inbound.processor.Processor;
import com.eactive.eai.message.StandardMessageUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
@Service
public class DJBApiAdapterService extends HttpAdapterServiceSupport {
static Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);
static Logger siftLogger = Logger.getLogger(Logger.LOGGER_SIFT);
public static final String HEADER_GROUP = "HEADER_GROUP";
public static final String HTTP_STATUS = "HTTP_STATUS"; // jwhong
// HEADER_GROUP JSON에 추가할 항목 정의, 없으면 전체 header 추가
public static final String HEADER_KEYS = "HEADER_KEYS";
public static final String PROPERTIES_NAME_HTTP_REQUEST_METHOD = "httpRequestMethod";
public static final String PAYLOAD_PARAM_NAME_CLIENT_ID = "client_id"; // jwhong
public String callApi(HttpServletRequest request, HttpServletResponse response, Properties httpProp,
AdapterGroupVO adapterGroupVO, AdapterVO adapterVO, Properties transactionProp) throws Exception {
int traceLevel = 0;
String adptGrpName = adapterGroupVO.getName();
String adptName = adapterVO.getName();
String urlDecodeYn = httpProp.getProperty(URL_DECODE_YN, "N");
String encode = StringUtils.defaultIfBlank(adapterGroupVO.getMessageEncode(), "UTF-8");
String traceLevelTemp = httpProp.getProperty(TRACE_LEVEL, "0");
String relayRequestHeaderKeys = httpProp.getProperty(HEADER_KEYS);
String headerGroupName = httpProp.getProperty(HEADER_GROUP);
boolean isParameterType = false;
String message = null;
String paramValue = null;
String adptMsgType = null;
Properties inboundHeaderProp = getHeaders(request);
transactionProp.put(INBOUND_METHOD, request.getMethod());
transactionProp.put(INBOUND_URI, request.getRequestURI());
transactionProp.put(INBOUND_QUERY_STRING, StringUtils.defaultString(request.getQueryString()));
transactionProp.put(INBOUND_HEADER, inboundHeaderProp);
transactionProp.put(INBOUND_EXTPARAMS, StringUtils.defaultString(request.getQueryString()));
if (StringUtils.equals(adapterVO.getAdapterGroupVO().getType(), Keys.TYPE_REST)
|| StringUtils.equals(adapterVO.getAdapterGroupVO().getType(), Keys.TYPE_HTTP_CUSTOM)) {
// /api/v1/public/getUserInfo.svc
String extUrl = StringUtils.removeStart(request.getRequestURI(), request.getContextPath());
transactionProp.put(INBOUND_EXTURI, extUrl);
} else {
transactionProp.put(INBOUND_EXTURI, getExtUri(request));
}
transactionProp.put(INBOUND_CLIENT_IP, getClientIp(request)); // Client IP 추가
transactionProp.put(Processor.REQUEST_ACTION, adapterVO.getAdapterGroupVO().getRefClass());
transactionProp.put(API_PATH, httpProp.getProperty(API_PATH, ""));
//djerp bypass
transactionProp.put(INBOUND_REWRITE_PATH,
getRewritePath(transactionProp.getProperty(INBOUND_EXTURI), transactionProp.getProperty(API_PATH)));
transactionProp.put(INBOUND_REMOTE_ADDR, request.getRemoteAddr());
transactionProp.put(INBOUND_SCHEME, request.getScheme());
transactionProp.put(PRE_FILTERS, httpProp.getProperty(PRE_FILTERS, ""));
transactionProp.put(POST_FILTERS, httpProp.getProperty(POST_FILTERS, ""));
transactionProp.put(PROPERTIES_NAME_HTTP_REQUEST_METHOD, request.getMethod());
transactionProp.put(ALLOW_IP, httpProp.getProperty(ALLOW_IP, ""));
transactionProp.put("BLOCK_IP", httpProp.getProperty("BLOCK_IP", ""));
//djerp bypass
Map<String, String> pathVariables = assignPathVariables(request, adptGrpName, adptName, null, transactionProp);
if (pathVariables != null) {
transactionProp.put(INBOUND_PATH_VARIABLES, pathVariables);
}
transactionProp.put(ENC_PATHS, httpProp.getProperty(ENC_PATHS, ""));
transactionProp.put(ENCRYPT_ALGORITHM, httpProp.getProperty(ENCRYPT_ALGORITHM, "")); //jwhong
if (inboundHeaderProp.get(HEADER_NAME_CLIENT_ID) != null) { // jwhong
transactionProp.put(HEADER_NAME_CLIENT_ID, inboundHeaderProp.get(HEADER_NAME_CLIENT_ID));
}
transactionProp.put(ENCRYPT_BASE_TYPE, httpProp.getProperty(ENCRYPT_BASE_TYPE, "")); //jwhong
// //transactionProp.put(INBOUND_TOKEN, getHeaders(request).get(INBOUND_TOKEN)); // jwhong
// transactionProp.put(INBOUND_TOKEN, getHeaders(request).get(INBOUND_TOKEN) != null ? getHeaders(request).get(INBOUND_TOKEN) : ""); //jwhong , null 이면 default로 "" put
String inboundToken = inboundHeaderProp.getOrDefault(INBOUND_TOKEN, "").toString();
if (StringUtils.isNotBlank(inboundToken) && StringUtils.startsWith(inboundToken, "Bearer ")) {
inboundToken = inboundToken.substring(7);
}
transactionProp.put(INBOUND_TOKEN, inboundToken);
transactionProp.put(ENCRYPT_AES256_IV, httpProp.getProperty(ENCRYPT_AES256_IV, "")); //jwhong
transactionProp.put(ENCRYPT_AES256_KEY, httpProp.getProperty(ENCRYPT_AES256_KEY, "")); //jwhong
transactionProp.put(AbstractCryptoFilter.PROP_MODULE_NAME, httpProp.getProperty(AbstractCryptoFilter.PROP_MODULE_NAME, ""));
transactionProp.put(HEADER_GROUP, headerGroupName);
transactionProp.put(HEADER_KEYS, relayRequestHeaderKeys);
// SEED 컬럼암호하 시 Key로 사용함
String seedkey = inboundHeaderProp.getOrDefault("x-obp-partnercode", "").toString();
ElinkTransactionContext.setSeedKey(seedkey);
try {
traceLevel = Integer.parseInt(traceLevelTemp);
} catch (Exception e) {
traceLevel = 0;
}
StopWatch stopWatch = new StopWatch();
stopWatch.start();
logger.debug("시작 >> encode = [" + encode + "]");
switch (HttpMethodType.getValue(request.getMethod())) {
case GET:
case DELETE:
isParameterType = true;
break;
case POST:
case PUT:
if (StringUtils.contains(request.getContentType(), "application/x-www-form-urlencoded")) {
isParameterType = true;
} else if ( StringUtils.isNoneBlank(request.getQueryString()) ) { // jwhong
isParameterType = true;
} else {
isParameterType = false;
}
break;
default:
break;
}
if (isParameterType) {
paramValue = request.getQueryString();
transactionProp.put(INBOUND_QUERY_STRING, StringUtils.defaultString(paramValue)); // Filter에서 QueryString 검증을 위해 저장
if (paramValue == null)
paramValue = "";
if (traceLevel >= 3) {
HttpMemoryLogger.txlog(adptGrpName + adptName,
"RECV " + "[" + paramValue + "]" + CommonLib.getDumpMessage(paramValue));
}
// json으로 변환
StringBuilder sb = new StringBuilder();
sb.append("{");
Map<String, String[]> paramMap = assignParameterMap(request, adptGrpName, adptName, null, transactionProp);
int i = 0;
for (Map.Entry<String, String[]> entry : paramMap.entrySet()) {
if (i > 0) {
sb.append(",");
}
sb.append("\"").append(entry.getKey()).append("\":");
String[] values = entry.getValue();
if (values.length > 1) {
// ["111", "222"]
sb.append("[");
for (int j = 0; j < values.length; j++) {
if (j > 0) {
sb.append(",");
}
sb.append("\"").append(JSONValue.escape(values[j])).append("\"");
}
sb.append("]");
} else {
sb.append("\"").append(JSONValue.escape(values[0])).append("\"");
}
i++;
}
sb.append("}");
paramValue = sb.toString();
if (logger.isDebug()) {
logger.debug("HttpAdapterServiceRest] RECV (" + adptGrpName + ") = [" + paramValue + "]\n"
+ CommonLib.getDumpMessage(paramValue.getBytes(encode)));
}
} else {
if (request.getContentLength() > 0) {
ServletInputStream sis = request.getInputStream();
ByteBuffer bb = ByteBuffer.allocate(1024).setAutoExpand(true);
int i = 0;
byte[] cbuf = new byte[1024];
while ((i = sis.read(cbuf, 0, 1024)) != -1) {
if (i == 1024) {
bb.put(cbuf);
} else {
byte[] tail = new byte[i];
System.arraycopy(cbuf, 0, tail, 0, i);
bb.put(tail);
}
}
byte[] data = new byte[bb.position()];
bb.position(0);
bb.get(data);
String bodyEncode = encode;
String contentTypeHeader = request.getContentType();
if (StringUtils.isNotBlank(contentTypeHeader)) {
try {
MediaType mediaType = MediaType.parseMediaType(contentTypeHeader);
if (mediaType.getCharset() != null) {
bodyEncode = mediaType.getCharset().name();
}
} catch (Exception ignored) {}
}
paramValue = new String(data, bodyEncode);
if (traceLevel >= 3) {
HttpMemoryLogger.txlog(adptGrpName + adptName,
"RECV " + "[" + paramValue + "]" + CommonLib.getDumpMessage(data));
}
if (logger.isDebug()) {
logger.debug("HttpAdapterServiceRest] RECV (" + adptGrpName + ") = [" + paramValue + "]\n"
+ CommonLib.getDumpMessage(data));
}
}
}
if (paramValue == null) { // parameter가 없는 경우때문에 처리
paramValue = "";
}
// 순수한 Body값을 저장을 위해 위치 변경.
transactionProp.put(INBOUND_REQUEST_MESSAGE, paramValue);
// paramValue가 null이 아닌 빈문자열(""," ")인 경우에 대비하여 조건 수정.
// if (StringUtils.isNotBlank(paramValue)) { // jwhong decrypt
// paramValue = doPreDecryption(paramValue, transactionProp, request);
// }
if ("Y".equals(urlDecodeYn) && isParameterType) {
message = URLDecoder.decode(paramValue);
} else {
message = paramValue;
}
TxFileLogger.logTxFile(transactionProp, message, "[IN_RECV]");
if (logger.isDebug()) {
String[] msgArgs = new String[2];
msgArgs[0] = adptGrpName;
msgArgs[1] = message;
String resMsg = ExceptionUtil.make("RICEAIAHA005", msgArgs);
logger.debug(resMsg);
}
adptMsgType = adapterVO.getAdapterGroupVO().getMessageType();
// HttpHeaders responseHeaders = new HttpHeaders();
// responseHeaders.setContentType(MediaType.valueOf("application/json;charset=" + encode));
// HEADER_GROUP 셋팅
// if (MessageType.JSON.equals(adptMsgType) && StringUtils.isNotBlank(headerGroupName)
// && StringUtils.isNotBlank(relayRequestHeaderKeys)) {
// JSONObject jsonMessage = (JSONObject) JSONValue.parse(message);
// JSONObject headerJson = new JSONObject();
// if (StringUtils.equalsIgnoreCase(relayRequestHeaderKeys, "ALL")) {
// for (Enumeration<String> e = request.getHeaderNames(); e.hasMoreElements(); ) {
// String key = e.nextElement();
// headerJson.put(key, request.getHeader(key));
// }
// } else {
// String[] relayKeyArr = org.springframework.util.StringUtils
// .tokenizeToStringArray(relayRequestHeaderKeys, ",");
//
// for (String key : relayKeyArr) {
// String headerValue = request.getHeader(key);
// if (StringUtils.isNotBlank(headerValue)) {
// headerJson.put(key, headerValue);
// }
// }
// }
//
// if (headerJson.size() > 0) {
// jsonMessage.put(headerGroupName, headerJson);
// message = jsonMessage.toJSONString();
// }
// }
Object reqObject = assignHeaderGroupRequestHeaders(adptGrpName, adptName, message,
transactionProp, request, response, adptMsgType);
// 위치변경 : 가공되지 않은 Body값을 저장하기 위하여 위쪽으로 이동.
// transactionProp.put(INBOUND_REQUEST_MESSAGE, message);
// 로컬 서비스 호출 ,encoding 처리 추가
ApiKeyExtractFilter apiKeyExtractor = new ApiKeyExtractFilter();
apiKeyExtractor.doPreFilter(adptGrpName, adptName, reqObject, transactionProp, request, response);
String result = (String) service(adptGrpName, adptName, reqObject, transactionProp, request, response);
// bypass만 필요
applyOutboundResponseHeaders(transactionProp, response);
result = applyHeaderGroupResponseHeaders(adptGrpName, adptName, result, transactionProp, request, response, adptMsgType);
if (logger.isDebug()) {
logger.debug("HttpAdapterServiceRest] result " + encode + " (" + adptGrpName + ") = [" + result + "]");
}
stopWatch.stop();
String syncAsyncType = transactionProp.getProperty(INBOUND_SYNC_ASYNC_TYPE);
if (!"ASYN".equals(syncAsyncType) && MessageType.JSON.equals(adptMsgType) && StringUtils.isNotBlank(headerGroupName)) {
ObjectMapper mapper = new ObjectMapper();
ObjectNode rootNode = (ObjectNode) mapper.readTree(result);
JsonNode headerGroup = rootNode.get(headerGroupName);
if(headerGroup != null) {
for(Iterator<String> it = headerGroup.fieldNames(); it.hasNext();) {
String name = it.next();
String value = headerGroup.get(name).asText();
response.addHeader(name, value);
}
rootNode.remove(headerGroupName);
result = mapper.writeValueAsString(rootNode);
}
}
// KJBank는 요청에 대한 응답 (Sync응답, Aync 에 대한 Ack응답) 에 대하여 암호화 하지 않는다. 무조건 안한다. 따라서 아래부분은 구현은 했지만 사용하지 않는다.
// 위 2가지 경우 암호화 하는걸로 요청 변경되어 아래 암호화 수행하도록 수정함
// encryptResponseApply = StringUtils.equalsIgnoreCase(httpProp.getProperty("ENCRYPT_RESPONSE_APPLY", "N"), "Y");
// if ( encryptResponseApply) {
// result = doPostEncryption(result, transactionProp, request); // jwhong Encrypt
// }
return result;
}
private Object assignHeaderGroupRequestHeaders(String adptGrpName, String adptName, Object message, Properties prop,
HttpServletRequest request, HttpServletResponse response, String adptMsgType) throws DocumentException, IOException {
// XML 지원 개발
String headerGroupName = prop.getProperty(HEADER_GROUP);
String relayRequestHeaderKeys = prop.getProperty(HEADER_KEYS);
if(StringUtils.isNotBlank(headerGroupName) && StringUtils.isNotBlank(relayRequestHeaderKeys)) {
if (MessageType.JSON.equals(adptMsgType)) {
if(message instanceof String && StringUtils.isEmpty((String)message))
message = "{}";
JSONObject jsonMessage = JSONUtils.parseJson(message);
JSONObject headerJson = JSONUtils.getChildJson(jsonMessage, headerGroupName);
if (headerJson == null)
headerJson = new JSONObject();
if (StringUtils.equalsIgnoreCase(relayRequestHeaderKeys, "ALL")) {
for (Enumeration<String> e = request.getHeaderNames(); e.hasMoreElements(); ) {
String key = e.nextElement();
headerJson.put(StringUtils.lowerCase(key), request.getHeader(key));
}
} else {
String[] relayKeyArr = org.springframework.util.StringUtils
.tokenizeToStringArray(relayRequestHeaderKeys, ",");
for (String key : relayKeyArr) {
String headerValue = request.getHeader(key);
if (StringUtils.isNotBlank(headerValue)) {
headerJson.put(StringUtils.lowerCase(key), headerValue);
}
}
}
if (headerJson.size() > 0) {
jsonMessage.put(headerGroupName, headerJson);
message = jsonMessage.toJSONString();
}
} else if(MessageType.XML.equals(adptMsgType)) {
Document document = XMLUtils.convertXmlDocument(message);
if(document != null) {
Element rootElement = document.getRootElement();
Element headerElement = rootElement.element(headerGroupName);
if(headerElement == null)
headerElement = DocumentHelper.createElement(headerGroupName);
boolean hasHeaderValues = false;
if (StringUtils.equalsIgnoreCase(relayRequestHeaderKeys, "ALL")) {
for (Enumeration<String> e = request.getHeaderNames(); e.hasMoreElements(); ) {
String key = e.nextElement();
String value = request.getHeader(key);
/*
* IBK 표준안에 따라 <{key}>{value}</{key}> 형태로 저장, KEY 는 소문자로 변경한다
* 대상 사이트의 표준안에 따라 작성 할 것
*
* HTTP 헤더 변환 예시
*
* 일반적인 HTTP 헤더 변환 예시:
* Content-Type: application/json
* X-Forwarded-For: 10.0.0.1
*
* <root>
* <data> original content </data>
* <httpHeaderGroup>
* <content-type>application/json</content-type>
* <x-forwarded-for>10.0.0.1</x-forwarded-for>
* </httpHeaderGroup>
* </root>
*/
if(StringUtils.isNotBlank(value)) {
Element headerItem = headerElement.addElement(key);
headerItem.setText(value);
hasHeaderValues = true;
}
}
} else {
String[] relayKeyArr = org.springframework.util.StringUtils
.tokenizeToStringArray(relayRequestHeaderKeys, ",");
for (String key : relayKeyArr) {
String headerValue = request.getHeader(key);
if (StringUtils.isNotBlank(headerValue)) {
String value = request.getHeader(key);
String keyName = StringUtils.lowerCase(key);
Element headerItem = headerElement.addElement(keyName);
headerItem.setText(value);
hasHeaderValues = true;
}
}
}
if (hasHeaderValues) {
rootElement.add(headerElement);
OutputFormat format = OutputFormat.createPrettyPrint();
StringWriter writer = new StringWriter();
XMLWriter xmlWriter = new XMLWriter(writer, format);
xmlWriter.write(document);
message = writer.toString();
}
}
}
}
if (message == null) {
message = "";
} // XML 지원 개발
return message;
}
/**
* HttpClientAdapterServiceBypass.assignRelayDataToInbound() 에서
* transactionProp에 저장한 OUTBOUND_RESPONSE_HEADERS를 response에 적용한다.
*/
@SuppressWarnings("unchecked")
private void applyOutboundResponseHeaders(Properties transactionProp, HttpServletResponse response) {
Map<String, Object> outboundPropertyMap = (Map<String, Object>) transactionProp.get(OUTBOUND_PROPERTY_MAP);
if (outboundPropertyMap == null) {
return;
}
Properties headerProp = (Properties) outboundPropertyMap.get(OUTBOUND_RESPONSE_HEADERS);
if (headerProp == null) {
return;
}
String[] hopByHopHeaders = { "Connection", "Keep-Alive", "Proxy-Authenticate", "Proxy-Authorization",
"TE", "Trailers", "Transfer-Encoding", "Upgrade" };
for (Map.Entry<Object, Object> entry : headerProp.entrySet()) {
String key = (String) entry.getKey();
String value = (String) entry.getValue();
if (StringUtils.equalsAnyIgnoreCase(key, hopByHopHeaders)) {
continue;
}
if (StringUtils.equalsIgnoreCase(key, HttpHeaders.CONTENT_LENGTH)) {
continue;
}
if (StringUtils.equalsIgnoreCase(key, HTTP_STATUS)) {
if (StringUtils.isNotBlank(value)) {
try {
response.setStatus(Integer.parseInt(value));
} catch (NumberFormatException ex) {
// ignore
}
}
continue;
}
response.addHeader(key, value);
}
}
private String applyHeaderGroupResponseHeaders(String adptGrpName, String adptName, Object resultMessage, Properties prop,
HttpServletRequest request, HttpServletResponse response, String adptMsgType) throws Exception {
// body headergroup -> http header 세팅
String headerGroupName = prop.getProperty(ApiAdapterService.HEADER_GROUP);
if (StringUtils.isNotBlank(headerGroupName)) {
if (resultMessage instanceof Node) {
adptMsgType = MessageType.XML;
} else if (resultMessage instanceof String) {
String strMessage = (String) resultMessage;
if (StringUtils.startsWith(strMessage, "<")) {
adptMsgType = MessageType.XML;
}
} else if (resultMessage instanceof byte[]) {
String strMessage = new String((byte[]) resultMessage);
if (StringUtils.startsWith(strMessage, "<")) {
adptMsgType = MessageType.XML;
}
}
if (MessageType.JSON.equals(adptMsgType)) {
JSONObject jsonObject = JSONUtils.parseJson(resultMessage);
JSONObject headerGroup = (JSONObject)jsonObject.get(headerGroupName);
if (headerGroup != null) {
Set<Map.Entry<String, Object>> fieldEntrySet = JSONUtils.getField(headerGroup);
for (Map.Entry<String, Object> fieldEntry : fieldEntrySet) {
logger.info("header group entry : {} , {}", fieldEntry.getKey(), fieldEntry.getValue());
if (fieldEntry.getValue() instanceof String) {
if(fieldEntry.getKey().equalsIgnoreCase("content-type")) {
logger.debug("set content-type {}: {}", fieldEntry.getKey(), fieldEntry.getValue());
response.setContentType((String) fieldEntry.getValue());
} else {
logger.debug("add header {}: {}", fieldEntry.getKey(), fieldEntry.getValue());
response.addHeader(fieldEntry.getKey(), (String) fieldEntry.getValue());
}
} else {
logger.info("fieldEntry value is not String. {}", fieldEntry.getValue());
}
}
} else {
logger.info("headerGroup is null");
}
jsonObject.remove(headerGroupName);
return jsonObject.toJSONString();
} else if(MessageType.XML.equals(adptMsgType)) {
Document document = XMLUtils.convertXmlDocument(resultMessage);
if(document != null) {
Element rootElement = document.getRootElement();
Element headerGroupElement = rootElement.element(headerGroupName);
if(headerGroupElement != null) {
//헤더그룹의 모든 하위 엘리먼트를 HTTP 응답 헤더로 설정
for(Iterator<Element> it = headerGroupElement.elementIterator(); it.hasNext();) {
Element headerElement = it.next();
String name = headerElement.getName();
String value = headerElement.getTextTrim();
if(StringUtils.isNoneBlank(value)) {
if(value.equalsIgnoreCase("content-type")) {
response.setContentType(value);
} else {
logger.debug("add header {}: {}", name, value);
response.addHeader(name, value);
}
} else {
logger.info("value is not String. {}", value);
}
}
} else {
logger.info("headerGroup is null");
}
// 헤더그룹 삭제
rootElement.remove(headerGroupElement);
return document.asXML();
}
}
} else {
logger.info("headerGroupName is blank");
}
return (String)resultMessage;
}
private Map<String, String[]> assignParameterMap(HttpServletRequest request, String adptGrpName, String adptName,
Object requestBytes, Properties prop) {
// PathVariable 체크
if (StringUtils.equalsAnyIgnoreCase(request.getMethod(), HttpMethod.GET.name(), HttpMethod.DELETE.name())
&& StringUtils.isBlank(request.getQueryString())) {
try {
String actionName = prop.getProperty(Processor.REQUEST_ACTION);
RequestAction action = ActionFactory.createAction(actionName);
action.setAdapterInfo(adptGrpName, adptName, prop);
String[] keys = action.perform(requestBytes);
String requestPath = keys[0];
// PathVariable 지원 추가
String ruledPath = StandardMessageUtil.getMatchedKey(requestPath, actionName);
if (!StringUtils.equals(requestPath, ruledPath) && StringUtils.contains(ruledPath, "{")) {
Map<String, String> paramMap = new AntPathMatcher().extractUriTemplateVariables(ruledPath,
requestPath);
if (paramMap != null && paramMap.size() > 0) {
Map<String, String[]> returnMap = new HashMap<>();
for (String key : paramMap.keySet()) {
if (StringUtils.equalsIgnoreCase(key, "method")) {
continue;
}
returnMap.put(key, new String[] { paramMap.get(key) });
}
return returnMap;
}
}
} catch (Exception e) {
logger.error(e.getMessage());
}
}
return request.getParameterMap();
}
private String getRewritePath(String extUri, String basePath) {
return StringUtils.removeStart(extUri, StringUtils.removeEnd(basePath, "/"));
}
private Map<String, String> assignPathVariables(HttpServletRequest request, String adptGrpName, String adptName,
Object requestBytes, Properties prop) {
Map<String, String> variablesMap = null;
// QueryString 있을때는 체크(X)
if (StringUtils.isBlank(request.getQueryString())) {
try {
String actionName = prop.getProperty(Processor.REQUEST_ACTION);
RequestAction action = ActionFactory.createAction(actionName);
action.setAdapterInfo(adptGrpName, adptName, prop);
String[] keys = action.perform(requestBytes);
String requestPath = keys[0];
// PathVariable 지원 추가
String ruledPath = StandardMessageUtil.getMatchedKey(requestPath, actionName);
if (!StringUtils.equals(requestPath, ruledPath) && StringUtils.contains(ruledPath, "{")) {
variablesMap = new AntPathMatcher().extractUriTemplateVariables(ruledPath, requestPath);
}
} catch (Exception e) {
logger.error(e.getMessage());
}
}
return variablesMap;
}
private Properties getHeaders(HttpServletRequest request) {
Properties prop = new Properties();
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String key = headerNames.nextElement();
// 동일 이름의 헤더가 여러 개인 경우 콤마로 합침 (RFC 7230)
Enumeration<String> values = request.getHeaders(key);
StringBuilder sb = new StringBuilder();
while (values.hasMoreElements()) {
if (sb.length() > 0) sb.append(", ");
sb.append(values.nextElement());
}
prop.setProperty(key, sb.toString());
}
return prop;
}
private String getExtUri(HttpServletRequest request) {
String orgUri = request.getRequestURI().replaceAll(request.getContextPath(), "");
String uri = getExtUri(orgUri, 3);
if (uri != null && uri.trim().length() > 0) {
return "/" + uri;
} else {
return "";
}
}
private static String getExtUri(String url, int length) {
String[] urls = url.split("/");
List<String> newUrls = new ArrayList<>();
Collections.addAll(newUrls, urls);
return StringUtils.join(newUrls.subList(length, urls.length).toArray(), "/");
}
@Override
@Deprecated
public void service(String adptGrpName, String adptName, HttpServletRequest request, HttpServletResponse response) {
}
/**
* HTTP 요청에서 클라이언트 IP를 추출합니다.
* X-Forwarded-For 헤더가 있는 경우 이를 우선적으로 사용하고,
* 없는 경우 remoteAddr을 사용합니다.
*/
private String getClientIp(HttpServletRequest request) {
String ipAddress = request.getHeader("X-Forwarded-For");
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("WL-Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("HTTP_CLIENT_IP");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getRemoteAddr();
}
return ipAddress;
}
private String assignApiId(String adptGrpName, String adptName, Object message, Properties prop,
HttpServletRequest request) {
String apiId = null;
try {
String actionName = prop.getProperty(Processor.REQUEST_ACTION);
RequestAction action = ActionFactory.createAction(actionName);
action.setAdapterInfo(adptGrpName, adptName, prop);
String[] keys = action.perform(message);
apiId = keys[0];
// PathVariable 지원 추가
apiId = StandardMessageUtil.getMatchedKey(apiId, actionName);
} catch (Exception e) {
logger.error(e.getMessage());
}
// // header 에서 확보
// String apiId = request.getHeader(HEADER_NAME_API_CODE);
// if (StringUtils.isBlank(apiId)) {
// // url에서 확보
// // /ONLWeb/api/v1/public/getUserInfo.svc/
// apiId = request.getRequestURI();
// // /ONLWeb/api/v1/public/getUserInfo.svc
// apiId = StringUtils.removeEnd(apiId, "/");
// // getUserInfo.svc
// apiId = StringUtils.substringAfterLast(apiId, "/");
// }
return apiId;
}
}
@@ -0,0 +1,500 @@
package com.eactive.eai.adapter.service;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.StopWatch;
import org.apache.mina.common.ByteBuffer;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
import org.springframework.context.annotation.Primary;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.util.AntPathMatcher;
import com.eactive.eai.adapter.AdapterGroupVO;
import com.eactive.eai.adapter.AdapterVO;
import com.eactive.eai.adapter.Keys;
import com.eactive.eai.adapter.http.HttpMemoryLogger;
import com.eactive.eai.adapter.http.HttpMethodType;
import com.eactive.eai.adapter.http.dynamic.HttpAdapterServiceSupport;
import com.eactive.eai.common.context.ElinkTransactionContext;
import com.eactive.eai.common.exception.ExceptionUtil;
import com.eactive.eai.common.message.MessageType;
import com.eactive.eai.common.util.CommonLib;
import com.eactive.eai.common.util.Logger;
import com.eactive.eai.inbound.action.ActionFactory;
import com.eactive.eai.inbound.action.RequestAction;
import com.eactive.eai.inbound.processor.Processor;
import com.eactive.eai.message.StandardMessageUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
@Primary
@Service
public class DJErpApiAdapterService extends HttpAdapterServiceSupport {
static Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);
public static final String HEADER_GROUP = "HEADER_GROUP";
public static final String HTTP_STATUS = "HTTP_STATUS";
public static final String HEADER_KEYS = "HEADER_KEYS";
public static final String PROPERTIES_NAME_HTTP_REQUEST_METHOD = "httpRequestMethod";
public String callApi(HttpServletRequest request, HttpServletResponse response, Properties httpProp, AdapterGroupVO adapterGroupVO, AdapterVO adapterVO, Properties transactionProp) throws Exception {
int traceLevel = 0;
String adptGrpName = adapterGroupVO.getName();
String adptName = adapterVO.getName();
String urlDecodeYn = httpProp.getProperty(URL_DECODE_YN, "N");
String encode = StringUtils.defaultIfBlank(adapterGroupVO.getMessageEncode(), "UTF-8");
String traceLevelTemp = httpProp.getProperty(TRACE_LEVEL, "0");
String relayRequestHeaderKeys = httpProp.getProperty(HEADER_KEYS);
String headerGroupName = httpProp.getProperty(HEADER_GROUP);
boolean isParameterType = false;
String message = null;
String paramValue = null;
String adptMsgType = null;
transactionProp.put(INBOUND_METHOD, request.getMethod());
transactionProp.put(INBOUND_URI, request.getRequestURI());
transactionProp.put(INBOUND_QUERY_STRING, StringUtils.defaultString(request.getQueryString()));
transactionProp.put(INBOUND_HEADER, getHeaders(request));
transactionProp.put(INBOUND_EXTPARAMS, StringUtils.defaultString(request.getQueryString()));
if (StringUtils.equals(adapterVO.getAdapterGroupVO().getType(), Keys.TYPE_REST)
|| StringUtils.equals(adapterVO.getAdapterGroupVO().getType(), Keys.TYPE_HTTP_CUSTOM)) {
// /api/v1/public/getUserInfo.svc
String extUrl = StringUtils.removeStart(request.getRequestURI(), request.getContextPath());
transactionProp.put(INBOUND_EXTURI, extUrl);
} else {
transactionProp.put(INBOUND_EXTURI, getExtUri(request));
}
transactionProp.put(INBOUND_CLIENT_IP, getClientIp(request));
transactionProp.put(Processor.REQUEST_ACTION, adapterVO.getAdapterGroupVO().getRefClass());
transactionProp.put(API_PATH, httpProp.getProperty(API_PATH, ""));
transactionProp.put(INBOUND_REWRITE_PATH,
getRewritePath(transactionProp.getProperty(INBOUND_EXTURI), transactionProp.getProperty(API_PATH)));
transactionProp.put(INBOUND_REMOTE_ADDR, request.getRemoteAddr());
transactionProp.put(INBOUND_SCHEME, request.getScheme());
transactionProp.put(PRE_FILTERS, httpProp.getProperty(PRE_FILTERS, ""));
transactionProp.put(POST_FILTERS, httpProp.getProperty(POST_FILTERS, ""));
transactionProp.put(PROPERTIES_NAME_HTTP_REQUEST_METHOD, request.getMethod());
transactionProp.put(ALLOW_IP, httpProp.getProperty(ALLOW_IP, ""));
transactionProp.put("BLOCK_IP", httpProp.getProperty("BLOCK_IP", ""));
Map<String, String> pathVariables = assignPathVariables(request, adptGrpName, adptName, null, transactionProp);
if (pathVariables != null) {
transactionProp.put(INBOUND_PATH_VARIABLES, pathVariables);
}
transactionProp.put(ENC_PATHS, httpProp.getProperty(ENC_PATHS, ""));
transactionProp.put(ENCRYPT_ALGORITHM, httpProp.getProperty(ENCRYPT_ALGORITHM, ""));
if (getHeaders(request).get(HEADER_NAME_CLIENT_ID) != null) {
transactionProp.put(HEADER_NAME_CLIENT_ID, getHeaders(request).get(HEADER_NAME_CLIENT_ID));
}
transactionProp.put(ENCRYPT_BASE_TYPE, httpProp.getProperty(ENCRYPT_BASE_TYPE, ""));
String inboundToken = getHeaders(request).getOrDefault(INBOUND_TOKEN, "").toString();
if (StringUtils.isNotBlank(inboundToken) && StringUtils.startsWith(inboundToken, "Bearer ")) {
inboundToken = inboundToken.substring(7);
}
transactionProp.put(INBOUND_TOKEN, inboundToken);
transactionProp.put(ENCRYPT_AES256_IV, httpProp.getProperty(ENCRYPT_AES256_IV, ""));
transactionProp.put(ENCRYPT_AES256_KEY, httpProp.getProperty(ENCRYPT_AES256_KEY, ""));
// SEED 컬럼암호화 시 Key로 사용함
String seedkey = getHeaders(request).getOrDefault("x-obp-partnercode", "").toString();
ElinkTransactionContext.setSeedKey(seedkey);
try {
traceLevel = Integer.parseInt(traceLevelTemp);
} catch (Exception e) {
traceLevel = 0;
}
StopWatch stopWatch = new StopWatch();
stopWatch.start();
logger.debug("시작 >> encode = [" + encode + "]");
switch (HttpMethodType.getValue(request.getMethod())) {
case GET:
case DELETE:
isParameterType = true;
break;
case POST:
case PUT:
if (StringUtils.contains(request.getContentType(), "application/x-www-form-urlencoded")) {
isParameterType = true;
} else if (StringUtils.isNoneBlank(request.getQueryString())) {
isParameterType = true;
} else {
isParameterType = false;
}
break;
default:
break;
}
if (isParameterType) {
paramValue = request.getQueryString();
transactionProp.put(INBOUND_QUERY_STRING, StringUtils.defaultString(paramValue));
if (paramValue == null)
paramValue = "";
if (traceLevel >= 3) {
HttpMemoryLogger.txlog(adptGrpName + adptName,
"RECV " + "[" + paramValue + "]" + CommonLib.getDumpMessage(paramValue));
}
// json으로 변환
StringBuilder sb = new StringBuilder();
sb.append("{");
Map<String, String[]> paramMap = assignParameterMap(request, adptGrpName, adptName, null, transactionProp);
int i = 0;
for (Map.Entry<String, String[]> entry : paramMap.entrySet()) {
if (i > 0) {
sb.append(",");
}
sb.append("\"").append(entry.getKey()).append("\":");
String[] values = entry.getValue();
if (values.length > 1) {
// ["111", "222"]
sb.append("[");
for (int j = 0; j < values.length; j++) {
if (j > 0) {
sb.append(",");
}
sb.append("\"").append(JSONValue.escape(values[j])).append("\"");
}
sb.append("]");
} else {
sb.append("\"").append(JSONValue.escape(values[0])).append("\"");
}
i++;
}
sb.append("}");
paramValue = sb.toString();
if (logger.isDebug()) {
logger.debug("HttpAdapterServiceRest] RECV (" + adptGrpName + ") = [" + paramValue + "]\n"
+ CommonLib.getDumpMessage(paramValue.getBytes(encode)));
}
} else {
if (request.getContentLength() > 0) {
ServletInputStream sis = request.getInputStream();
ByteBuffer bb = ByteBuffer.allocate(1024).setAutoExpand(true);
int i = 0;
byte[] cbuf = new byte[1024];
while ((i = sis.read(cbuf, 0, 1024)) != -1) {
if (i == 1024) {
bb.put(cbuf);
} else {
byte[] tail = new byte[i];
System.arraycopy(cbuf, 0, tail, 0, i);
bb.put(tail);
}
}
byte[] data = new byte[bb.position()];
bb.position(0);
bb.get(data);
String bodyEncode = encode;
String contentTypeHeader = request.getContentType();
if (StringUtils.isNotBlank(contentTypeHeader)) {
try {
MediaType mediaType = MediaType.parseMediaType(contentTypeHeader);
if (mediaType.getCharset() != null) {
bodyEncode = mediaType.getCharset().name();
}
} catch (Exception ignored) {}
}
paramValue = new String(data, bodyEncode);
if (traceLevel >= 3) {
HttpMemoryLogger.txlog(adptGrpName + adptName,
"RECV " + "[" + paramValue + "]" + CommonLib.getDumpMessage(data));
}
if (logger.isDebug()) {
logger.debug("HttpAdapterServiceRest] RECV (" + adptGrpName + ") = [" + paramValue + "]\n"
+ CommonLib.getDumpMessage(data));
}
}
}
if (paramValue == null) {
paramValue = "";
}
transactionProp.put(INBOUND_REQUEST_MESSAGE, paramValue);
if ("Y".equals(urlDecodeYn) && isParameterType) {
message = URLDecoder.decode(paramValue);
} else {
message = paramValue;
}
if (logger.isDebug()) {
String[] msgArgs = new String[2];
msgArgs[0] = adptGrpName;
msgArgs[1] = message;
String resMsg = ExceptionUtil.make("RICEAIAHA005", msgArgs);
logger.debug(resMsg);
}
adptMsgType = adapterVO.getAdapterGroupVO().getMessageType();
// HEADER_GROUP 셋팅
if (MessageType.JSON.equals(adptMsgType) && StringUtils.isNotBlank(headerGroupName)
&& StringUtils.isNotBlank(relayRequestHeaderKeys)) {
JSONObject jsonMessage = (JSONObject) JSONValue.parse(message);
JSONObject headerJson = new JSONObject();
if (StringUtils.equalsIgnoreCase(relayRequestHeaderKeys, "ALL")) {
for (Enumeration<String> e = request.getHeaderNames(); e.hasMoreElements(); ) {
String key = e.nextElement();
headerJson.put(key, request.getHeader(key));
}
} else {
String[] relayKeyArr = org.springframework.util.StringUtils
.tokenizeToStringArray(relayRequestHeaderKeys, ",");
for (String key : relayKeyArr) {
String headerValue = request.getHeader(key);
if (StringUtils.isNotBlank(headerValue)) {
headerJson.put(key, headerValue);
}
}
}
if (headerJson.size() > 0) {
jsonMessage.put(headerGroupName, headerJson);
message = jsonMessage.toJSONString();
}
}
if (message == null) {
message = "";
}
String result = (String) service(adptGrpName, adptName, message, transactionProp, request, response);
applyOutboundResponseHeaders(transactionProp, response);
if (logger.isDebug()) {
logger.debug("HttpAdapterServiceRest] result " + encode + " (" + adptGrpName + ") = [" + result + "]");
}
stopWatch.stop();
String syncAsyncType = transactionProp.getProperty(INBOUND_SYNC_ASYNC_TYPE);
if (!"ASYN".equals(syncAsyncType) && MessageType.JSON.equals(adptMsgType) && StringUtils.isNotBlank(headerGroupName)) {
ObjectMapper mapper = new ObjectMapper();
ObjectNode rootNode = (ObjectNode) mapper.readTree(result);
JsonNode headerGroup = rootNode.get(headerGroupName);
if(headerGroup != null) {
for(Iterator<String> it = headerGroup.fieldNames(); it.hasNext();) {
String name = it.next();
String value = headerGroup.get(name).asText();
response.addHeader(name, value);
}
rootNode.remove(headerGroupName);
result = mapper.writeValueAsString(rootNode);
}
}
return result;
}
/**
* HttpClientAdapterServiceBypass.assignRelayDataToInbound() 에서
* transactionProp에 저장한 OUTBOUND_RESPONSE_HEADERS를 response에 적용한다.
*/
@SuppressWarnings("unchecked")
private void applyOutboundResponseHeaders(Properties transactionProp, HttpServletResponse response) {
Map<String, Object> outboundPropertyMap = (Map<String, Object>) transactionProp.get(OUTBOUND_PROPERTY_MAP);
if (outboundPropertyMap == null) {
return;
}
Properties headerProp = (Properties) outboundPropertyMap.get(OUTBOUND_RESPONSE_HEADERS);
if (headerProp == null) {
return;
}
String[] hopByHopHeaders = { "Connection", "Keep-Alive", "Proxy-Authenticate", "Proxy-Authorization",
"TE", "Trailers", "Transfer-Encoding", "Upgrade" };
for (Map.Entry<Object, Object> entry : headerProp.entrySet()) {
String key = (String) entry.getKey();
String value = (String) entry.getValue();
if (StringUtils.equalsAnyIgnoreCase(key, hopByHopHeaders)) {
continue;
}
if (StringUtils.equalsIgnoreCase(key, HttpHeaders.CONTENT_LENGTH)) {
continue;
}
if (StringUtils.equalsIgnoreCase(key, HTTP_STATUS)) {
if (StringUtils.isNotBlank(value)) {
try {
response.setStatus(Integer.parseInt(value));
} catch (NumberFormatException ex) {
// ignore
}
}
continue;
}
response.addHeader(key, value);
}
}
private Map<String, String[]> assignParameterMap(HttpServletRequest request, String adptGrpName, String adptName,
Object requestBytes, Properties prop) {
// PathVariable 체크
if (StringUtils.equalsAnyIgnoreCase(request.getMethod(), HttpMethod.GET.name(), HttpMethod.DELETE.name())
&& StringUtils.isBlank(request.getQueryString())) {
try {
String actionName = prop.getProperty(Processor.REQUEST_ACTION);
RequestAction action = ActionFactory.createAction(actionName);
action.setAdapterInfo(adptGrpName, adptName, prop);
String[] keys = action.perform(requestBytes);
String requestPath = keys[0];
// PathVariable 지원 추가
String ruledPath = StandardMessageUtil.getMatchedKey(requestPath, actionName);
if (!StringUtils.equals(requestPath, ruledPath) && StringUtils.contains(ruledPath, "{")) {
Map<String, String> paramMap = new AntPathMatcher().extractUriTemplateVariables(ruledPath,
requestPath);
if (paramMap != null && paramMap.size() > 0) {
Map<String, String[]> returnMap = new HashMap<>();
for (String key : paramMap.keySet()) {
if (StringUtils.equalsIgnoreCase(key, "method")) {
continue;
}
returnMap.put(key, new String[] { paramMap.get(key) });
}
return returnMap;
}
}
} catch (Exception e) {
logger.error(e.getMessage());
}
}
return request.getParameterMap();
}
private String getRewritePath(String extUri, String basePath) {
return StringUtils.removeStart(extUri, StringUtils.removeEnd(basePath, "/"));
}
private Map<String, String> assignPathVariables(HttpServletRequest request, String adptGrpName, String adptName,
Object requestBytes, Properties prop) {
Map<String, String> variablesMap = null;
// QueryString 있을때는 체크(X)
if (StringUtils.isBlank(request.getQueryString())) {
try {
String actionName = prop.getProperty(Processor.REQUEST_ACTION);
RequestAction action = ActionFactory.createAction(actionName);
action.setAdapterInfo(adptGrpName, adptName, prop);
String[] keys = action.perform(requestBytes);
String requestPath = keys[0];
// PathVariable 지원 추가
String ruledPath = StandardMessageUtil.getMatchedKey(requestPath, actionName);
if (!StringUtils.equals(requestPath, ruledPath) && StringUtils.contains(ruledPath, "{")) {
variablesMap = new AntPathMatcher().extractUriTemplateVariables(ruledPath, requestPath);
}
} catch (Exception e) {
logger.error(e.getMessage());
}
}
return variablesMap;
}
private Properties getHeaders(HttpServletRequest request) {
Properties prop = new Properties();
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String key = headerNames.nextElement();
// 동일 이름의 헤더가 여러 개인 경우 콤마로 합침 (RFC 7230)
Enumeration<String> values = request.getHeaders(key);
StringBuilder sb = new StringBuilder();
while (values.hasMoreElements()) {
if (sb.length() > 0) sb.append(", ");
sb.append(values.nextElement());
}
prop.setProperty(key, sb.toString());
}
return prop;
}
private String getExtUri(HttpServletRequest request) {
String orgUri = request.getRequestURI().replaceAll(request.getContextPath(), "");
String uri = getExtUri(orgUri, 3);
if (uri != null && uri.trim().length() > 0) {
return "/" + uri;
} else {
return "";
}
}
private static String getExtUri(String url, int length) {
String[] urls = url.split("/");
List<String> newUrls = new ArrayList<>();
Collections.addAll(newUrls, urls);
return StringUtils.join(newUrls.subList(length, urls.length).toArray(), "/");
}
@Override
@Deprecated
public void service(String adptGrpName, String adptName, HttpServletRequest request, HttpServletResponse response) {
}
private String getClientIp(HttpServletRequest request) {
String ipAddress = request.getHeader("X-Forwarded-For");
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("WL-Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("HTTP_CLIENT_IP");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getRemoteAddr();
}
return ipAddress;
}
}
@@ -1,60 +0,0 @@
package com.eactive.eai.agent.inflow;
import com.eactive.eai.agent.command.Command;
import com.eactive.eai.agent.command.CommandException;
import com.eactive.eai.common.inflow.AbstractInflowControlManager;
import com.eactive.eai.common.inflow.InflowControlUtil;
import com.eactive.eai.common.util.Logger;
import com.eactive.eai.custom.inflow.ClientDualInflowControlManager;
public class ReloadInflowClientControlCommand extends Command {
private static final long serialVersionUID = 1L;
public ReloadInflowClientControlCommand() {
super("ReloadInflowClientControlCommand");
}
public Object execute() throws CommandException {
Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT);
if (logger.isInfo())
logger.info(this.name + " is executed");
if (!(args instanceof String)) {
String rspErrorCode = "RECEAIMCM001";
String msg = makeException(rspErrorCode, null);
if (logger.isError())
logger.error(msg);
throw new CommandException(msg);
}
String keyName = (String) args;
try {
AbstractInflowControlManager base = InflowControlUtil.getInflowControlManager();
if (!(base instanceof ClientDualInflowControlManager)) {
throw new IllegalStateException("ClientDualInflowControlManager 가 활성화되지 않았습니다.");
}
ClientDualInflowControlManager manager = (ClientDualInflowControlManager) base;
if (keyName != null) {
if ("ALL".equals(keyName)) {
manager.reloadClient();
if (logger.isWarn())
logger.warn(this.name + "] all rule Reload.");
} else {
manager.reloadClient(keyName);
if (logger.isWarn())
logger.warn(this.name + "] " + keyName + " Reload.");
}
}
} catch (Exception e) {
String rspErrorCode = "RECEAIMCM002";
String msg = makeException(rspErrorCode, e);
if (logger.isError())
logger.error(msg, e);
throw new CommandException(msg);
}
return "success";
}
}
@@ -1,50 +0,0 @@
package com.eactive.eai.agent.inflow;
import com.eactive.eai.agent.command.Command;
import com.eactive.eai.agent.command.CommandException;
import com.eactive.eai.common.inflow.AbstractInflowControlManager;
import com.eactive.eai.common.inflow.InflowControlUtil;
import com.eactive.eai.common.util.Logger;
import com.eactive.eai.custom.inflow.ClientDualInflowControlManager;
public class RemoveInflowClientControlCommand extends Command {
private static final long serialVersionUID = 1L;
public RemoveInflowClientControlCommand() {
super("RemoveInflowClientControlCommand");
}
public Object execute() throws CommandException {
Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT);
if (logger.isInfo())
logger.info(this.name + " is executed");
if (!(args instanceof String)) {
String rspErrorCode = "RECEAIMCM001";
String msg = makeException(rspErrorCode, null);
if (logger.isError())
logger.error(msg);
throw new CommandException(msg);
}
String key = (String) args;
try {
AbstractInflowControlManager base = InflowControlUtil.getInflowControlManager();
if (!(base instanceof ClientDualInflowControlManager)) {
throw new IllegalStateException("ClientDualInflowControlManager 가 활성화되지 않았습니다.");
}
ClientDualInflowControlManager manager = (ClientDualInflowControlManager) base;
manager.removeClient(key);
if (logger.isWarn())
logger.warn(this.name + "] " + key + " removed.");
} catch (Exception e) {
String rspErrorCode = "RECEAIMCM002";
String msg = makeException(rspErrorCode, e);
if (logger.isError())
logger.error(msg, e);
throw new CommandException(msg);
}
return "success";
}
}
@@ -1,8 +1,6 @@
package com.eactive.eai.authserver.config;
import java.security.KeyPair;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.time.LocalDateTime;
import java.util.Arrays;
@@ -37,7 +35,6 @@ import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;
import com.eactive.eai.authserver.dao.TokenIssuanceLogDAO;
import com.eactive.eai.authserver.jwt.PssJwtAccessTokenConverter;
import com.eactive.eai.authserver.service.OAuth2Manager;
import com.eactive.eai.authserver.vo.ClientVO;
import com.eactive.eai.common.dao.DAOException;
@@ -142,10 +139,9 @@ public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdap
Resource keystore = new ClassPathResource(keystorePath);
KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(keystore, keystorePassword.toCharArray());
KeyPair keyPair = keyStoreKeyFactory.getKeyPair(keyAlias, keyPassword.toCharArray());
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
return new PssJwtAccessTokenConverter(privateKey, publicKey);
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setKeyPair(keyPair);
return converter;
}
@@ -0,0 +1,200 @@
package com.eactive.eai.authserver.custom;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.json.simple.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import com.eactive.eai.adapter.http.dynamic.filter.JwtAuthException;
import com.eactive.eai.authserver.config.RequestContextData;
import com.eactive.eai.authserver.dao.TokenIssuanceLogDAO;
import com.eactive.eai.authserver.service.BearerTokenService;
import com.eactive.eai.authserver.service.OAuth2Manager;
import com.eactive.eai.authserver.vo.ClientVO;
import com.eactive.eai.common.logger.EAIDBLogControl;
import com.eactive.eai.common.util.Logger;
import com.eactive.eai.common.util.MessageUtil;
import com.eactive.eai.data.entity.onl.authserver.TokenIssuanceLog;
@RestController
@RequestMapping("/auth/oauth/v2")
public class BearerTokenController {
public static Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT);
private final BearerTokenService tokenService;
@Autowired
private TokenIssuanceLogDAO tokenIssuanceLogDAO;
public BearerTokenController(BearerTokenService tokenService) {
this.tokenService = tokenService;
}
@RequestMapping(
value = "/token",
method = {RequestMethod.POST, RequestMethod.GET}
)
public ResponseEntity<?> issueCAToken(HttpServletRequest request, @RequestParam MultiValueMap<String, String> req) throws JwtAuthException {
JSONObject resObject = new JSONObject();
String clientId = req.getFirst("client_id");
String grantType = req.getFirst("grant_type");
String clientSecret = req.getFirst("client_secret");
String scopes = req.getFirst("scope");
try {
RequestContextData data = new RequestContextData();
data.setClientId(request.getParameter("client_id"));
data.setIpAddress(extractIpAddress(request));
data.setGrantType(request.getParameter("grant_type"));
data.setScope(request.getParameter("scope"));
data.setUsername(request.getParameter("username"));
data.setResource(request.getParameter("resource"));
RequestContextData.ThreadLocalRequestContext.set(data);
if (!StringUtils.equals(grantType, "client_credentials") ) {
throw new JwtAuthException("unsupported_grant_type", "Unsupported grant type");
}
ClientDetails clientDetails = OAuth2Manager.getInstance().getClientDeatilsStore().get(clientId);
if (clientDetails == null) {
throw new JwtAuthException("invalid_client", "Bad client credentials(not found)");
}
String tokenType = "Bearer";
Long expiresIn = Long.valueOf(clientDetails.getAccessTokenValiditySeconds());
String[] reqScopeArr = org.springframework.util.StringUtils.tokenizeToStringArray(scopes, " ");
Set<String> scopeSet = new HashSet<String>();
for (String scope : reqScopeArr) {
scopeSet.add(scope);
}
veryfyClient(clientDetails, clientSecret, scopeSet);
String token = tokenService.generateCAToken(clientId, expiresIn, scopeSet);
Map<String, Object> tokenMap = new HashMap<>();
tokenMap.put("token_type", tokenType);
tokenMap.put("access_token", token);
tokenMap.put("expires_in", expiresIn);
tokenMap.put("scope", scopes);
// ... and so on for all key-value pairs
resObject.putAll(tokenMap);
logTokenIssuance(true,false, resObject.toJSONString(), token);
return ResponseEntity.ok(resObject);
} catch (JwtAuthException e) {
logger.error("Token request[/auth/oauth/v2/token] => clientId: {}, grantType: {}, scope: {}",
clientId, grantType, scopes);
logger.error(e);
String errorJson = MessageUtil.makeJsonErrorMessage(MessageUtil.ERROR_CODE_AUTH_FAIL, e.getMessage());
logTokenIssuance(false ,false, e.getMessage(), null);
return ResponseEntity.status(401).body(errorJson);
} catch (Exception e) {
logger.error("Token request[/auth/oauth/v2/token] => clientId: {}, grantType: {}, scope: {}",
clientId, grantType, scopes);
logger.error(e);
String errorJson = MessageUtil.makeJsonErrorMessage(MessageUtil.ERROR_CODE_AP_ERROR, e.getMessage());
logTokenIssuance(false ,false, e.getMessage(), null);
return ResponseEntity.status(500).body(errorJson);
}finally {
RequestContextData.ThreadLocalRequestContext.clear();
}
}
private String extractIpAddress(HttpServletRequest request) {
String ipAddress = request.getHeader("X-Forwarded-For");
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("WL-Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("HTTP_CLIENT_IP");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getRemoteAddr();
}
return ipAddress;
}
private void logTokenIssuance(boolean isSuccess, boolean isRestrictionExempt, String resultMessage, String accessToken) {
try {
if (!EAIDBLogControl.isEnable()) {
logger.warn("DB logging is disabled. Skipping token issuance logging.");
return;
}
RequestContextData data = RequestContextData.ThreadLocalRequestContext.get();
OAuth2Manager.getInstance();
TokenIssuanceLog log = new TokenIssuanceLog();
String clientId = data.getClientId();
log.setClientId(clientId);
if(StringUtils.isNotBlank(clientId)) {
ClientVO clientVO = OAuth2Manager.getInstance().getClientInfo(clientId);
log.setAppName(clientVO.getClientName());
log.setOrgId(clientVO.getOrgId());
log.setOrgName(clientVO.getOrgName());
}
log.setGrantType(data.getGrantType());
log.setScope(data.getScope());
log.setIpAddress(data.getIpAddress());
log.setSuccessYn(isSuccess ? "Y" : "N");
log.setIssuanceDateTime(LocalDateTime.now());
log.setRestrictionExemptYn(isRestrictionExempt ? "Y" : "N");
log.setResultMessage(resultMessage);
log.setAccessToken(accessToken); // Add access token value to the log
tokenIssuanceLogDAO.saveTokenIssuanceLog(log);
} catch (Throwable th) {
logger.error("Error while logging token issuance: " + th.getMessage(), th);
}
}
private void veryfyClient(ClientDetails clientDetails, String clientSecret, Set<String> scopeSet) throws JwtAuthException {
if (!StringUtils.equals(clientDetails.getClientSecret(), clientSecret)) {
throw new JwtAuthException("invalid_client", "Bad client credentials(client_secret is empty or not match)");
}
if (scopeSet.isEmpty()) {
throw new JwtAuthException("invalid_client", "Bad client credentials(Scope is empty)");
}
if (!clientDetails.getScope().containsAll(scopeSet)) {
throw new JwtAuthException("invalid_client", "Bad client credentials(Include unacceptable scope)");
}
}
}
@@ -5,7 +5,7 @@ import java.io.Serializable;
import com.fasterxml.jackson.annotation.JsonProperty;
@SuppressWarnings("serial")
public class DJBOAuth2AccessTokenRequest implements Serializable {
public class DJErpOAuth2AccessTokenRequest implements Serializable {
@JsonProperty("grant_type")
private String grantType;
@@ -10,7 +10,7 @@ import com.fasterxml.jackson.annotation.JsonInclude.Include;
@SuppressWarnings("serial")
@JsonInclude(Include.NON_NULL)
@JsonPropertyOrder({ "access_token", "token_type", "expires_in", "scope", "jti", "client_id" })
public class DJBOAuth2AccessTokenResponse implements Serializable {
public class DJErpOAuth2AccessTokenResponse implements Serializable {
@JsonProperty("access_token")
private String accessToken;
@@ -46,7 +46,7 @@ import com.eactive.eai.data.entity.onl.authserver.TokenIssuanceLog;
import com.fasterxml.jackson.databind.ObjectMapper;
@Controller
public class DJBOAuth2Controller {
public class DJErpOAuth2Controller {
public static Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT);
private static final String HEADER_TRACEID = "traceId";
@@ -58,7 +58,7 @@ public class DJBOAuth2Controller {
@RequestMapping(value = { "/dj/oauth/token", "/dj/oauth2/token" }, method = RequestMethod.POST,
consumes = "application/json", produces = "application/json; charset=UTF-8")
@ResponseBody
public ResponseEntity<?> tokenJson(@RequestBody DJBOAuth2AccessTokenRequest tokenRequest,
public ResponseEntity<?> tokenJson(@RequestBody DJErpOAuth2AccessTokenRequest tokenRequest,
HttpServletRequest request, HttpServletResponse response) {
return issueToken(tokenRequest, request, response);
}
@@ -68,7 +68,7 @@ public class DJBOAuth2Controller {
@ResponseBody
public ResponseEntity<?> tokenForm(@RequestParam Map<String, String> params,
HttpServletRequest request, HttpServletResponse response) {
DJBOAuth2AccessTokenRequest tokenRequest = new DJBOAuth2AccessTokenRequest();
DJErpOAuth2AccessTokenRequest tokenRequest = new DJErpOAuth2AccessTokenRequest();
tokenRequest.setGrantType(params.get("grant_type"));
tokenRequest.setClientId(params.get("client_id"));
tokenRequest.setClientSecret(params.get("client_secret"));
@@ -80,7 +80,7 @@ public class DJBOAuth2Controller {
@ResponseBody
public ResponseEntity<?> tokenGet(@RequestParam Map<String, String> params,
HttpServletRequest request, HttpServletResponse response) {
DJBOAuth2AccessTokenRequest tokenRequest = new DJBOAuth2AccessTokenRequest();
DJErpOAuth2AccessTokenRequest tokenRequest = new DJErpOAuth2AccessTokenRequest();
tokenRequest.setGrantType(params.get("grant_type"));
tokenRequest.setClientId(params.get("client_id"));
tokenRequest.setClientSecret(params.get("client_secret"));
@@ -88,7 +88,7 @@ public class DJBOAuth2Controller {
return issueToken(tokenRequest, request, response);
}
private ResponseEntity<?> issueToken(DJBOAuth2AccessTokenRequest tokenRequest,
private ResponseEntity<?> issueToken(DJErpOAuth2AccessTokenRequest tokenRequest,
HttpServletRequest request, HttpServletResponse response) {
if (logger.isDebug()) {
@@ -143,7 +143,7 @@ public class DJBOAuth2Controller {
ResponseEntity<OAuth2AccessToken> result = tokenEndpoint().postAccessToken(principal, authorizationParameters);
OAuth2AccessToken token = result.getBody();
DJBOAuth2AccessTokenResponse responseToken = new DJBOAuth2AccessTokenResponse();
DJErpOAuth2AccessTokenResponse responseToken = new DJErpOAuth2AccessTokenResponse();
responseToken.setAccessToken(token.getValue());
responseToken.setTokenType(token.getTokenType());
responseToken.setExpiresIn(Long.valueOf(token.getExpiresIn()));
@@ -0,0 +1,63 @@
package com.eactive.eai.authserver.custom;
import java.io.Serializable;
import com.fasterxml.jackson.annotation.JsonProperty;
@SuppressWarnings("serial")
public class KjbMGOAuth2AccessTokenRequest implements Serializable {
@JsonProperty("grant_type")
private String grantType;
@JsonProperty("client_id")
private String clientId;
@JsonProperty("client_secret")
private String clientSecret;
private String resource;
private String scope;
public String getGrantType() {
return grantType;
}
public void setGrantType(String grantType) {
this.grantType = grantType;
}
public String getClientId() {
return clientId;
}
public void setClientId(String clientId) {
this.clientId = clientId;
}
public String getClientSecret() {
return clientSecret;
}
public void setClientSecret(String clientSecret) {
this.clientSecret = clientSecret;
}
public String getResource() {
return resource;
}
public void setResource(String resource) {
this.resource = resource;
}
public String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
}
@Override
public String toString() {
return "KjbMGOAuth2AccessTokenRequest [grantType=" + grantType + ", clientId=" + clientId + ", clientSecret="
+ clientSecret + ", resource=" + resource + ", scope=" + scope + "]";
}
}
@@ -0,0 +1,105 @@
package com.eactive.eai.authserver.custom;
import java.io.Serializable;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
@SuppressWarnings("serial")
@JsonInclude(Include.NON_NULL)
public class KjbMGOAuth2AccessTokenResponse implements Serializable {
private String responseCode;
private String responseMessage;
@JsonProperty("access_token")
private String accessToken;
@JsonProperty("refresh_token")
private String refreshToken;
@JsonProperty("token_type")
private String tokenType;
@JsonProperty("expires_in")
private Long expiresIn;
@JsonProperty("expires_on")
private Long expiresOn;
private String resource;
private String scope;
public String getResponseCode() {
return responseCode;
}
public void setResponseCode(String responseCode) {
this.responseCode = responseCode;
}
public String getResponseMessage() {
return responseMessage;
}
public void setResponseMessage(String responseMessage) {
this.responseMessage = responseMessage;
}
public String getAccessToken() {
return accessToken;
}
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
public String getrefreshToken() {
return refreshToken;
}
public void setrefreshToken(String refreshToken) {
this.refreshToken = refreshToken;
}
public String getTokenType() {
return tokenType;
}
public void setTokenType(String tokenType) {
this.tokenType = tokenType;
}
public Long getExpiresIn() {
return expiresIn;
}
public void setExpiresIn(Long expiresIn) {
this.expiresIn = expiresIn;
}
public Long getExpiresOn() {
return expiresOn;
}
public void setExpiresOn(Long expiresOn) {
this.expiresOn = expiresOn;
}
public String getResource() {
return resource;
}
public void setResource(String resource) {
this.resource = resource;
}
public String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
}
@Override
public String toString() {
return "KjbMGOAuth2AccessTokenResponse [responseCode=" + responseCode + ", responseMessage=" + responseMessage
+ ", accessToken=" + accessToken + ", tokenType=" + tokenType + ", expiresIn=" + expiresIn
+ ", refreshToken=" + refreshToken+ ", expiresOn=" + expiresOn+ ", resource=" + resource+ ", scope=" + scope + "]";
}
}
@@ -0,0 +1,360 @@
package com.eactive.eai.authserver.custom;
import java.nio.charset.StandardCharsets;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.Principal;
import java.security.SecureRandom;
import java.security.Signature;
import java.time.LocalDateTime;
import java.util.Base64;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.crypto.Cipher;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.util.OAuth2Utils;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.OAuth2Request;
import org.springframework.security.oauth2.provider.endpoint.TokenEndpoint;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import com.eactive.eai.adapter.http.dynamic.filter.JwtAuthException;
import com.eactive.eai.authserver.config.IssueLimitOAuth2Exception;
import com.eactive.eai.authserver.config.RequestContextData;
import com.eactive.eai.authserver.dao.TokenIssuanceLogDAO;
import com.eactive.eai.authserver.service.OAuth2Manager;
import com.eactive.eai.authserver.util.BeanUtils;
import com.eactive.eai.authserver.vo.ClientVO;
import com.eactive.eai.common.dao.DAOException;
import com.eactive.eai.common.logger.EAIDBLogControl;
import com.eactive.eai.common.util.Logger;
import com.eactive.eai.common.util.MessageUtil;
import com.eactive.eai.common.util.UUID;
import com.eactive.eai.data.entity.onl.authserver.TokenIssuanceLog;
import com.fasterxml.jackson.databind.ObjectMapper;
@Controller
public class KjbMGOAuth2Controller {
public static Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT);
private static final String HEADER_TRACEID = "traceId";
private static final String SERVICE_CODE_ACCESS_TOKEN = "00"; // 임시설정
@Autowired
private TokenIssuanceLogDAO tokenIssuanceLogDAO;
@RequestMapping(value = "/mapi/oauth2/token", method = RequestMethod.POST, consumes = "application/json", produces = "application/json; charset=UTF-8")
@ResponseBody
public ResponseEntity<String> tokenJson(@RequestBody KjbMGOAuth2AccessTokenRequest tokenRequest,
HttpServletRequest request, HttpServletResponse response) {
return issueToken(tokenRequest, request, response);
}
@RequestMapping(value = "/mapi/oauth2/token", method = RequestMethod.POST, consumes = "application/x-www-form-urlencoded", produces = "application/json; charset=UTF-8")
@ResponseBody
public ResponseEntity<String> tokenForm(@RequestParam Map<String, String> params, HttpServletRequest request,
HttpServletResponse response) {
KjbMGOAuth2AccessTokenRequest tokenRequest = new KjbMGOAuth2AccessTokenRequest();
tokenRequest.setGrantType(params.get("grant_type"));
tokenRequest.setClientId(params.get("client_id"));
tokenRequest.setClientSecret(params.get("client_secret"));
tokenRequest.setScope(params.get("scope"));
tokenRequest.setResource(params.get("resource"));
return issueToken(tokenRequest, request, response);
}
@GetMapping(value = "/mapi/oauth2/token", produces = "application/json; charset=UTF-8")
@ResponseBody
public ResponseEntity<String> tokenGet(@RequestParam Map<String, String> params, HttpServletRequest request,
HttpServletResponse response) {
KjbMGOAuth2AccessTokenRequest tokenRequest = new KjbMGOAuth2AccessTokenRequest();
tokenRequest.setGrantType(params.get("grant_type"));
tokenRequest.setClientId(params.get("client_id"));
tokenRequest.setClientSecret(params.get("client_secret"));
tokenRequest.setScope(params.get("scope"));
tokenRequest.setResource(params.get("resource"));
return issueToken(tokenRequest, request, response);
}
private ResponseEntity<String> issueToken(
KjbMGOAuth2AccessTokenRequest tokenRequest,
HttpServletRequest request,
HttpServletResponse response) {
if (logger.isDebug()) {
logger.debug(tokenRequest.toString());
}
RequestContextData data = new RequestContextData();
data.setClientId(tokenRequest.getClientId());
data.setIpAddress(extractIpAddress(request));
data.setGrantType(tokenRequest.getGrantType());
data.setScope(tokenRequest.getScope());
data.setUsername("none");
data.setResource("none");
RequestContextData.ThreadLocalRequestContext.set(data);
KjbMGOAuth2AccessTokenResponse responseToken = new KjbMGOAuth2AccessTokenResponse();
String grantType = tokenRequest.getGrantType();
String clientId = tokenRequest.getClientId();
String clientSecret = tokenRequest.getClientSecret();
String scopes = tokenRequest.getScope();
try {
if (!StringUtils.equals(tokenRequest.getGrantType(), "client_credentials")) {
throw new JwtAuthException(
String.format("%d%s%s", HttpStatus.BAD_REQUEST.value(), SERVICE_CODE_ACCESS_TOKEN, "02"),
"Invalid Mandatory Field {grantType}");
}
String[] scopeArr = org.springframework.util.StringUtils.tokenizeToStringArray(scopes, " ");
Set<String> scopeSet = new HashSet<String>();
for (String scope : scopeArr) {
scopeSet.add(scope);
}
String[] resourceArr = org.springframework.util.StringUtils.tokenizeToStringArray(tokenRequest.getResource(), ",");
Set<String> resourceSet = new HashSet<String>();
for (String resource : resourceArr) {
resourceSet.add(resource);
}
ClientDetails clientDetails = OAuth2Manager.getInstance().getClientDeatilsStore().get(clientId);
verifyClient(clientDetails, clientId, clientSecret, scopeSet);
String traceId = UUID.randomUUID().toString().replace("-", "");
response.setHeader(HEADER_TRACEID, traceId);
HashMap<String, String> authorizationParameters = new HashMap<String, String>();
authorizationParameters.put(OAuth2Utils.GRANT_TYPE, grantType);
authorizationParameters.put(OAuth2Utils.CLIENT_ID, clientId);
authorizationParameters.put("client_secret", clientSecret);
Set<String> responseType = new HashSet<String>();
responseType.add(tokenRequest.getGrantType());
OAuth2Request authorizationRequest = new OAuth2Request(authorizationParameters, clientId, null, true, scopeSet,
resourceSet, "", responseType, null);
Principal principal = new OAuth2Authentication(authorizationRequest, null);
ResponseEntity<OAuth2AccessToken> result = tokenEndpoint().postAccessToken(principal,
authorizationParameters);
OAuth2AccessToken token = result.getBody();
if(StringUtils.isNotBlank(token.getTokenType()) && StringUtils.equalsIgnoreCase(token.getTokenType(), "bearer")) {
responseToken.setTokenType("Bearer");
} else { responseToken.setTokenType(token.getTokenType()); }
responseToken.setAccessToken(token.getValue());
responseToken.setExpiresIn(Long.valueOf(token.getExpiresIn()));
responseToken.setrefreshToken("");
responseToken.setExpiresOn(System.currentTimeMillis() + (token.getExpiresIn() * 1000));
responseToken.setScope(tokenRequest.getScope());
responseToken.setResource(tokenRequest.getResource());
// 성공 시 JSON 문자열로 변환하여 반환
ObjectMapper mapper = new ObjectMapper();
String successJson = mapper.writeValueAsString(responseToken);
return ResponseEntity.ok(successJson);
} catch (JwtAuthException e) {
logger.info("Token request[/mapi/oauth2/token] => clientId: {}, grantType: {}, scope: {}",
clientId, grantType, scopes);
logger.error(e.getMessage());
String errorJson = MessageUtil.makeJsonErrorMessage(MessageUtil.ERROR_CODE_AUTH_FAIL, e.getMessage());
int statusCode = NumberUtils.toInt(StringUtils.left(e.getCode(), 3), HttpStatus.UNAUTHORIZED.value());
logTokenIssuance(false ,false, e.getMessage(), null);
return ResponseEntity.status(statusCode).body(errorJson);
} catch (Exception e) {
logger.info("Token request[/mapi/oauth2/token] => clientId: {}, grantType: {}, scope: {}",
clientId, grantType, scopes);
logger.error(e.getMessage());
String errorJson = MessageUtil.makeJsonErrorMessage(MessageUtil.ERROR_CODE_AUTH_FAIL, "Unauthorized. [Unknown]");
logTokenIssuance(false ,false, e.getMessage(), null);
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(errorJson);
}finally {
RequestContextData.ThreadLocalRequestContext.clear();
}
}
private String extractIpAddress(HttpServletRequest request) {
String ipAddress = request.getHeader("X-Forwarded-For");
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("WL-Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("HTTP_CLIENT_IP");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getRemoteAddr();
}
return ipAddress;
}
private void verifyClient(ClientDetails clientDetails, String clientId, String clientSecret, Set<String> scopeSet)
throws JwtAuthException {
if (StringUtils.isBlank(clientId)) {
throw new JwtAuthException(
String.format("%d%s%s", HttpStatus.BAD_REQUEST.value(), SERVICE_CODE_ACCESS_TOKEN, "02"),
"Invalid Mandatory Field {client_id}");
}
if (clientDetails == null) {
throw new JwtAuthException(
String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_ACCESS_TOKEN, "00"),
"Unauthorized. [client not found]");
}
if (StringUtils.isBlank(clientSecret)) {
throw new JwtAuthException(
String.format("%d%s%s", HttpStatus.BAD_REQUEST.value(), SERVICE_CODE_ACCESS_TOKEN, "02"),
"Invalid Mandatory Field {client_secret}");
}
if (!StringUtils.equals(clientDetails.getClientSecret(), clientSecret)) {
throw new JwtAuthException(
String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_ACCESS_TOKEN, "00"),
"Unauthorized. [Bad client credentials(Client Secret is not match)]");
}
if (scopeSet.isEmpty()) {
throw new JwtAuthException(
String.format("%d%s%s", HttpStatus.BAD_REQUEST.value(), SERVICE_CODE_ACCESS_TOKEN, "02"),
"Invalid Mandatory Field {scope}");
}
if (!clientDetails.getScope().containsAll(scopeSet)) {
throw new JwtAuthException(
String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_ACCESS_TOKEN, "00"),
"Unauthorized. [Bad client credentials(Include unacceptable scope)]");
}
}
private TokenEndpoint tokenEndpoint() {
return BeanUtils.getBean("tokenEndpoint", TokenEndpoint.class);
}
private void logTokenIssuance(boolean isSuccess, boolean isRestrictionExempt, String resultMessage, String accessToken) {
try {
if (!EAIDBLogControl.isEnable()) {
logger.warn("DB logging is disabled. Skipping token issuance logging.");
return;
}
RequestContextData data = RequestContextData.ThreadLocalRequestContext.get();
OAuth2Manager.getInstance();
TokenIssuanceLog log = new TokenIssuanceLog();
String clientId = data.getClientId();
log.setClientId(clientId);
if(StringUtils.isNotBlank(clientId)) {
ClientVO clientVO = OAuth2Manager.getInstance().getClientInfo(clientId);
log.setAppName(clientVO.getClientName());
log.setOrgId(clientVO.getOrgId());
log.setOrgName(clientVO.getOrgName());
}
log.setGrantType(data.getGrantType());
log.setScope(data.getScope());
log.setIpAddress(data.getIpAddress());
log.setSuccessYn(isSuccess ? "Y" : "N");
log.setIssuanceDateTime(LocalDateTime.now());
log.setRestrictionExemptYn(isRestrictionExempt ? "Y" : "N");
log.setResultMessage(resultMessage);
log.setAccessToken(accessToken); // Add access token value to the log
tokenIssuanceLogDAO.saveTokenIssuanceLog(log);
} catch (Throwable th) {
logger.error("Error while logging token issuance: " + th.getMessage(), th);
}
}
public static void main(String[] args) throws Exception {
// First generate a public/private key pair
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
// generator.initialize(512, new SecureRandom());
//generator.initialize(1024, new SecureRandom());
generator.initialize(2048, new SecureRandom());
KeyPair pair = generator.generateKeyPair();
// The private key can be used to sign (not encrypt!) a message. The public key
// holder can then verify the message.
String message = "sSGJDwlJsel5WeXodzd3J3MQ20KYCZpL|2022-12-21T14:36:19+07:00";
// Let's sign our message
Signature privateSignature = Signature.getInstance("SHA256withRSA");
privateSignature.initSign(pair.getPrivate());
privateSignature.update(message.getBytes(StandardCharsets.UTF_8));
// System.out.println("private key=" + Base64.getEncoder().encodeToString(pair.getPrivate().getEncoded()));
byte[] signature = privateSignature.sign();
//System.out.println("signature=" + new String(signature, StandardCharsets.UTF_8));
// System.out.println("signature=" + Base64.getEncoder().encodeToString(signature));
// Let's check the signature
Signature publicSignature = Signature.getInstance("SHA256withRSA");
publicSignature.initVerify(pair.getPublic());
publicSignature.update(message.getBytes(StandardCharsets.UTF_8));
boolean isCorrect = publicSignature.verify(signature);
// System.out.println("public key=" + Base64.getEncoder().encodeToString(pair.getPublic().getEncoded()));
// System.out.println("Signature correct: " + isCorrect);
// The public key can be used to encrypt a message, the private key can be used
// to decrypt it.
// Encrypt the message
Cipher encryptCipher = Cipher.getInstance("RSA");
encryptCipher.init(Cipher.ENCRYPT_MODE, pair.getPublic());
byte[] cipherText = encryptCipher.doFinal(message.getBytes(StandardCharsets.UTF_8));
// Now decrypt it
Cipher decriptCipher = Cipher.getInstance("RSA");
decriptCipher.init(Cipher.DECRYPT_MODE, pair.getPrivate());
String decipheredMessage = new String(decriptCipher.doFinal(cipherText), StandardCharsets.UTF_8);
// System.out.println(decipheredMessage);
}
}
@@ -0,0 +1,49 @@
package com.eactive.eai.authserver.custom;
import java.io.Serializable;
@SuppressWarnings("serial")
public class SnapOAuth2AccessTokenRequest implements Serializable {
private String grantType;
private String authCode;
private String refreshToken;
private Object additionalInfo;
public String getGrantType() {
return grantType;
}
public void setGrantType(String grantType) {
this.grantType = grantType;
}
public String getAuthCode() {
return authCode;
}
public void setAuthCode(String authCode) {
this.authCode = authCode;
}
public String getRefreshToken() {
return refreshToken;
}
public void setRefreshToken(String refreshToken) {
this.refreshToken = refreshToken;
}
public Object getAdditionalInfo() {
return additionalInfo;
}
public void setAdditionalInfo(Object additionalInfo) {
this.additionalInfo = additionalInfo;
}
@Override
public String toString() {
return "SnapOAuth2AccessTokenRequest [grantType=" + grantType + ", authCode=" + authCode + ", refreshToken="
+ refreshToken + ", additionalInfo=" + additionalInfo + "]";
}
}
@@ -0,0 +1,72 @@
package com.eactive.eai.authserver.custom;
import java.io.Serializable;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
@SuppressWarnings("serial")
@JsonInclude(Include.NON_NULL)
public class SnapOAuth2AccessTokenResponse implements Serializable {
private String responseCode;
private String responseMessage;
private String accessToken;
private String tokenType;
private String expiresIn;
private Object additionalInfo;
public String getResponseCode() {
return responseCode;
}
public void setResponseCode(String responseCode) {
this.responseCode = responseCode;
}
public String getResponseMessage() {
return responseMessage;
}
public void setResponseMessage(String responseMessage) {
this.responseMessage = responseMessage;
}
public String getAccessToken() {
return accessToken;
}
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
public String getTokenType() {
return tokenType;
}
public void setTokenType(String tokenType) {
this.tokenType = tokenType;
}
public String getExpiresIn() {
return expiresIn;
}
public void setExpiresIn(String expiresIn) {
this.expiresIn = expiresIn;
}
public Object getAdditionalInfo() {
return additionalInfo;
}
public void setAdditionalInfo(Object additionalInfo) {
this.additionalInfo = additionalInfo;
}
@Override
public String toString() {
return "SnapOAuth2AccessTokenResponse [responseCode=" + responseCode + ", responseMessage=" + responseMessage
+ ", accessToken=" + accessToken + ", tokenType=" + tokenType + ", expiresIn=" + expiresIn
+ ", additionalInfo=" + additionalInfo + "]";
}
}
@@ -0,0 +1,222 @@
package com.eactive.eai.authserver.custom;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.Principal;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Signature;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import javax.crypto.Cipher;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.util.OAuth2Utils;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.OAuth2Request;
import org.springframework.security.oauth2.provider.endpoint.TokenEndpoint;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import com.eactive.eai.adapter.http.dynamic.filter.JwtAuthException;
import com.eactive.eai.authserver.service.OAuth2Manager;
import com.eactive.eai.authserver.util.BeanUtils;
import com.eactive.eai.authserver.vo.ClientVO;
import com.eactive.eai.common.util.Logger;
@Controller
public class SnapOAuth2Controller {
public static Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT);
private static final String HEADER_TIMESTAMP = "X-TIMESTAMP";
private static final String HEADER_CLIENT_ID = "X-CLIENT-KEY";
private static final String HEADER_SIGNATURE = "X-SIGNATURE";
private static final String SERVICE_CODE_ACCESS_TOKEN = "00"; // 임시설정
@RequestMapping(value = "/bukopin_snap_api/v1.0/access-token/b2b", method = RequestMethod.POST, produces = "application/json; charset=\"UTF-8\"")
@ResponseBody
public SnapOAuth2AccessTokenResponse token(@RequestBody SnapOAuth2AccessTokenRequest tokenRequest,
HttpServletRequest request, HttpServletResponse response) {
if (logger.isDebug()) {
logger.debug(tokenRequest.toString());
}
SnapOAuth2AccessTokenResponse responseToken = new SnapOAuth2AccessTokenResponse();
try {
if (!StringUtils.equals(tokenRequest.getGrantType(), "client_credentials")) {
throw new JwtAuthException(
String.format("%d%s%s", HttpStatus.BAD_REQUEST.value(), SERVICE_CODE_ACCESS_TOKEN, "02"),
"Invalid Mandatory Field {grantType}");
}
String timeStamp = request.getHeader(HEADER_TIMESTAMP);
String clientId = request.getHeader(HEADER_CLIENT_ID);
String signature = request.getHeader(HEADER_SIGNATURE);
ClientDetails clientDetails = OAuth2Manager.getInstance().getClientDeatilsStore().get(clientId);
verifyClient(clientDetails, timeStamp, clientId, signature);
response.setHeader(HEADER_TIMESTAMP, timeStamp);
response.setHeader(HEADER_CLIENT_ID, clientId);
HashMap<String, String> authorizationParameters = new HashMap<String, String>();
authorizationParameters.put(OAuth2Utils.GRANT_TYPE, tokenRequest.getGrantType());
authorizationParameters.put(OAuth2Utils.CLIENT_ID, clientId);
authorizationParameters.put("client_secret", clientDetails.getClientSecret());
Set<String> responseType = new HashSet<String>();
responseType.add(tokenRequest.getGrantType());
OAuth2Request authorizationRequest = new OAuth2Request(authorizationParameters, clientId, null, true, null,
null, "", responseType, null);
Principal principal = new OAuth2Authentication(authorizationRequest, null);
ResponseEntity<OAuth2AccessToken> result = tokenEndpoint().postAccessToken(principal,
authorizationParameters);
OAuth2AccessToken token = result.getBody();
responseToken.setAccessToken(token.getValue());
responseToken.setExpiresIn(String.valueOf(token.getExpiresIn()));
responseToken.setTokenType(token.getTokenType());
} catch (JwtAuthException e) {
response.setStatus(NumberUtils.toInt(StringUtils.left(e.getCode(), 3), HttpStatus.UNAUTHORIZED.value()));
responseToken.setResponseCode(e.getCode());
responseToken.setResponseMessage(e.getMessage());
} catch (Exception e) {
logger.error(e.getMessage());
response.setStatus(HttpStatus.UNAUTHORIZED.value());
responseToken.setResponseCode(
String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_ACCESS_TOKEN, "00"));
responseToken.setResponseMessage("Unauthorized. [Unknown]");
}
return responseToken;
}
private void verifyClient(ClientDetails clientDetails, String timeStamp, String clientId, String signatureStr)
throws JwtAuthException {
if (StringUtils.isBlank(timeStamp)) {
throw new JwtAuthException(
String.format("%d%s%s", HttpStatus.BAD_REQUEST.value(), SERVICE_CODE_ACCESS_TOKEN, "02"),
"Invalid Mandatory Field {" + HEADER_TIMESTAMP + "}");
}
if (StringUtils.isBlank(clientId)) {
throw new JwtAuthException(
String.format("%d%s%s", HttpStatus.BAD_REQUEST.value(), SERVICE_CODE_ACCESS_TOKEN, "02"),
"Invalid Mandatory Field {" + HEADER_CLIENT_ID + "}");
}
if (StringUtils.isBlank(signatureStr)) {
throw new JwtAuthException(
String.format("%d%s%s", HttpStatus.BAD_REQUEST.value(), SERVICE_CODE_ACCESS_TOKEN, "02"),
"Invalid Mandatory Field {" + HEADER_SIGNATURE + "}");
}
if (clientDetails == null) {
throw new JwtAuthException(
String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_ACCESS_TOKEN, "00"),
"Unauthorized. [client not found]");
}
String publicKeyStr = ((ClientVO) clientDetails).getSecurityKey();
if (StringUtils.isBlank(publicKeyStr)) {
throw new JwtAuthException(
String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_ACCESS_TOKEN, "00"),
"Unauthorized. [public key not found]");
}
try {
String message = clientId + "|" + timeStamp;
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKeyStr));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(keySpec);
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initVerify(publicKey);
signature.update(message.getBytes(StandardCharsets.UTF_8));
if (!signature.verify(Base64.getDecoder().decode(signatureStr))) {
throw new JwtAuthException(
String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_ACCESS_TOKEN, "00"),
"Unauthorized. [Signature not matched]");
}
} catch (JwtAuthException e) {
throw e;
} catch (Exception e) {
logger.error(e.getMessage());
throw new JwtAuthException(
String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_ACCESS_TOKEN, "00"),
"Unauthorized. [Signature fail]");
}
}
private TokenEndpoint tokenEndpoint() {
return BeanUtils.getBean("tokenEndpoint", TokenEndpoint.class);
}
public static void main(String[] args) throws Exception {
// First generate a public/private key pair
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
// generator.initialize(512, new SecureRandom());
//generator.initialize(1024, new SecureRandom());
generator.initialize(2048, new SecureRandom());
KeyPair pair = generator.generateKeyPair();
// The private key can be used to sign (not encrypt!) a message. The public key
// holder can then verify the message.
String message = "sSGJDwlJsel5WeXodzd3J3MQ20KYCZpL|2022-12-21T14:36:19+07:00";
// Let's sign our message
Signature privateSignature = Signature.getInstance("SHA256withRSA");
privateSignature.initSign(pair.getPrivate());
privateSignature.update(message.getBytes(StandardCharsets.UTF_8));
System.out.println(
"private key=" + Base64.getEncoder().encodeToString(pair.getPrivate().getEncoded()));
byte[] signature = privateSignature.sign();
//System.out.println("signature=" + new String(signature, StandardCharsets.UTF_8));
System.out.println("signature=" + Base64.getEncoder().encodeToString(signature));
// Let's check the signature
Signature publicSignature = Signature.getInstance("SHA256withRSA");
publicSignature.initVerify(pair.getPublic());
publicSignature.update(message.getBytes(StandardCharsets.UTF_8));
boolean isCorrect = publicSignature.verify(signature);
System.out.println("public key=" + Base64.getEncoder().encodeToString(pair.getPublic().getEncoded()));
System.out.println("Signature correct: " + isCorrect);
// The public key can be used to encrypt a message, the private key can be used
// to decrypt it.
// Encrypt the message
Cipher encryptCipher = Cipher.getInstance("RSA");
encryptCipher.init(Cipher.ENCRYPT_MODE, pair.getPublic());
byte[] cipherText = encryptCipher.doFinal(message.getBytes(StandardCharsets.UTF_8));
// Now decrypt it
Cipher decriptCipher = Cipher.getInstance("RSA");
decriptCipher.init(Cipher.DECRYPT_MODE, pair.getPrivate());
String decipheredMessage = new String(decriptCipher.doFinal(cipherText), StandardCharsets.UTF_8);
System.out.println(decipheredMessage);
}
}
@@ -1,48 +0,0 @@
package com.eactive.eai.custom.adapter.handler;
import java.util.Properties;
import org.apache.commons.lang3.StringUtils;
import com.eactive.eai.adapter.handler.TemplateCodeConvertAdapterErrorMsgHandler;
import com.eactive.eai.common.message.EAIMessage;
import com.eactive.eai.common.util.Logger;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
public class ErpAdapterErrorMsgHandler extends TemplateCodeConvertAdapterErrorMsgHandler {
private static final Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
@Override
public Object generateNonStandardErrorResponseMessage(String inboundAdapterGroupName, String inboundAdapterName,
Properties callProp, Object outboundRequestData, EAIMessage resEaiMsg) throws Exception {
Object responseMsessage = super.generateNonStandardErrorResponseMessage(inboundAdapterGroupName,
inboundAdapterName, callProp, outboundRequestData, resEaiMsg);
logger.debug("generateNonStandardErrorResponseMessage - {}", responseMsessage);
if (!(responseMsessage instanceof String)) {
return responseMsessage;
}
String jsonStr = (String) responseMsessage;
if (StringUtils.isBlank(jsonStr)) {
return responseMsessage;
}
JsonNode rootNode = OBJECT_MAPPER.readTree(jsonStr);
boolean modified = false;
if (modified) {
responseMsessage = OBJECT_MAPPER.writeValueAsString(rootNode);
}
return responseMsessage;
}
}
@@ -1,122 +0,0 @@
package com.eactive.eai.custom.adapter.handler;
import java.util.Map;
import java.util.Properties;
import org.apache.commons.lang3.StringUtils;
import com.eactive.eai.adapter.handler.TemplateAdapterErrorMsgHandler;
import com.eactive.eai.adapter.handler.TemplateCodeConvertAdapterErrorMsgHandler;
import com.eactive.eai.adapter.http.dynamic.HttpAdapterServiceKey;
import com.eactive.eai.common.message.EAIMessage;
import com.eactive.eai.common.message.EAIMessageKeys;
import com.eactive.eai.common.util.Logger;
import com.eactive.eai.custom.message.StandardMessageCoordinatorDJB;
import com.eactive.eai.message.StandardItem;
import com.eactive.eai.message.StandardMessage;
import com.eactive.eai.message.manager.StandardMessageManager;
import com.eactive.eai.message.service.InterfaceMapper;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
public class KakaopayAdapterErrorMsgHandler extends TemplateCodeConvertAdapterErrorMsgHandler {
private static final Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
@Override
public Object generateNonStandardErrorResponseMessage(String inboundAdapterGroupName, String inboundAdapterName,
Properties callProp, Object outboundRequestData, EAIMessage resEaiMsg) throws Exception {
Object responseMsessage = super.generateNonStandardErrorResponseMessage(inboundAdapterGroupName,
inboundAdapterName, callProp, outboundRequestData, resEaiMsg);
logger.debug("generateNonStandardErrorResponseMessage - {}", responseMsessage);
if (!(responseMsessage instanceof String)) {
return responseMsessage;
}
String jsonStr = (String) responseMsessage;
if (StringUtils.isBlank(jsonStr)) {
return responseMsessage;
}
JsonNode rootNode = OBJECT_MAPPER.readTree(jsonStr);
boolean modified = false;
if (modified) {
responseMsessage = OBJECT_MAPPER.writeValueAsString(rootNode);
}
return responseMsessage;
}
/**
* 표준 -> 비표준 거래 아웃바운드 어댑터에서 Exception이 발생했을 때, Outbound Process 응답 메시지 생성
*/
@Override
public Object generateOutboundErrorResponseMessage(String outboundadapterGroupName, String outboundadapterName,
Properties callProp, Object outboundRequestData, EAIMessage resEaiMsg) throws Exception {
String msgCd = "error";
String msgCtnt = "IF오류";
String msgDesc = "";
// 에러에 대한 응답메시지
if (callProp.get(HttpAdapterServiceKey.OUTBOUND_PROPERTY_MAP) instanceof Map) {
Map<String, Object> map = (Map<String, Object>) callProp.get(HttpAdapterServiceKey.OUTBOUND_PROPERTY_MAP);
Object responseBody = map.get(HttpAdapterServiceKey.OUTBOUND_RESPONSE_MESSAGE);
if (responseBody instanceof String) {
String jsonStr = (String) responseBody;
if (!StringUtils.isBlank(jsonStr)) {
JsonNode rootNode = OBJECT_MAPPER.readTree(jsonStr);
StringBuffer sb = new StringBuffer();
JsonNode errorCode = rootNode.get("error_code");
if(errorCode != null)
sb.append(errorCode.textValue());
sb.append("/");
JsonNode errorMessage = rootNode.get("error_message");
if(errorMessage != null)
sb.append(errorMessage.textValue());
msgDesc = sb.toString();
msgCd = "BFEX03216";
msgCtnt = "참가기관코드 에러입니다.";
logger.debug("오류 응답 수신 - {}", jsonStr);
}
}
}
StandardMessage standardMessage = resEaiMsg.getStandardMessage();
// msg_dvcd = EM (에러메시지)
standardMessage.setData(StandardMessageCoordinatorDJB.MSG_DVCD, "NM");
// 출력속성코드 = 1(팝업)
standardMessage.setData(StandardMessageCoordinatorDJB.MSG_OUTP_ATRB_CD, "");
// MSG_LIST 1건 구성
standardMessage.setData(StandardMessageCoordinatorDJB.MSG_LIST_ROWCNT, "0");
StandardItem msgPart = standardMessage.findItem("MSG");
msgPart.getChilds().remove("MSG_LIST");
StandardMessageManager manager = StandardMessageManager.getInstance();
InterfaceMapper mapper = manager.getMapper();
// mapper 에러코드 설정
mapper.setErrorCode(standardMessage, msgCd);
mapper.setErrorMsg(standardMessage, msgCtnt);
if (StringUtils.isNotEmpty(msgDesc))
standardMessage.setData(StandardMessageCoordinatorDJB.MSG_OUTP_MSG_DESC, msgDesc);
// 성공 처리 처럼 진행한다.
resEaiMsg.setRspErrCd(EAIMessageKeys.EAI_SUCCESS_CODE, false);
return null;
}
}
@@ -0,0 +1,52 @@
package com.eactive.eai.custom.adapter.handler;
import java.util.Properties;
import com.eactive.eai.adapter.AdapterGroupVO;
import com.eactive.eai.adapter.AdapterManager;
import com.eactive.eai.adapter.handler.AdapterErrorMessageHandler;
import com.eactive.eai.common.message.MessageType;
import com.eactive.eai.message.StandardMessage;
import com.eactive.eai.message.manager.StandardMessageManager;
import com.eactive.eai.message.service.InterfaceMapper;
import com.ext.eai.common.stdmessage.STDMessageKeys;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
public class LonErrorMsgHandler implements AdapterErrorMessageHandler {
@Override
public Object generateNonStandardErrorResponseMessage(String inboudnAdapterGroupName, String inboudnAdapterName,
Properties callProp, Object outboundRequestData, StandardMessage resStandardMessage) throws Exception{
if(resStandardMessage != null && isErrorMessage(resStandardMessage)) {
AdapterGroupVO adapterGroupVO = AdapterManager.getInstance().getAdapterGroupVO(inboudnAdapterGroupName);
if(MessageType.JSON.equals(adapterGroupVO.getMessageType())) {
ObjectMapper objectMapper = new ObjectMapper();
String requestMessage = (String) outboundRequestData;
JsonNode requestJson = objectMapper.readTree(requestMessage);
String alncInstCd = requestJson.get("dataCmn").get("alncInstCd").asText();
ObjectNode responseJson = objectMapper.createObjectNode();
ObjectNode dataCmnNode = objectMapper.createObjectNode();
responseJson.set("dataCmn", dataCmnNode);
dataCmnNode.put("fnclInstCd", "KBK");
dataCmnNode.put("alncInstCd", alncInstCd);
dataCmnNode.put("tlgrRspnsCd", "0210");
return responseJson.toString();
}
}
return "";
}
private boolean isErrorMessage(StandardMessage standardMessage) {
InterfaceMapper mapper = StandardMessageManager.getInstance().getMapper();
String responseType = mapper.getResponseType(standardMessage);
return STDMessageKeys.RESPONSE_TYPE_CODE_E.equals(responseType);
}
}
@@ -1,62 +0,0 @@
package com.eactive.eai.custom.adapter.handler;
import java.util.Map;
import java.util.Properties;
import org.apache.commons.lang3.StringUtils;
import com.eactive.eai.adapter.handler.TemplateAdapterErrorMsgHandler;
import com.eactive.eai.adapter.http.dynamic.HttpAdapterServiceKey;
import com.eactive.eai.common.message.EAIMessage;
import com.eactive.eai.common.message.EAIMessageKeys;
import com.eactive.eai.common.util.Logger;
import com.eactive.eai.custom.message.StandardMessageCoordinatorDJB;
import com.eactive.eai.message.StandardItem;
import com.eactive.eai.message.StandardMessage;
import com.eactive.eai.message.manager.StandardMessageManager;
import com.eactive.eai.message.service.InterfaceMapper;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
public class NaverpayAdapterErrorMsgHandler extends TemplateAdapterErrorMsgHandler {
private static final Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
/**
* 표준 -> 비표준 거래
* 아웃바운드 어댑터에서 Exception이 발생했을 때, Outbound Process 응답 메시지 생성
*/
@Override
public Object generateOutboundErrorResponseMessage(String outboundadapterGroupName,
String outboundadapterName, Properties callProp, Object outboundRequestData, EAIMessage resEaiMsg)
throws Exception {
StandardMessage standardMessage = resEaiMsg.getStandardMessage();
// msg_dvcd = EM (에러메시지)
standardMessage.setData(StandardMessageCoordinatorDJB.MSG_DVCD, "NM");
// 출력속성코드 = 1(팝업)
standardMessage.setData(StandardMessageCoordinatorDJB.MSG_OUTP_ATRB_CD, "1");
// MSG_LIST 1건 구성
standardMessage.setData(StandardMessageCoordinatorDJB.MSG_LIST_ROWCNT, "0");
StandardItem msgPart = standardMessage.findItem("MSG");
msgPart.getChilds().remove("MSG_LIST");
StandardMessageManager manager = StandardMessageManager.getInstance();
InterfaceMapper mapper = manager.getMapper();
// mapper 에러코드 설정
mapper.setErrorCode(standardMessage, "");
mapper.setErrorMsg(standardMessage, "처리오류");
// 성공 처리 처럼 진행한다.
resEaiMsg.setRspErrCd(EAIMessageKeys.EAI_SUCCESS_CODE, false);
return null;
}
}
@@ -0,0 +1,897 @@
package com.eactive.eai.custom.adapter.http.client.impl;
import java.io.IOException;
import java.io.StringReader;
import java.math.BigDecimal;
import java.net.ConnectException;
import java.net.SocketTimeoutException;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.StopWatch;
import org.apache.commons.codec.binary.Base64;
import org.apache.hc.client5.http.classic.HttpClient;
import org.apache.hc.client5.http.classic.methods.HttpDelete;
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.client5.http.classic.methods.HttpPut;
import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.entity.UrlEncodedFormEntity;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.core5.http.*;
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.http.protocol.BasicHttpContext;
import org.apache.hc.core5.http.protocol.HttpContext;
import org.apache.hc.core5.net.URIBuilder;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
import com.eactive.eai.adapter.http.HttpMemoryLogger;
import com.eactive.eai.adapter.http.HttpMethodType;
import com.eactive.eai.adapter.http.client.HttpClient5AdapterServiceSupport;
import com.eactive.eai.adapter.http.client.HttpClientAdapterServiceKey;
import com.eactive.eai.adapter.http.client.HttpClientAdapterVO;
import com.eactive.eai.adapter.http.dynamic.HttpAdapterServiceKey;
import com.eactive.eai.common.TransactionContextKeys;
import com.eactive.eai.common.authoutbound.AccessTokenManagerByDB;
import com.eactive.eai.common.exception.ExceptionUtil;
import com.eactive.eai.common.message.MessageType;
import com.eactive.eai.common.util.CommonLib;
import com.jayway.jsonpath.DocumentContext;
import com.jayway.jsonpath.JsonPath;
import com.openbanking.eai.common.token.AccessTokenManager;
import com.openbanking.eai.common.token.AccessTokenVO;
import com.openbanking.eai.common.token.OAuth2AccessTokenVO;
import com.eactive.eai.common.property.PropManager;
public class HttpClient5AdapterServiceBase64 extends HttpClient5AdapterServiceSupport
implements HttpClientAdapterServiceKey {
public static final String TYPE_VARIABLE_URL_REQUEST = "variableUrlRequest";
public static final String TYPE_SIMPLE_REQUEST = "simpleRequest";
public static final String REST_OPTION = "REST_OPTION";
//public static final String HEADER_CONTENT_TYPE = "Content-Type";
public static final String HEADER_GROUP = "HEADER_GROUP";
public static final String HTTP_STATUS = "HTTP_STATUS";
public static final String HEADER_KEYS = "HEADER_KEYS";
public static final String AUTH_TOKEN = "AUTH_TOKEN";
/**
* 1. 기능 : REST API 통신에 사용 <br>
* 2. 처리 개요 : - 속성 정보를 설정 하고 수동 시스템 서비스를 호출 한다. <br>
* 3. 주의사항 <br>
*
* @param prop Http Adapter 속성 정보
* @return 반환 된 Object
* @exception Exception 수동 시스템 간 통신 중 발생
**/
@SuppressWarnings({ "unchecked" })
public Object execute(Properties prop, Object data, Properties tempProp) throws Exception {
HttpClientAdapterVO vo = super.setting(prop, tempProp);
// 2025.04.09 Authorization 헤더값을 프로퍼티(AUTH_TOKEN)에서 지정하여 설정할 수 있도록 수정
String bizCode = "";
String authToken = "";
try {
Properties authProp = PropManager.getInstance().getProperties(AUTH_TOKEN); //Authorization
bizCode = vo.getAdapterGroupName().substring(1, 4);
authToken = authProp.getProperty(bizCode, "");
} catch (Exception e) {
logger.debug("Property Group AUTH_TOKEN is not Exist !!");
}
logger.debug("authToken :::::::::::::::::::::::::::::::::::" + authToken);
String headerGroupName = prop.getProperty(HEADER_GROUP);
String relayResponseHeaderKeys = prop.getProperty(HEADER_KEYS, "");
boolean useAdapterToken = StringUtils.equalsIgnoreCase(prop.getProperty("ADAPTER_TOKEN_USE_YN", "N"), "Y");
boolean useForwardProxy = StringUtils.equalsIgnoreCase(prop.getProperty("FORWARD_PROXY_USE_YN", "N"), "Y");
String forwardProxyUrl = prop.getProperty("FORWARD_PROXY_URL");
// ex) $.dataHeader.GW_RSLT_CD
String tokenErrorCodeKey = prop.getProperty("ADAPTER_TOKEN_ERROR_CODE_KEY");
String tokenErrorCodeValues = prop.getProperty("ADAPTER_TOKEN_ERROR_CODE_VALUES");
String tokenErrorHttpStatusCode = prop.getProperty("ADAPTER_TOKEN_ERROR_HTTP_STATUS_CODE"); // 200 or 400번대
// 레이아웃 메시지 타입 REST URL 추출 시 활용. Default JSON
String messageType = prop.getProperty("MESSAGE_TYPE", MessageType.JSON);
String inboundMethod = tempProp.getProperty(HttpAdapterServiceKey.INBOUND_METHOD, "POST");
/**
* @formatter:off
* TSEAIHE02.RESTOPTION 정보 => JSON 형태로 구성
* - type : simpleRequest, variableUrlRequest
* - extraPath : 어댑터 프로퍼티 URL에 추가될 HTTP REST URL
* - method : get, delete, post, put
* - contentType: application/json(default), application/x-www-form-urlencoded
* - adapterTokenUseYn: Y, N(default)
* ex)
* {"type":"simpleRequest","extraPath":"v2.0/accout/balance","method":"post"}
* @formatter:on
*/
String restOptionData = tempProp.getProperty(REST_OPTION);
JSONObject restOptionObject = parseJson(restOptionData);
if (restOptionObject == null) {
throw new Exception("OptionData parsing result is NULL");
}
String rmethod = (String) restOptionObject.getOrDefault("method", inboundMethod);
String contentType = (String) restOptionObject.getOrDefault("contentType", "application/json");
String adapterTokenUseYn = (String) restOptionObject.get("adapterTokenUseYn");
if (StringUtils.isNotBlank(adapterTokenUseYn)) { // interface 에 설정된게 우선한다.
useAdapterToken = StringUtils.equalsIgnoreCase(adapterTokenUseYn, "Y");
}
HttpClient mclient = this.client;
String sendData = "";
if (data instanceof String) {
sendData = (String) data;
} else if (data instanceof byte[]) {
sendData = new String((byte[]) data, vo.getEncode());
}
////mclient.getParams().setContentCharset(vo.getEncode());
JSONObject dataObject = null;
Object httpHeader = null;
Object dataContent = null;
if (MessageType.JSON.equals(messageType)) {
Object parsed = parseJsonGeneric(sendData);
if(parsed instanceof JSONObject) {
dataObject = parseJson(sendData);
if (dataObject != null && StringUtils.isNotBlank(headerGroupName)) {
httpHeader = dataObject.get(headerGroupName);
dataObject.remove(headerGroupName);
}
dataContent = dataObject;
if (dataObject.containsKey("innerList")) {
JSONArray innerList = (JSONArray) dataObject.get("innerList");
dataContent = innerList;
}
}
}
logger.debug("HttpClientAdapterServiceRest] dataObject1 = [" + dataObject + "]");
logger.debug("HttpClientAdapterServiceRest] dataContent = [" + dataContent + "]");
// interface 에 설정된게 우선한다.
String uri = null;
if (dataObject == null) {
uri = changeUrl(messageType, vo.getUrl(), restOptionData, sendData);
} else {
uri = changeUrl(messageType, vo.getUrl(), restOptionData, dataObject);
}
// ////if (vo.getConnectionTimeout() != 0) {
// mclient.getHttpConnectionManager().getParams().setConnectionTimeout(vo.getConnectionTimeout());
// }
if (logger.isDebug() && "N".equals(vo.getTestCallYn())) {
logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") URL=[" + vo.getUrl() + "] ");
logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") PARAMETER_NAME=[" + vo.getParameterName() + "] ");
logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") ENCODE=[" + vo.getEncode() + "] ");
logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") RESPONSE_TYPE=[" + vo.getResponseType() + "] ");
logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") URL_ENCODE_YN=[" + vo.getUrlEncodeYn() + "] ");
logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") CONNECTION_TIMEOUT=[" + vo.getConnectionTimeout() + "] ");
logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") REST_OPTION=[" + restOptionData + "] ");
}
HttpUriRequestBase method = null;
// 메소드 타입에 따라 HttpRequestBase 인스턴스 생성
switch (HttpMethodType.getValue(rmethod)) {
case GET:
method = new HttpGet(uri);
break;
case DELETE:
method = new HttpDelete(uri);
break;
case POST:
method = new HttpPost(uri);
break;
case PUT:
method = new HttpPut(uri);
break;
default:
method = new HttpGet(uri);
break;
}
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);
}
configureHttpClient(requestConfigBuilder, vo, method);
switch (HttpMethodType.getValue(rmethod)) {
case GET:
case DELETE:
HashMap<String, String> h = getParameters(dataObject);
URIBuilder uriBuilder = new URIBuilder(uri);
for (Map.Entry<String, String> entry : h.entrySet()) {
uriBuilder.addParameter(entry.getKey(), entry.getValue());
}
URI fullUri = uriBuilder.build();
if (method instanceof HttpGet) {
((HttpGet) method).setUri(fullUri);
} else if (method instanceof HttpDelete) {
((HttpDelete) method).setUri(fullUri);
}
if (logger.isDebug()) {
logger.debug("HttpClientAdapterServiceRest] (get Method) QueryString = [" + fullUri.getQuery() + "]");
}
break;
case PUT:
case POST:
//assignPostBody(dataObject, contentType, vo.getEncode(), method);
assignPostBody(dataContent, contentType, vo.getEncode(), method);
break;
default:
break;
}
if (logger.isDebugEnabled()) {
logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") method.getParams()=["
+ method.getEntity() + "] ");
}
OAuth2AccessTokenVO accessToken = null;
if (useAdapterToken) {
AccessTokenManagerByDB tokenManager = AccessTokenManagerByDB.getInstance();
accessToken = (OAuth2AccessTokenVO) tokenManager.getAccessTokenVO(vo.getAdapterGroupName());
// 토큰이 없거나 만료 됐으면 재발급
if (accessToken == null || accessToken.isExpired()) {
String oldToken = accessToken == null ? null : accessToken.getAccessToken();
accessToken = (OAuth2AccessTokenVO) tokenManager.retryAccessTokenVO(vo.getAdapterGroupName(), prop,
oldToken);
}
if (logger.isDebug()) {
logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") TOKEN = ["
+ accessToken + "]");
}
setAuthHeaders(method, accessToken);
if (logger.isDebug()) {
logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName()
+ ") RequestHeader [Authorization=" + method.getHeader("Authorization") + "]");
}
}
// 2025.04.09 Authorization 헤더값을 프로퍼티(AUTH_TOKEN)에서 지정하여 설정할 수 있도록 수정
if(!StringUtils.isBlank(authToken)) {
setAuthHeaders(method, authToken);
}
// 전달 header 셋팅
// Adapter 레벨 header 보다 data로 넘어온 httpHeader를 우선 적용(header명이 같다면 덮어씀)
assignRequestHeaders(method, httpHeader);
int status = -1;
try {
byte[] responseMessage = null;
Header[] responseHeaders;
if (logger.isDebug() && "N".equals(vo.getTestCallYn())) {
if (dataContent != null) { //dataObject
logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") = ["
+ dataContent.toString() + "]"); //dataObject.toJSONString()
logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") = "
+ CommonLib.getDumpMessage(dataContent.toString())); //dataObject.toJSONString()
} else {
logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") = [" + sendData
+ "]");
logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") = "
+ CommonLib.getDumpMessage(sendData));
}
}
StopWatch stopWatch = new StopWatch();
stopWatch.start();
if (vo.getTraceLevel() >= 3) {
HttpMemoryLogger.txlog(vo.getAdapterGroupName() + vo.getAdapterName(),
"SEND [" + sendData + "]" + CommonLib.getDumpMessage(sendData));
}
String uuid = tempProp.getProperty(TransactionContextKeys.TRANSACTION_UUID);
HttpContext context = new BasicHttpContext();
context.setAttribute(TransactionContextKeys.TRANSACTION_UUID, uuid);
context.setAttribute(HttpClientAdapterServiceKey.ADAPTER_GROUP_NAME, vo.getAdapterGroupName());
context.setAttribute(HttpClientAdapterServiceKey.ADAPTER_NAME, vo.getAdapterName());
Integer logProcessNo = (Integer) tempProp.get(HttpClientAdapterServiceKey.LOG_PROCESS_NO);
if (logProcessNo != null && logProcessNo > 0) {
context.setAttribute(HttpClientAdapterServiceKey.LOG_PROCESS_NO, logProcessNo);
}
try {
if (logger.isDebug()) {
logger.debug("[method getName]" + method.getMethod());
logger.debug("[method getEntity]" + method.getEntity());
logger.debug("[method getRequestHeaders]" + method.getHeaders().toString());
logger.debug("[method getURI]" + method.getPath());
}
try (CloseableHttpResponse response = (CloseableHttpResponse) mclient.execute(method, context)) {
status = response.getCode();
if (status >= 200 && status <= 207) {
status = 200;
}
// if (status == 404) {
// status = 200;
// }
// responseMessage = EntityUtils.toByteArray(response.getEntity());
responseHeaders = response.getHeaders();
// 2025/08/06 Response body를 Base64로 Encode 해서 Json으로 만들어서 전달하도록 수정
HttpEntity entity = response.getEntity();
if (entity != null) {
byte[] entityBytes = EntityUtils.toByteArray(response.getEntity());
String b64 = Base64.encodeBase64String(entityBytes);
JSONObject resultJson = new JSONObject();
resultJson.put("base64_file_data", b64);
responseMessage = resultJson.toString().getBytes();
} else {
responseMessage = EntityUtils.toByteArray(response.getEntity());
}
if (logger.isDebug()) {
logger.debug("[received status]" + status);
logger.debug("HttpClientAdapterServiceRest] RECV (" + vo.getAdapterGroupName() + ") = [" + responseMessage + "]");
}
}
} catch (ConnectException e) {
if (logger.isDebug() && "N".equals(vo.getTestCallYn())) {
logger.debug("HttpClientAdapterServiceRest] responseType =" + vo.getResponseType());
}
throw new Exception("excuteMethod java.net.ConnectException = " + e.getMessage());
}
stopWatch.stop();
/**
* @formatter:off
* OAuth 토큰 응답 체크(Adapter properties로 설정)
* 응답코드에 따라 유효한 토큰 확인(토큰 재발급 여부 확인)
* API 서비스 마다 정책이 다름(보통 400대에서 체크하나 200에서도 체크할 수 있음)
*
* TOKEN_ERROR_CODE_KEY: 응답 json error code key
* __ex) $.dataHeader.resultCode
* TOKEN_ERROR_CODE_VALUES: 토큰 재발급이 필요한 응답 error codes(ex: O0001,O0002,O0003)
* TOKEN_ERROR_HTTP_STATUS_CODE: 보통 400대에서 체크하나 API 제공자에 따라 200에서도 체클할수 있음
* ex) TOKEN_ERROR_HTTP_STATUS_CODE = 200
* @formatter:on
*/
boolean needReissue = false;
Properties responseHeaderProp = assignRelayDataToInbound(tempProp, responseHeaders);
String responseString = new String(responseMessage, vo.getEncode());
Map<String, Object> map = new HashMap<String, Object>();
tempProp.put(HttpAdapterServiceKey.OUTBOUND_PROPERTY_MAP, map);
map.put(HttpAdapterServiceKey.OUTBOUND_RESPONSE_HEADERS, responseHeaderProp);
map.put(HttpAdapterServiceKey.OUTBOUND_RESPONSE_MESSAGE, responseString);
map.put(HttpAdapterServiceKey.OUTBOUND_RESPONSE_STATUS, status);
if (status >= 200 && status <= 207) {
if (useAdapterToken && StringUtils.equals(tokenErrorHttpStatusCode, "200")) {
needReissue = checkTokenRetry(responseMessage, tokenErrorCodeKey, tokenErrorCodeValues,
vo.getEncode());
}
} else if (status == 302) {
if (!StringUtils.contains(relayResponseHeaderKeys, "Location")) {
relayResponseHeaderKeys += ",Location";
}
} else {
String errMsg = String.format(
"http receive status fail value=%d adapterName=%s.%s uri=%s method=%s response Data=%s",
status, vo.getAdapterGroupName(), vo.getAdapterName(), uri, rmethod, responseString);
// logger.error(errMsg);
if (status >= 400 && status < 500) {
if (useAdapterToken) {
needReissue = checkTokenRetry(responseMessage, tokenErrorCodeKey, tokenErrorCodeValues,
vo.getEncode());
}
// if (!needReissue) {
// throw new Exception(errMsg);
// }
} else {
throw new Exception(errMsg);
}
}
// ----------------------------------------------------------
if (logger.isDebug() && "N".equals(vo.getTestCallYn())) {
logger.debug("HttpClientAdapterServiceRest] responseType =" + vo.getResponseType());
logger.debug("HttpClientAdapterServiceRest] RECV " + vo.getEncode() + "(" + vo.getAdapterGroupName() + ") = [" + new String(responseMessage, vo.getEncode()) + "]");
// logger.debug("HttpClientAdapterServiceRest] RECV MS949 (" + vo.getAdapterGroupName() + ") = [" + new String(responseMessage, "MS949") + "]");
// logger.debug("HttpClientAdapterServiceRest] RECV euc-kr (" + vo.getAdapterGroupName() + ") = [" + new String(responseMessage, "euc-kr") + "]");
logger.debug("HttpClientAdapterServiceRest] RECV (" + vo.getAdapterGroupName() + ") = " + CommonLib.getDumpMessage(responseMessage));
}
if (useAdapterToken) {
if (responseMessage == null) {
throw new Exception("responseMessage is NULL, HttpStatus=" + status);
}
// OAuth 토큰 재요청 코드 확인
if (needReissue) {
if (logger.isDebug() && "N".equals(vo.getTestCallYn())) {
logger.debug("HttpClientAdapterServiceRest] retry access token response code = ["
+ vo.getResponseType() + "] message = [" + new String(responseMessage, vo.getEncode())
+ "]");
}
// 토큰 재발급
AccessTokenManager tokenManager = AccessTokenManager.getInstance();
String oldToken = accessToken == null ? null : accessToken.getAccessToken();
OAuth2AccessTokenVO newaccessToken = (OAuth2AccessTokenVO) tokenManager
.retryAccessTokenVO(vo.getAdapterGroupName(), prop, oldToken);
method.setHeader("Authorization", newaccessToken.getAuthorization());
setAuthHeaders(method, newaccessToken);
if (logger.isDebug() && "N".equals(vo.getTestCallYn())) {
logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName()
+ ") RETRY TOKEN = [" + newaccessToken + "]");
}
try (CloseableHttpResponse response = (CloseableHttpResponse) mclient.execute(method)) {
status = response.getCode();
responseMessage = EntityUtils.toByteArray(response.getEntity());
if (logger.isDebug()) {
logger.debug("[received status]" + status);
logger.debug("HttpClientAdapterServiceRest] RECV (" + vo.getAdapterGroupName() + ") = [" + new String(responseMessage, vo.getEncode()) + "]");
}
}catch (IOException e){
throw new Exception("retry excuteMethod Exceptioin = " + e.getMessage());
}
if (status == 302) {
if (!StringUtils.contains(relayResponseHeaderKeys, "Location")) {
relayResponseHeaderKeys += ",Location";
}
} else {
String errMsg = String.format(
"http receive status fail value=%d adapterName=%s.%s uri=%s method=%s response Data=%s",
status, vo.getAdapterGroupName(), vo.getAdapterName(), uri, rmethod, responseMessage);
throw new Exception(errMsg);
}
// ----------------------------------------------------------
if (logger.isDebug() && "N".equals(vo.getTestCallYn())) {
logger.debug("HttpClientAdapterServiceRest] RETRY responseType =" + vo.getResponseType());
logger.debug("HttpClientAdapterServiceRest] RETRY RECV " + vo.getEncode() + "("
+ vo.getAdapterGroupName() + ") = [" + new String(responseMessage, vo.getEncode())
+ "]");
logger.debug("HttpClientAdapterServiceRest] RETRY RECV (" + vo.getAdapterGroupName() + ") = "
+ CommonLib.getDumpMessage(responseMessage));
}
// ----------------------------------------------------------
}
}
if (vo.getTraceLevel() >= 3) {
HttpMemoryLogger.txlog(vo.getAdapterGroupName() + vo.getAdapterName(),
"RECV [" + new String(responseMessage, vo.getEncode()) + "]"
+ CommonLib.getDumpMessage(responseMessage));
}
return assignResponseHeaders(method, responseMessage, headerGroupName, vo.getEncode(), messageType,
relayResponseHeaderKeys, status, responseHeaderProp);
} catch (SocketTimeoutException ste) { // Read Timeout :: HttpClient 3.1일 경우
if (vo.getTraceLevel() >= 3) {
HttpMemoryLogger.error(vo.getAdapterGroupName() + vo.getAdapterName(),
"HttpClientAdapterServiceRest] SocketTimeoutException (" + vo.getAdapterGroupName() + ") : "
+ ste.toString(),
ste);
}
logger.error("HttpClientAdapterServiceRest] SocketTimeoutException (" + vo.getAdapterGroupName() + ") : "
+ ste.toString(), ste);
throw ste;
} catch (ConnectException ce) {
if (vo.getTraceLevel() >= 3) {
HttpMemoryLogger.error(vo.getAdapterGroupName() + vo.getAdapterName(),
"HttpClientAdapterServiceRest] Connection Exception (" + vo.getAdapterGroupName() + ") : "
+ ce.toString(),
ce);
}
logger.error("HttpClientAdapterServiceRest] Connection Exception (" + vo.getAdapterGroupName() + ") : "
+ ce.toString(), ce);
throw ce;
} catch (Exception e) {
if (vo.getTraceLevel() >= 3) {
HttpMemoryLogger.error(vo.getAdapterGroupName() + vo.getAdapterName(), e.toString(), e);
}
logger.error("HttpClientAdapterServiceRest] Exception (" + vo.getAdapterGroupName() + ") : " + e.toString(),
e);
throw e;
} finally {
if (status != HttpStatus.SC_OK) {
String[] msgArgs = new String[1];
msgArgs[0] = String.valueOf(status);
String resMsg = ExceptionUtil.make("RDCEAIAHA013", msgArgs);
if (logger.isDebug()) {
logger.debug(resMsg);
}
}
}
}
private Properties assignRelayDataToInbound(Properties prop, Header[] responseHeaders) {
Properties headerProp = new Properties();
if (responseHeaders == null || responseHeaders.length == 0) {
return headerProp;
}
for (Header header : responseHeaders) {
headerProp.put(header.getName(), header.getValue());
}
return headerProp;
}
/**
* 현재 1단계 레이아웃에만 적용되도록 구현됨.
*
* @param method
* @param responseMessage
* @param headerGroupName
* @param encode
* @param messageType
* @param relayHeaderKeys
* @return
* @throws Exception
*/
@SuppressWarnings("unchecked")
private String assignResponseHeaders(HttpUriRequestBase method, byte[] responseMessage, String headerGroupName,
String encode, String messageType, String relayHeaderKeys, int status, Properties responseHeaderProp) throws Exception {
if (!MessageType.JSON.equals(messageType) || StringUtils.isBlank(headerGroupName)
|| StringUtils.isBlank(relayHeaderKeys)) {
return new String(responseMessage, encode);
}
String[] relayKeyArr = org.springframework.util.StringUtils.tokenizeToStringArray(relayHeaderKeys, ",");
JSONObject headerJson = new JSONObject();
for (String key : relayKeyArr) {
String value = responseHeaderProp.getProperty(key);
if (StringUtils.isNotBlank(value)) {
headerJson.put(key, value);
}
}
headerJson.put(HTTP_STATUS, String.valueOf(status));
if (headerJson.size() <= 0) {
return new String(responseMessage, encode);
}
JSONObject message = null;
if (responseMessage == null || responseMessage.length == 0) {
message = new JSONObject();
} else {
message = parseJson(new String(responseMessage, encode));
if (message == null) {
message = new JSONObject();
message.put("Malformed_Response_Message", new String(responseMessage, encode));
}
}
if (StringUtils.isNotBlank(headerGroupName) && message != null)
message.put(headerGroupName, headerJson);
logger.info("------------- message --------------- : {} ", message);
return message.toJSONString();
}
// 2025.04.09 Authorization 헤더값을 프로퍼티(AUTH_TOKEN)에서 지정하여 설정할 수 있도록 수정
private void setAuthHeaders(HttpUriRequestBase method, String authorization) throws Exception {
method.setHeader("Authorization",
String.format("%s", authorization));
}
private void setAuthHeaders(HttpUriRequestBase method, OAuth2AccessTokenVO accessToken) throws Exception {
method.setHeader("Authorization",
String.format("%s %s", AccessTokenVO.BEARER_TYPE, accessToken.getAccessToken()));
}
private boolean checkTokenRetry(byte[] responseMessage, String tokenErrorCodeKey, String tokenErrorCodeValues,
String encode) {
if (responseMessage == null) {
return false;
}
if (StringUtils.isBlank(tokenErrorCodeKey)) {
return false;
}
if (StringUtils.isBlank(tokenErrorCodeValues)) {
return false;
}
try {
DocumentContext jsonContext = JsonPath.parse(new String(responseMessage, encode));
String responseCode = jsonContext.read(tokenErrorCodeKey);
if (StringUtils.isBlank(responseCode)) {
return false;
}
String[] arr = org.springframework.util.StringUtils.tokenizeToStringArray(tokenErrorCodeValues, ",");
return ArrayUtils.contains(arr, responseCode);
} catch (Exception e) {
logger.error("HttpClientAdapterServiceRest] checkTokenRetry error=" + e.getMessage());
}
return false;
}
private void assignRequestHeaders(HttpUriRequestBase method, Object httpHeader) {
if (httpHeader == null) {
return;
}
if (httpHeader instanceof JSONObject) {
JSONObject jsonObject = (JSONObject) httpHeader;
for (Object key : jsonObject.keySet()) {
Object obj = jsonObject.get(key);
if ((obj instanceof JSONObject) || (obj instanceof JSONArray)) {
continue;
}
method.setHeader((String) key, (String) obj);
if (logger.isDebugEnabled()) {
logger.debug("Request Header :" + (String) key + "=[" + (String) obj + "]");
}
}
}
}
@SuppressWarnings("deprecation")
private void assignPostBody(Object eaiBody, String contentType, String charset, HttpUriRequestBase method) {
if (eaiBody == null) {
return;
}
if (StringUtils.contains(contentType, "application/x-www-form-urlencoded")) {
if (eaiBody instanceof JSONObject) {
List<NameValuePair> params = new ArrayList<>();
JSONObject jsonObject = (JSONObject) eaiBody;
for (Object key : jsonObject.keySet()) {
params.add(new BasicNameValuePair((String) key, (String) jsonObject.get(key)));
}
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(params, StandardCharsets.UTF_8);
method.setEntity(entity);
}
} else {
ContentType contentTypeObj = ContentType.create(contentType, charset);
// 요청 본문을 StringEntity 객체로 생성
StringEntity entity = new StringEntity(eaiBody.toString(), contentTypeObj);
// 요청에 본문 추가
method.setEntity(entity);
}
}
private HashMap<String, String> getParameters(Object message) throws Exception {
HashMap<String, String> result = new HashMap<String, String>();
if (message != null) {
if (message instanceof JSONObject) {
JSONObject jsonObject = (JSONObject) message;
for (Object key : jsonObject.keySet()) {
Object obj = jsonObject.get(key);
if ((obj instanceof JSONObject) || (obj instanceof JSONArray)) {
continue;
}
result.put((String) key, getStringValue(obj));
}
} else {
String[] messages = ((String) message).split("&");
String[] data = null;
for (int i = 0; i < messages.length; i++) {
data = messages[i].split("=", 2);
if (data.length == 2) {
result.put(data[0], data[1]);
}
}
}
}
return result;
}
public String getStringValue(Object obj) {
if (obj == null) {
return null;
}
if (obj instanceof String) {
return (String) obj;
} else if (obj instanceof Long) {
return Long.toString((Long) obj);
} else if (obj instanceof BigDecimal) {
return ((BigDecimal) obj).toPlainString();
} else {
return (String) obj;
}
}
public static Object parseJsonGeneric(String jsonData) {
if (StringUtils.isBlank(jsonData)) {
return null;
}
try {
JSONParser parser = new JSONParser();
Object parsedObj = parser.parse(jsonData);
return parsedObj;
} catch (ParseException e) {
e.printStackTrace();
return null;
}
}
@SuppressWarnings("deprecation")
private JSONObject parseJson(String message) throws Exception {
if (message == null) {
return null;
}
return (JSONObject) JSONValue.parse(message);
}
private Document convertXmlDocument(String message) throws Exception {
SAXReader builder = new SAXReader();
Document document = builder.read(new StringReader(message));
return document;
}
@SuppressWarnings("deprecation")
private String changeUrl(String messageType, String url, String restOption, Object sendData) {
if ((MessageType.JSON.equals(messageType) || MessageType.XML.equals(messageType))) {
try {
if (StringUtils.isBlank(restOption)) {
return url;
}
List<String> urlVariableList = new ArrayList<String>();
JSONObject restOptionObject = parseJson(restOption);
if (restOptionObject == null)
throw new Exception("restOption is NULL");
String type = (String) restOptionObject.get("type");
String requestExtraPath = (String) restOptionObject.get("extraPath");
url = getUrl(url, requestExtraPath);
if (TYPE_VARIABLE_URL_REQUEST.equals(type)) {
Pattern p = Pattern.compile("\\{(.*?)\\}");
Matcher m = p.matcher(requestExtraPath);
List<String> uriVariables = new ArrayList<>();
while(m.find()) {
String fieldName = m.group(1);
uriVariables.add(fieldName);
}
if (MessageType.JSON.equals(messageType)) {
JSONObject jsonObject;
if (sendData instanceof JSONObject) {
jsonObject = (JSONObject) sendData;
} else {
jsonObject = (JSONObject) JSONValue.parse((String) sendData);
}
for (Object tempObject : uriVariables) {
String urlVaribleId = (String) tempObject;
String uriVariable = (String) jsonObject.get(urlVaribleId);
urlVariableList.add(uriVariable);
jsonObject.remove(urlVaribleId);
}
} else if (MessageType.XML.equals(messageType)) {
Document doc;
if (sendData instanceof Document) {
doc = (Document) sendData;
} else {
doc = convertXmlDocument((String) sendData);
}
for (Object tempObject : uriVariables) {
String urlVaribleId = (String) tempObject;
Element element = (Element) doc.selectSingleNode("//" + urlVaribleId);
urlVariableList.add(element.getText());
if (doc.getRootElement() == element) {
doc = null;
} else {
element.getParent().remove(element);
}
}
}
if (urlVariableList.size() > 0) {
UriComponents uriComponents = UriComponentsBuilder.fromUriString(url).build();
Object[] urls = urlVariableList.toArray();
url = uriComponents.expand(urls).toUriString();
logger.debug("HttpClientAdapterServiceRest] after extand url=[" + url + "] ");
}
}
} catch (Exception e) {
logger.error("Failed to change url", e);
}
}
return url;
}
private String getUrl(String baseUrl, String extraPath) {
if (StringUtils.isBlank(extraPath)) {
return baseUrl;
}
if (StringUtils.contains(extraPath, "http://") || StringUtils.contains(extraPath, "https://")) {
return extraPath;
}
String targetUrl = baseUrl;
if (!targetUrl.endsWith("/")) {
targetUrl += "/";
}
if (extraPath.startsWith("/")) {
targetUrl += extraPath.substring(1);
} else {
targetUrl += extraPath;
}
logger.debug("HttpClientAdapterServiceRest] after concatenate url=[" + targetUrl + "] ");
return targetUrl;
}
}
@@ -0,0 +1,680 @@
package com.eactive.eai.custom.adapter.http.client.impl;
import java.io.IOException;
import java.net.ConnectException;
import java.net.SocketTimeoutException;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Formatter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.StopWatch;
import org.apache.hc.client5.http.classic.HttpClient;
import org.apache.hc.client5.http.classic.methods.HttpDelete;
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.client5.http.classic.methods.HttpPut;
import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HttpStatus;
import org.apache.hc.core5.http.io.entity.ByteArrayEntity;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.http.protocol.BasicHttpContext;
import org.apache.hc.core5.http.protocol.HttpContext;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
import com.eactive.eai.adapter.http.HttpMemoryLogger;
import com.eactive.eai.adapter.http.HttpMethodType;
import com.eactive.eai.adapter.http.client.HttpClient5AdapterServiceSupport;
import com.eactive.eai.adapter.http.client.HttpClientAdapterServiceKey;
import com.eactive.eai.adapter.http.client.HttpClientAdapterVO;
import com.eactive.eai.adapter.http.client.impl.HttpClientAdapterServiceRest;
import com.eactive.eai.adapter.http.dynamic.HttpAdapterServiceKey;
import com.eactive.eai.adapter.http.dynamic.impl.HttpAdapterServiceBypass;
import com.eactive.eai.common.TransactionContextKeys;
import com.eactive.eai.common.exception.ExceptionUtil;
import com.eactive.eai.common.util.CommonLib;
import com.jayway.jsonpath.DocumentContext;
import com.jayway.jsonpath.JsonPath;
import com.openbanking.eai.common.token.AccessTokenManager;
import com.openbanking.eai.common.token.AccessTokenVO;
import com.openbanking.eai.common.token.OAuth2AccessTokenVO;
public class HttpClient5AdapterServiceBypass extends HttpClient5AdapterServiceSupport
implements HttpClientAdapterServiceKey {
public static final String TYPE_BYPASS_REQUEST = "bypassRequest";
public static final String REST_OPTION = "REST_OPTION";
protected boolean doSendUrlFragment = true;
protected boolean doHandleCompression = false;
protected boolean doForwardIP = false;
@SuppressWarnings({ "unchecked" })
public Object execute(Properties prop, Object data, Properties tempProp) throws Exception {
HttpClientAdapterVO vo = super.setting(prop, tempProp);
boolean useAdapterToken = StringUtils.equalsIgnoreCase(prop.getProperty("ADAPTER_TOKEN_USE_YN", "N"), "Y");
// ex) $.dataHeader.GW_RSLT_CD
String tokenErrorCodeKey = prop.getProperty("ADAPTER_TOKEN_ERROR_CODE_KEY");
String tokenErrorCodeValues = prop.getProperty("ADAPTER_TOKEN_ERROR_CODE_VALUES");
String tokenErrorHttpStatusCode = prop.getProperty("ADAPTER_TOKEN_ERROR_HTTP_STATUS_CODE"); // 200 or 400번대
/**
* @formatter:off
* TSEAIHE02.RESTOPTION 정보 => JSON 형태로 구성
* - type : simpleRequest, variableUrlRequest
* - extraPath : 어댑터 프로퍼티 URL에 추가될 HTTP REST URL
* - method : get, delete, post, put
* - contentType : application/json, application/x-www-form-urlencoded
* - adapterTokenUseYn: Y, N(default)
* ex)
* {"type":"variableUrlRequest","extraPath":"/aaa/{userId}","uriVariables":["userId"]}
* @formatter:on
*/
String restOptionData = tempProp.getProperty(REST_OPTION, "{}");
JSONObject restOptionObject = parseJson(restOptionData);
String adapterTokenUseYn = (String) restOptionObject.get("adapterTokenUseYn");
if (StringUtils.isNotBlank(adapterTokenUseYn)) { // interface 에 설정된게 우선한다.
useAdapterToken = StringUtils.equalsIgnoreCase(adapterTokenUseYn, "Y");
}
String inboundMethod = tempProp.getProperty(HttpAdapterServiceKey.INBOUND_METHOD, "POST");
String inboundRewritePath = tempProp.getProperty(HttpAdapterServiceKey.INBOUND_REWRITE_PATH, "");
String inboundQueryString = tempProp.getProperty(HttpAdapterServiceKey.INBOUND_QUERY_STRING, "");
Properties inboundHeaders = (Properties) tempProp.get(HttpAdapterServiceKey.INBOUND_HEADER);
Map<String, String> inboundPathVariables = (Map<String, String>) tempProp
.get(HttpAdapterServiceKey.INBOUND_PATH_VARIABLES);
String restMethod = (String) restOptionObject.get("method");
if (StringUtils.isBlank(restMethod)) {
restMethod = inboundMethod;
}
String url = getRewriteUrl(vo, inboundRewritePath, inboundQueryString, restOptionObject, inboundPathVariables);
if (logger.isDebug()) {
logger.debug("HttpClient5AdapterServiceBypass] url = [" + url + "]");
}
HttpClient mclient = this.client;
HttpUriRequestBase method = generateMethod(restMethod, url);
assignRequestHeaders(method, inboundHeaders, tempProp);
if (hasBody(data, method)) {
byte[] bodyBytes;
if (data instanceof byte[]) {
bodyBytes = (byte[]) data;
} else if (data instanceof String) {
bodyBytes = ((String) data).getBytes(vo.getEncode());
} else {
bodyBytes = new byte[0];
}
method.setEntity(new ByteArrayEntity(bodyBytes, null));
}
String contentType = (String) restOptionObject.get("contentType");
if (StringUtils.isBlank(contentType) && inboundHeaders != null) {
contentType = getIgnoreCaseProp(inboundHeaders, HttpHeaders.CONTENT_TYPE);
contentType = StringUtils.substringBefore(contentType, ";");
}
RequestConfig.Builder requestConfigBuilder = RequestConfig.custom();
configureHttpClient(requestConfigBuilder, vo, method);
OAuth2AccessTokenVO accessToken = null;
if (useAdapterToken) {
AccessTokenManager tokenManager = AccessTokenManager.getInstance();
accessToken = (OAuth2AccessTokenVO) tokenManager.getAccessTokenVO(vo.getAdapterGroupName());
// 토큰이 없거나 만료 됐으면 재발급
if (accessToken == null || accessToken.isExpired()) {
String oldToken = accessToken == null ? null : accessToken.getAccessToken();
accessToken = (OAuth2AccessTokenVO) tokenManager.retryAccessTokenVO(vo.getAdapterGroupName(), prop,
oldToken);
}
if (logger.isDebug()) {
logger.debug("HttpClient5AdapterServiceBypass] SEND (" + vo.getAdapterGroupName() + ") TOKEN = ["
+ accessToken + "]");
}
setAuthHeaders(method, accessToken);
if (logger.isDebug()) {
logger.debug("HttpClient5AdapterServiceBypass] SEND (" + vo.getAdapterGroupName()
+ ") RequestHeader [Authorization=" + method.getFirstHeader("Authorization") + "]");
}
}
// 로깅 인터셉터에 전달할 컨텍스트 정보 설정
String uuid = tempProp.getProperty(TransactionContextKeys.TRANSACTION_UUID);
HttpContext context = new BasicHttpContext();
context.setAttribute(TransactionContextKeys.TRANSACTION_UUID, uuid);
context.setAttribute(HttpClientAdapterServiceKey.ADAPTER_GROUP_NAME, vo.getAdapterGroupName());
context.setAttribute(HttpClientAdapterServiceKey.ADAPTER_NAME, vo.getAdapterName());
Integer logProcessNo = (Integer) tempProp.get(HttpClientAdapterServiceKey.LOG_PROCESS_NO);
if (logProcessNo != null && logProcessNo > 0) {
context.setAttribute(HttpClientAdapterServiceKey.LOG_PROCESS_NO, logProcessNo);
}
int status = -1;
try {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
byte[] responseMessage = null;
Header[] responseHeaders = null;
if (vo.getTraceLevel() >= 3) {
HttpMemoryLogger.txlog(vo.getAdapterGroupName() + vo.getAdapterName(),
"SEND [Bypass Request..]" + CommonLib.getDumpMessage(data));
}
try {
if (logger.isDebug()) {
logger.debug("[method getName]" + method.getMethod());
logger.debug("[method getRequestHeaders]" + java.util.Arrays.toString(method.getHeaders()));
logger.debug("[method getURI]" + method.getPath());
}
try (CloseableHttpResponse response = (CloseableHttpResponse) mclient.execute(method, context)) {
status = response.getCode();
responseHeaders = response.getHeaders();
responseMessage = EntityUtils.toByteArray(response.getEntity());
if (logger.isDebug()) {
logger.debug("[received status]" + status);
}
}
} catch (ConnectException e) {
if (logger.isDebug() && "N".equals(vo.getTestCallYn())) {
logger.debug("HttpClient5AdapterServiceBypass] responseType =" + vo.getResponseType());
logger.debug("HttpClient5AdapterServiceBypass] RECV (" + vo.getAdapterGroupName() + ") = "
+ CommonLib.getDumpMessage(responseMessage));
}
throw new Exception("excuteMethod java.net.ConnectException = " + e.getMessage());
}
stopWatch.stop();
/**
* @formatter:off
* OAuth 토큰 응답 체크(Adapter properties로 설정)
* 응답코드에 따라 유효한 토큰 확인(토큰 재발급 여부 확인)
* API 서비스 마다 정책이 다름(보통 400대에서 체크하나 200에서도 체크할 수 있음)
*
* TOKEN_ERROR_CODE_KEY: 응답 json error code key
* __ex) $.dataHeader.resultCode
* TOKEN_ERROR_CODE_VALUES: 토큰 재발급이 필요한 응답 error codes(ex: O0001,O0002,O0003)
* TOKEN_ERROR_HTTP_STATUS_CODE: 보통 400대에서 체크하나 API 제공자에 따라 200에서도 체클할수 있음
* ex) TOKEN_ERROR_HTTP_STATUS_CODE = 200
* @formatter:on
*/
boolean needReissue = false;
if (status == 200) {
if (useAdapterToken && StringUtils.equals(tokenErrorHttpStatusCode, "200")) {
needReissue = checkTokenRetry(responseMessage, tokenErrorCodeKey, tokenErrorCodeValues,
vo.getEncode());
}
} else if (status >= 400 && status < 500) {
if (useAdapterToken) {
needReissue = checkTokenRetry(responseMessage, tokenErrorCodeKey, tokenErrorCodeValues,
vo.getEncode());
}
if (!needReissue) {
// body 값이 없을때만 exception 처리하고, body가 있으면 bypass 한다.
if (responseMessage == null || responseMessage.length == 0) {
String errMsg = String.format(
"http receive status fail value=%d adapterName=%s.%s uri=%s method=%s response Data=%s",
status, vo.getAdapterGroupName(), vo.getAdapterName(), url, restMethod, responseMessage);
throw new Exception(errMsg);
}
}
} else if (status != 200) {
responseMessage = responseMessage != null ? responseMessage : new byte[0];
// body 값이 없을때만 exception 처리하고, body가 있으면 bypass 한다.
if (responseMessage.length == 0) {
String errMsg = String.format(
"http receive status fail value=%d adapterName=%s.%s uri=%s method=%s response Data=%s",
status, vo.getAdapterGroupName(), vo.getAdapterName(), url, restMethod, responseMessage);
throw new Exception(errMsg);
}
}
if (useAdapterToken) {
if (responseMessage == null) {
throw new Exception("responseMessage is NULL, HttpStatus=" + status);
}
// OAuth 토큰 재요청 코드 확인
if (needReissue) {
if (logger.isDebug() && "N".equals(vo.getTestCallYn())) {
logger.debug("HttpClient5AdapterServiceBypass] retry access token response code = ["
+ vo.getResponseType() + "] message = [" + new String(responseMessage, vo.getEncode())
+ "]");
}
// 토큰 재발급
AccessTokenManager tokenManager = AccessTokenManager.getInstance();
String oldToken = accessToken == null ? null : accessToken.getAccessToken();
OAuth2AccessTokenVO newAccessToken = (OAuth2AccessTokenVO) tokenManager
.retryAccessTokenVO(vo.getAdapterGroupName(), prop, oldToken);
method.setHeader("Authorization", newAccessToken.getAuthorization());
setAuthHeaders(method, newAccessToken);
if (logger.isDebug() && "N".equals(vo.getTestCallYn())) {
logger.debug("HttpClient5AdapterServiceBypass] SEND (" + vo.getAdapterGroupName()
+ ") RETRY TOKEN = [" + newAccessToken + "]");
}
try (CloseableHttpResponse retryResponse = (CloseableHttpResponse) mclient.execute(method, context)) {
status = retryResponse.getCode();
responseHeaders = retryResponse.getHeaders();
responseMessage = EntityUtils.toByteArray(retryResponse.getEntity());
} catch (IOException e) {
throw new Exception("retry excuteMethod Exception = " + e.getMessage());
}
if (status != 200) {
responseMessage = responseMessage != null ? responseMessage : new byte[0];
if (responseMessage.length == 0) {
String errMsg = String.format(
"http receive status fail value=%d adapterName=%s.%s uri=%s method=%s response Data=%s",
status, vo.getAdapterGroupName(), vo.getAdapterName(), url, restMethod,
responseMessage);
throw new Exception(errMsg);
}
}
if (logger.isDebug() && "N".equals(vo.getTestCallYn())) {
logger.debug("HttpClient5AdapterServiceBypass] RETRY responseType =" + vo.getResponseType());
logger.debug("HttpClient5AdapterServiceBypass] RETRY RECV (" + vo.getAdapterGroupName() + ") = "
+ CommonLib.getDumpMessage(responseMessage));
}
}
}
if (vo.getTraceLevel() >= 3) {
HttpMemoryLogger.txlog(vo.getAdapterGroupName() + vo.getAdapterName(),
"RECV [Bypass Request..]" + CommonLib.getDumpMessage(responseMessage));
}
// 응답 Content-Type charset 기반 인코딩 결정, 없으면 어댑터 encoding 사용
String responseEncode = vo.getEncode();
if (responseHeaders != null) {
for (Header header : responseHeaders) {
if (StringUtils.equalsIgnoreCase(header.getName(), HttpHeaders.CONTENT_TYPE)) {
try {
MediaType mediaType = MediaType.parseMediaType(header.getValue());
if (mediaType.getCharset() != null) {
responseEncode = mediaType.getCharset().name();
}
} catch (Exception ignored) {}
break;
}
}
}
assignRelayDataToInbound(tempProp, responseHeaders, status);
return responseMessage != null ? new String(responseMessage, responseEncode) : "";
} catch (SocketTimeoutException ste) {
if (vo.getTraceLevel() >= 3) {
HttpMemoryLogger.error(vo.getAdapterGroupName() + vo.getAdapterName(),
"HttpClient5AdapterServiceBypass] SocketTimeoutException (" + vo.getAdapterGroupName() + ") : "
+ ste.toString(),
ste);
}
logger.error("HttpClient5AdapterServiceBypass] SocketTimeoutException (" + vo.getAdapterGroupName() + ") : "
+ ste.toString(), ste);
throw ste;
} catch (ConnectException ce) {
if (vo.getTraceLevel() >= 3) {
HttpMemoryLogger.error(vo.getAdapterGroupName() + vo.getAdapterName(),
"HttpClient5AdapterServiceBypass] Connection Exception (" + vo.getAdapterGroupName() + ") : "
+ ce.toString(),
ce);
}
logger.error("HttpClient5AdapterServiceBypass] Connection Exception (" + vo.getAdapterGroupName() + ") : "
+ ce.toString(), ce);
throw ce;
} catch (Exception e) {
if (vo.getTraceLevel() >= 3) {
HttpMemoryLogger.error(vo.getAdapterGroupName() + vo.getAdapterName(), e.toString(), e);
}
logger.error(
"HttpClient5AdapterServiceBypass] Exception (" + vo.getAdapterGroupName() + ") : " + e.toString(),
e);
throw e;
} finally {
if (status != HttpStatus.SC_OK) {
String[] msgArgs = new String[1];
msgArgs[0] = String.valueOf(status);
String resMsg = ExceptionUtil.make("RDCEAIAHA013", msgArgs);
if (logger.isDebug()) {
logger.debug(resMsg);
}
}
}
}
private boolean hasBody(Object data, HttpUriRequestBase method) {
if (data == null) {
return false;
}
// GET, DELETE는 표준 HTTP에서 body를 지원하지 않음
return method instanceof HttpPost || method instanceof HttpPut;
}
private HttpUriRequestBase generateMethod(String restMethod, String url) {
switch (HttpMethodType.getValue(restMethod)) {
case GET:
return new HttpGet(url);
case DELETE:
return new HttpDelete(url);
case POST:
return new HttpPost(url);
case PUT:
return new HttpPut(url);
default:
return new HttpPost(url);
}
}
private String getRewriteUrl(HttpClientAdapterVO vo, String inboundRewritePath, String queryString,
JSONObject restOptionObject, Map<String, String> inboundPathVariables) {
String fragment = null;
if (StringUtils.isNotBlank(queryString)) {
int fragIdx = queryString.indexOf('#');
if (fragIdx >= 0) {
fragment = queryString.substring(fragIdx + 1);
queryString = queryString.substring(0, fragIdx);
}
}
String restExtraPath = (String) restOptionObject.get("extraPath");
if (StringUtils.isBlank(restExtraPath)) {
StringBuilder uri = new StringBuilder(500);
String baseUrl = StringUtils.removeEnd(vo.getUrl(), "/");
String rewritePath = inboundRewritePath != null && !inboundRewritePath.startsWith("/")
? "/" + inboundRewritePath : inboundRewritePath;
uri.append(baseUrl).append(StringUtils.defaultString(rewritePath));
if (StringUtils.isNotBlank(queryString)) {
uri.append('?');
uri.append(encodeUriQuery(queryString, false));
}
if (doSendUrlFragment && fragment != null) {
uri.append('#');
uri.append(encodeUriQuery(fragment, false));
}
return uri.toString();
} else {
String type = (String) restOptionObject.get("type");
String url = getUrl(vo.getUrl(), restExtraPath);
if (StringUtils.equalsIgnoreCase(type, HttpClientAdapterServiceRest.TYPE_VARIABLE_URL_REQUEST)) {
inboundPathVariables = mergeInboundPathVariables(inboundPathVariables, queryString);
List<String> urlVariableValueList = new ArrayList<>();
JSONArray uriVariables = (JSONArray) restOptionObject.get("uriVariables");
if (uriVariables != null && !uriVariables.isEmpty() && inboundPathVariables != null
&& !inboundPathVariables.isEmpty()) {
for (Object tempObject : uriVariables) {
String urlVaribleId = (String) tempObject;
String uriVariableValue = inboundPathVariables.get(urlVaribleId);
urlVariableValueList.add(uriVariableValue);
}
if (!urlVariableValueList.isEmpty()) {
UriComponents uriComponents = UriComponentsBuilder.fromUriString(url).build();
Object[] urls = urlVariableValueList.toArray();
url = uriComponents.expand(urls).toUriString();
}
}
} else {
if (StringUtils.isNotBlank(queryString)) {
url += "?" + encodeUriQuery(queryString, false);
}
if (doSendUrlFragment && fragment != null) {
url += "#" + encodeUriQuery(fragment, false);
}
}
return url;
}
}
private Map<String, String> mergeInboundPathVariables(Map<String, String> inboundPathVariables,
String queryString) {
if (StringUtils.isBlank(queryString)) {
return inboundPathVariables;
}
if (inboundPathVariables == null) {
inboundPathVariables = new LinkedHashMap<>();
}
String[] pairs = queryString.split("&");
for (String pair : pairs) {
int idx = pair.indexOf("=");
inboundPathVariables.put(pair.substring(0, idx), pair.substring(idx + 1));
}
return inboundPathVariables;
}
private String getUrl(String baseUrl, String extraPath) {
if (StringUtils.isBlank(extraPath)) {
return baseUrl;
}
if (StringUtils.contains(extraPath, "http://") || StringUtils.contains(extraPath, "https://")) {
return extraPath;
}
String targetUrl = baseUrl;
if (!targetUrl.endsWith("/")) {
targetUrl += "/";
}
if (extraPath.startsWith("/")) {
targetUrl += extraPath.substring(1);
} else {
targetUrl += extraPath;
}
return targetUrl;
}
protected CharSequence encodeUriQuery(CharSequence in, boolean encodePercent) {
StringBuilder outBuf = null;
Formatter formatter = null;
for (int i = 0; i < in.length(); i++) {
char c = in.charAt(i);
boolean escape = true;
if (c < 128) {
if (asciiQueryChars.get(c) && !(encodePercent && c == '%')) {
escape = false;
}
} else if (!Character.isISOControl(c) && !Character.isSpaceChar(c)) {
escape = false;
}
if (!escape) {
if (outBuf != null)
outBuf.append(c);
} else {
if (outBuf == null) {
outBuf = new StringBuilder(in.length() + 5 * 3);
outBuf.append(in, 0, i);
formatter = new Formatter(outBuf);
}
formatter.format("%%%02X", (int) c);
}
}
return outBuf != null ? outBuf : in;
}
protected static final BitSet asciiQueryChars;
static {
char[] c_unreserved = "_-!.~'()*".toCharArray();
char[] c_punct = ",;:$&+=".toCharArray();
char[] c_reserved = "/@".toCharArray();
asciiQueryChars = new BitSet(128);
for (char c = 'a'; c <= 'z'; c++)
asciiQueryChars.set(c);
for (char c = 'A'; c <= 'Z'; c++)
asciiQueryChars.set(c);
for (char c = '0'; c <= '9'; c++)
asciiQueryChars.set(c);
for (char c : c_unreserved)
asciiQueryChars.set(c);
for (char c : c_punct)
asciiQueryChars.set(c);
for (char c : c_reserved)
asciiQueryChars.set(c);
asciiQueryChars.set('%');
}
private void assignRelayDataToInbound(Properties prop, Header[] responseHeaders, int status) {
Properties headerProp = new Properties();
if (responseHeaders != null) {
for (Header header : responseHeaders) {
String name = header.getName();
String value = header.getValue();
String existing = headerProp.getProperty(name);
if (existing != null) {
// 동일 이름의 헤더가 여러 개인 경우 콤마로 합침 (RFC 7230)
headerProp.put(name, existing + ", " + value);
} else {
headerProp.put(name, value);
}
}
}
headerProp.put(HttpAdapterServiceBypass.HTTP_STATUS, String.valueOf(status));
Map<String, Object> map = new HashMap<String, Object>();
map.put(HttpAdapterServiceKey.OUTBOUND_RESPONSE_HEADERS, headerProp);
prop.put(HttpAdapterServiceKey.OUTBOUND_PROPERTY_MAP, map);
}
private void setAuthHeaders(HttpUriRequestBase method, OAuth2AccessTokenVO accessToken) throws Exception {
method.setHeader("Authorization",
String.format("%s %s", AccessTokenVO.BEARER_TYPE, accessToken.getAccessToken()));
}
private boolean checkTokenRetry(byte[] responseMessage, String tokenErrorCodeKey, String tokenErrorCodeValues,
String encode) {
if (responseMessage == null) {
return false;
}
if (StringUtils.isBlank(tokenErrorCodeKey)) {
return false;
}
if (StringUtils.isBlank(tokenErrorCodeValues)) {
return false;
}
try {
DocumentContext jsonContext = JsonPath.parse(new String(responseMessage, encode));
String responseCode = jsonContext.read(tokenErrorCodeKey);
if (StringUtils.isBlank(responseCode)) {
return false;
}
String[] arr = org.springframework.util.StringUtils.tokenizeToStringArray(tokenErrorCodeValues, ",");
return ArrayUtils.contains(arr, responseCode);
} catch (Exception e) {
logger.error("HttpClient5AdapterServiceBypass] checkTokenRetry error=" + e.getMessage());
}
return false;
}
private void assignRequestHeaders(HttpUriRequestBase method, Properties headerProp, Properties inProp) {
if (headerProp == null) {
return;
}
for (Entry<Object, Object> e : headerProp.entrySet()) {
String key = (String) e.getKey();
String value = (String) e.getValue();
if (StringUtils.equalsAnyIgnoreCase(key, HttpAdapterServiceBypass.HOP_BY_HOP_HEADERS)
|| StringUtils.equalsIgnoreCase(key, HttpHeaders.CONTENT_LENGTH)) {
continue;
}
if (doHandleCompression && StringUtils.equalsIgnoreCase(key, HttpHeaders.ACCEPT_ENCODING)) {
continue;
}
method.addHeader(key, value);
if (logger.isDebugEnabled()) {
logger.debug("Request Header :" + key + "=[" + value + "]");
}
}
if (doForwardIP) {
String forHeaderName = "X-Forwarded-For";
String forHeader = inProp.getProperty(HttpAdapterServiceKey.INBOUND_REMOTE_ADDR);
if (StringUtils.isNotBlank(forHeader)) {
String existingForHeader = headerProp.getProperty(forHeaderName);
if (existingForHeader != null) {
forHeader = existingForHeader + ", " + forHeader;
}
method.addHeader(forHeaderName, forHeader);
}
String protoHeaderName = "X-Forwarded-Proto";
String protoHeader = inProp.getProperty(HttpAdapterServiceKey.INBOUND_SCHEME);
if (StringUtils.isNotBlank(protoHeader)) {
method.addHeader(protoHeaderName, protoHeader);
}
}
}
@SuppressWarnings("deprecation")
private JSONObject parseJson(String message) throws Exception {
if (message == null) {
return null;
}
return (JSONObject) JSONValue.parse(message);
}
public static final String getIgnoreCaseProp(Properties prop, String key) {
if (prop == null) {
return null;
}
for (Entry<Object, Object> e : prop.entrySet()) {
if (StringUtils.equalsIgnoreCase(key, (String) e.getKey())) {
return (String) e.getValue();
}
}
return null;
}
}
@@ -0,0 +1,888 @@
package com.eactive.eai.custom.adapter.http.client.impl;
import java.io.IOException;
import java.io.StringReader;
import java.math.BigDecimal;
import java.net.ConnectException;
import java.net.SocketTimeoutException;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.StopWatch;
import org.apache.hc.client5.http.classic.HttpClient;
import org.apache.hc.client5.http.classic.methods.HttpDelete;
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.client5.http.classic.methods.HttpPut;
import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.entity.UrlEncodedFormEntity;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.HttpStatus;
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.http.protocol.BasicHttpContext;
import org.apache.hc.core5.http.protocol.HttpContext;
import org.apache.hc.core5.net.URIBuilder;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
import com.eactive.eai.adapter.http.HttpMemoryLogger;
import com.eactive.eai.adapter.http.HttpMethodType;
import com.eactive.eai.adapter.http.client.HttpClient5AdapterServiceSupport;
import com.eactive.eai.adapter.http.client.HttpClientAdapterServiceKey;
import com.eactive.eai.adapter.http.client.HttpClientAdapterVO;
import com.eactive.eai.adapter.http.dynamic.HttpAdapterServiceKey;
import com.eactive.eai.common.TransactionContextKeys;
import com.eactive.eai.common.authoutbound.AccessTokenManagerByDB;
import com.eactive.eai.common.exception.ExceptionUtil;
import com.eactive.eai.common.message.MessageType;
import com.eactive.eai.common.util.CommonLib;
import com.jayway.jsonpath.DocumentContext;
import com.jayway.jsonpath.JsonPath;
import com.openbanking.eai.common.token.AccessTokenManager;
import com.openbanking.eai.common.token.AccessTokenVO;
import com.openbanking.eai.common.token.OAuth2AccessTokenVO;
import com.eactive.eai.common.property.PropManager;
public class HttpClient5AdapterServiceKbank extends HttpClient5AdapterServiceSupport
implements HttpClientAdapterServiceKey {
public static final String TYPE_VARIABLE_URL_REQUEST = "variableUrlRequest";
public static final String TYPE_SIMPLE_REQUEST = "simpleRequest";
public static final String REST_OPTION = "REST_OPTION";
//public static final String HEADER_CONTENT_TYPE = "Content-Type";
public static final String HEADER_GROUP = "HEADER_GROUP";
public static final String HTTP_STATUS = "HTTP_STATUS";
public static final String HEADER_KEYS = "HEADER_KEYS";
public static final String AUTH_TOKEN = "AUTH_TOKEN";
/**
* 1. 기능 : REST API 통신에 사용 <br>
* 2. 처리 개요 : - 속성 정보를 설정 하고 수동 시스템 서비스를 호출 한다. <br>
* 3. 주의사항 <br>
*
* @param prop Http Adapter 속성 정보
* @return 반환 된 Object
* @exception Exception 수동 시스템 간 통신 중 발생
**/
@SuppressWarnings({ "unchecked" })
public Object execute(Properties prop, Object data, Properties tempProp) throws Exception {
HttpClientAdapterVO vo = super.setting(prop, tempProp);
// 2025.04.09 Authorization 헤더값을 프로퍼티(AUTH_TOKEN)에서 지정하여 설정할 수 있도록 수정
String bizCode = "";
String authToken = "";
try {
Properties authProp = PropManager.getInstance().getProperties(AUTH_TOKEN); //Authorization
bizCode = vo.getAdapterGroupName().substring(1, 4);
authToken = authProp.getProperty(bizCode, "");
} catch (Exception e) {
logger.debug("Property Group AUTH_TOKEN is not Exist !!");
}
logger.debug("authToken :::::::::::::::::::::::::::::::::::" + authToken);
String headerGroupName = prop.getProperty(HEADER_GROUP);
String relayResponseHeaderKeys = prop.getProperty(HEADER_KEYS, "");
boolean useAdapterToken = StringUtils.equalsIgnoreCase(prop.getProperty("ADAPTER_TOKEN_USE_YN", "N"), "Y");
boolean useForwardProxy = StringUtils.equalsIgnoreCase(prop.getProperty("FORWARD_PROXY_USE_YN", "N"), "Y");
String forwardProxyUrl = prop.getProperty("FORWARD_PROXY_URL");
// ex) $.dataHeader.GW_RSLT_CD
String tokenErrorCodeKey = prop.getProperty("ADAPTER_TOKEN_ERROR_CODE_KEY");
String tokenErrorCodeValues = prop.getProperty("ADAPTER_TOKEN_ERROR_CODE_VALUES");
String tokenErrorHttpStatusCode = prop.getProperty("ADAPTER_TOKEN_ERROR_HTTP_STATUS_CODE"); // 200 or 400번대
// 레이아웃 메시지 타입 REST URL 추출 시 활용. Default JSON
String messageType = prop.getProperty("MESSAGE_TYPE", MessageType.JSON);
String inboundMethod = tempProp.getProperty(HttpAdapterServiceKey.INBOUND_METHOD, "POST");
/**
* @formatter:off
* TSEAIHE02.RESTOPTION 정보 => JSON 형태로 구성
* - type : simpleRequest, variableUrlRequest
* - extraPath : 어댑터 프로퍼티 URL에 추가될 HTTP REST URL
* - method : get, delete, post, put
* - contentType: application/json(default), application/x-www-form-urlencoded
* - adapterTokenUseYn: Y, N(default)
* ex)
* {"type":"simpleRequest","extraPath":"v2.0/accout/balance","method":"post"}
* @formatter:on
*/
String restOptionData = tempProp.getProperty(REST_OPTION);
JSONObject restOptionObject = parseJson(restOptionData);
if (restOptionObject == null) {
throw new Exception("OptionData parsing result is NULL");
}
String rmethod = (String) restOptionObject.getOrDefault("method", inboundMethod);
String contentType = (String) restOptionObject.getOrDefault("contentType", "application/json");
String adapterTokenUseYn = (String) restOptionObject.get("adapterTokenUseYn");
if (StringUtils.isNotBlank(adapterTokenUseYn)) { // interface 에 설정된게 우선한다.
useAdapterToken = StringUtils.equalsIgnoreCase(adapterTokenUseYn, "Y");
}
HttpClient mclient = this.client;
String sendData = "";
if (data instanceof String) {
sendData = (String) data;
} else if (data instanceof byte[]) {
sendData = new String((byte[]) data, vo.getEncode());
}
////mclient.getParams().setContentCharset(vo.getEncode());
JSONObject dataObject = null;
Object httpHeader = null;
Object dataContent = null;
if (MessageType.JSON.equals(messageType)) {
Object parsed = parseJsonGeneric(sendData);
if(parsed instanceof JSONObject) {
dataObject = parseJson(sendData);
if (dataObject != null && StringUtils.isNotBlank(headerGroupName)) {
httpHeader = dataObject.get(headerGroupName);
dataObject.remove(headerGroupName);
}
dataContent = dataObject;
if (dataObject.containsKey("innerList")) {
JSONArray innerList = (JSONArray) dataObject.get("innerList");
dataContent = innerList;
}
}
}
logger.debug("HttpClientAdapterServiceRest] dataObject1 = [" + dataObject + "]");
logger.debug("HttpClientAdapterServiceRest] dataContent = [" + dataContent + "]");
// interface 에 설정된게 우선한다.
String uri = null;
if (dataObject == null) {
uri = changeUrl(messageType, vo.getUrl(), restOptionData, sendData);
} else {
uri = changeUrl(messageType, vo.getUrl(), restOptionData, dataObject);
}
// ////if (vo.getConnectionTimeout() != 0) {
// mclient.getHttpConnectionManager().getParams().setConnectionTimeout(vo.getConnectionTimeout());
// }
if (logger.isDebug() && "N".equals(vo.getTestCallYn())) {
logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") URL=[" + vo.getUrl() + "] ");
logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") PARAMETER_NAME=[" + vo.getParameterName() + "] ");
logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") ENCODE=[" + vo.getEncode() + "] ");
logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") RESPONSE_TYPE=[" + vo.getResponseType() + "] ");
logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") URL_ENCODE_YN=[" + vo.getUrlEncodeYn() + "] ");
logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") CONNECTION_TIMEOUT=[" + vo.getConnectionTimeout() + "] ");
logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") REST_OPTION=[" + restOptionData + "] ");
}
HttpUriRequestBase method = null;
// 메소드 타입에 따라 HttpRequestBase 인스턴스 생성
switch (HttpMethodType.getValue(rmethod)) {
case GET:
method = new HttpGet(uri);
break;
case DELETE:
method = new HttpDelete(uri);
break;
case POST:
method = new HttpPost(uri);
break;
case PUT:
method = new HttpPut(uri);
break;
default:
method = new HttpGet(uri);
break;
}
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);
}
configureHttpClient(requestConfigBuilder, vo, method);
switch (HttpMethodType.getValue(rmethod)) {
case GET:
case DELETE:
HashMap<String, String> h = getParameters(dataObject);
URIBuilder uriBuilder = new URIBuilder(uri);
for (Map.Entry<String, String> entry : h.entrySet()) {
uriBuilder.addParameter(entry.getKey(), entry.getValue());
}
URI fullUri = uriBuilder.build();
if (method instanceof HttpGet) {
((HttpGet) method).setUri(fullUri);
} else if (method instanceof HttpDelete) {
((HttpDelete) method).setUri(fullUri);
}
if (logger.isDebug()) {
logger.debug("HttpClientAdapterServiceRest] (get Method) QueryString = [" + fullUri.getQuery() + "]");
}
break;
case PUT:
case POST:
//assignPostBody(dataObject, contentType, vo.getEncode(), method);
assignPostBody(dataContent, contentType, vo.getEncode(), method);
break;
default:
break;
}
if (logger.isDebugEnabled()) {
logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") method.getParams()=["
+ method.getEntity() + "] ");
}
OAuth2AccessTokenVO accessToken = null;
if (useAdapterToken) {
AccessTokenManagerByDB tokenManager = AccessTokenManagerByDB.getInstance();
accessToken = (OAuth2AccessTokenVO) tokenManager.getAccessTokenVO(vo.getAdapterGroupName());
// 토큰이 없거나 만료 됐으면 재발급
if (accessToken == null || accessToken.isExpired()) {
String oldToken = accessToken == null ? null : accessToken.getAccessToken();
accessToken = (OAuth2AccessTokenVO) tokenManager.retryAccessTokenVO(vo.getAdapterGroupName(), prop,
oldToken);
}
if (logger.isDebug()) {
logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") TOKEN = ["
+ accessToken + "]");
}
setAuthHeaders(method, accessToken);
if (logger.isDebug()) {
logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName()
+ ") RequestHeader [Authorization=" + method.getHeader("Authorization") + "]");
}
}
// 2025.04.09 Authorization 헤더값을 프로퍼티(AUTH_TOKEN)에서 지정하여 설정할 수 있도록 수정
if(!StringUtils.isBlank(authToken)) {
setAuthHeaders(method, authToken);
}
// 전달 header 셋팅
// Adapter 레벨 header 보다 data로 넘어온 httpHeader를 우선 적용(header명이 같다면 덮어씀)
assignRequestHeaders(method, httpHeader);
int status = -1;
try {
byte[] responseMessage = null;
Header[] responseHeaders;
if (logger.isDebug() && "N".equals(vo.getTestCallYn())) {
if (dataContent != null) { //dataObject
logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") = ["
+ dataContent.toString() + "]"); //dataObject.toJSONString()
logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") = "
+ CommonLib.getDumpMessage(dataContent.toString())); //dataObject.toJSONString()
} else {
logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") = [" + sendData
+ "]");
logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") = "
+ CommonLib.getDumpMessage(sendData));
}
}
StopWatch stopWatch = new StopWatch();
stopWatch.start();
if (vo.getTraceLevel() >= 3) {
HttpMemoryLogger.txlog(vo.getAdapterGroupName() + vo.getAdapterName(),
"SEND [" + sendData + "]" + CommonLib.getDumpMessage(sendData));
}
String uuid = tempProp.getProperty(TransactionContextKeys.TRANSACTION_UUID);
HttpContext context = new BasicHttpContext();
context.setAttribute(TransactionContextKeys.TRANSACTION_UUID, uuid);
context.setAttribute(HttpClientAdapterServiceKey.ADAPTER_GROUP_NAME, vo.getAdapterGroupName());
context.setAttribute(HttpClientAdapterServiceKey.ADAPTER_NAME, vo.getAdapterName());
Integer logProcessNo = (Integer) tempProp.get(HttpClientAdapterServiceKey.LOG_PROCESS_NO);
if (logProcessNo != null && logProcessNo > 0) {
context.setAttribute(HttpClientAdapterServiceKey.LOG_PROCESS_NO, logProcessNo);
}
try {
if (logger.isDebug()) {
logger.debug("[method getName]" + method.getMethod());
logger.debug("[method getEntity]" + method.getEntity());
logger.debug("[method getRequestHeaders]" + method.getHeaders().toString());
logger.debug("[method getURI]" + method.getPath());
}
try (CloseableHttpResponse response = (CloseableHttpResponse) mclient.execute(method, context)) {
status = response.getCode();
if (status >= 200 && status <= 207) {
status = 200;
}
// if (status == 404) {
// status = 200;
// }
responseMessage = EntityUtils.toByteArray(response.getEntity());
responseHeaders = response.getHeaders();
if (logger.isDebug()) {
logger.debug("[received status]" + status);
logger.debug("HttpClientAdapterServiceRest] RECV (" + vo.getAdapterGroupName() + ") = [" + responseMessage + "]");
}
}
} catch (ConnectException e) {
if (logger.isDebug() && "N".equals(vo.getTestCallYn())) {
logger.debug("HttpClientAdapterServiceRest] responseType =" + vo.getResponseType());
}
throw new Exception("excuteMethod java.net.ConnectException = " + e.getMessage());
}
stopWatch.stop();
/**
* @formatter:off
* OAuth 토큰 응답 체크(Adapter properties로 설정)
* 응답코드에 따라 유효한 토큰 확인(토큰 재발급 여부 확인)
* API 서비스 마다 정책이 다름(보통 400대에서 체크하나 200에서도 체크할 수 있음)
*
* TOKEN_ERROR_CODE_KEY: 응답 json error code key
* __ex) $.dataHeader.resultCode
* TOKEN_ERROR_CODE_VALUES: 토큰 재발급이 필요한 응답 error codes(ex: O0001,O0002,O0003)
* TOKEN_ERROR_HTTP_STATUS_CODE: 보통 400대에서 체크하나 API 제공자에 따라 200에서도 체클할수 있음
* ex) TOKEN_ERROR_HTTP_STATUS_CODE = 200
* @formatter:on
*/
boolean needReissue = false;
Properties responseHeaderProp = assignRelayDataToInbound(tempProp, responseHeaders);
String responseString = new String(responseMessage, vo.getEncode());
Map<String, Object> map = new HashMap<String, Object>();
tempProp.put(HttpAdapterServiceKey.OUTBOUND_PROPERTY_MAP, map);
map.put(HttpAdapterServiceKey.OUTBOUND_RESPONSE_HEADERS, responseHeaderProp);
map.put(HttpAdapterServiceKey.OUTBOUND_RESPONSE_MESSAGE, responseString);
map.put(HttpAdapterServiceKey.OUTBOUND_RESPONSE_STATUS, status);
if (status >= 200 && status <= 207) {
if (useAdapterToken && StringUtils.equals(tokenErrorHttpStatusCode, "200")) {
needReissue = checkTokenRetry(responseMessage, tokenErrorCodeKey, tokenErrorCodeValues,
vo.getEncode());
}
} else if (status == 302) {
if (!StringUtils.contains(relayResponseHeaderKeys, "Location")) {
relayResponseHeaderKeys += ",Location";
}
} else {
String errMsg = String.format(
"http receive status fail value=%d adapterName=%s.%s uri=%s method=%s response Data=%s",
status, vo.getAdapterGroupName(), vo.getAdapterName(), uri, rmethod, responseString);
// logger.error(errMsg);
if (status >= 400 && status < 500) {
if (useAdapterToken) {
needReissue = checkTokenRetry(responseMessage, tokenErrorCodeKey, tokenErrorCodeValues,
vo.getEncode());
}
// if (!needReissue) {
// throw new Exception(errMsg);
// }
} else {
throw new Exception(errMsg);
}
}
// ----------------------------------------------------------
if (logger.isDebug() && "N".equals(vo.getTestCallYn())) {
logger.debug("HttpClientAdapterServiceRest] responseType =" + vo.getResponseType());
logger.debug("HttpClientAdapterServiceRest] RECV " + vo.getEncode() + "(" + vo.getAdapterGroupName() + ") = [" + new String(responseMessage, vo.getEncode()) + "]");
// logger.debug("HttpClientAdapterServiceRest] RECV MS949 (" + vo.getAdapterGroupName() + ") = [" + new String(responseMessage, "MS949") + "]");
// logger.debug("HttpClientAdapterServiceRest] RECV euc-kr (" + vo.getAdapterGroupName() + ") = [" + new String(responseMessage, "euc-kr") + "]");
logger.debug("HttpClientAdapterServiceRest] RECV (" + vo.getAdapterGroupName() + ") = " + CommonLib.getDumpMessage(responseMessage));
}
if (useAdapterToken) {
if (responseMessage == null) {
throw new Exception("responseMessage is NULL, HttpStatus=" + status);
}
// OAuth 토큰 재요청 코드 확인
if (needReissue) {
if (logger.isDebug() && "N".equals(vo.getTestCallYn())) {
logger.debug("HttpClientAdapterServiceRest] retry access token response code = ["
+ vo.getResponseType() + "] message = [" + new String(responseMessage, vo.getEncode())
+ "]");
}
// 토큰 재발급
AccessTokenManager tokenManager = AccessTokenManager.getInstance();
String oldToken = accessToken == null ? null : accessToken.getAccessToken();
OAuth2AccessTokenVO newaccessToken = (OAuth2AccessTokenVO) tokenManager
.retryAccessTokenVO(vo.getAdapterGroupName(), prop, oldToken);
method.setHeader("Authorization", newaccessToken.getAuthorization());
setAuthHeaders(method, newaccessToken);
if (logger.isDebug() && "N".equals(vo.getTestCallYn())) {
logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName()
+ ") RETRY TOKEN = [" + newaccessToken + "]");
}
try (CloseableHttpResponse response = (CloseableHttpResponse) mclient.execute(method)) {
status = response.getCode();
responseMessage = EntityUtils.toByteArray(response.getEntity());
if (logger.isDebug()) {
logger.debug("[received status]" + status);
logger.debug("HttpClientAdapterServiceRest] RECV (" + vo.getAdapterGroupName() + ") = [" + new String(responseMessage, vo.getEncode()) + "]");
}
}catch (IOException e){
throw new Exception("retry excuteMethod Exceptioin = " + e.getMessage());
}
if (status == 302) {
if (!StringUtils.contains(relayResponseHeaderKeys, "Location")) {
relayResponseHeaderKeys += ",Location";
}
} else {
String errMsg = String.format(
"http receive status fail value=%d adapterName=%s.%s uri=%s method=%s response Data=%s",
status, vo.getAdapterGroupName(), vo.getAdapterName(), uri, rmethod, responseMessage);
throw new Exception(errMsg);
}
// ----------------------------------------------------------
if (logger.isDebug() && "N".equals(vo.getTestCallYn())) {
logger.debug("HttpClientAdapterServiceRest] RETRY responseType =" + vo.getResponseType());
logger.debug("HttpClientAdapterServiceRest] RETRY RECV " + vo.getEncode() + "("
+ vo.getAdapterGroupName() + ") = [" + new String(responseMessage, vo.getEncode())
+ "]");
logger.debug("HttpClientAdapterServiceRest] RETRY RECV (" + vo.getAdapterGroupName() + ") = "
+ CommonLib.getDumpMessage(responseMessage));
}
// ----------------------------------------------------------
}
}
if (vo.getTraceLevel() >= 3) {
HttpMemoryLogger.txlog(vo.getAdapterGroupName() + vo.getAdapterName(),
"RECV [" + new String(responseMessage, vo.getEncode()) + "]"
+ CommonLib.getDumpMessage(responseMessage));
}
return assignResponseHeaders(method, responseMessage, headerGroupName, vo.getEncode(), messageType,
relayResponseHeaderKeys, status, responseHeaderProp);
} catch (SocketTimeoutException ste) { // Read Timeout :: HttpClient 3.1일 경우
if (vo.getTraceLevel() >= 3) {
HttpMemoryLogger.error(vo.getAdapterGroupName() + vo.getAdapterName(),
"HttpClientAdapterServiceRest] SocketTimeoutException (" + vo.getAdapterGroupName() + ") : "
+ ste.toString(),
ste);
}
logger.error("HttpClientAdapterServiceRest] SocketTimeoutException (" + vo.getAdapterGroupName() + ") : "
+ ste.toString(), ste);
throw ste;
} catch (ConnectException ce) {
if (vo.getTraceLevel() >= 3) {
HttpMemoryLogger.error(vo.getAdapterGroupName() + vo.getAdapterName(),
"HttpClientAdapterServiceRest] Connection Exception (" + vo.getAdapterGroupName() + ") : "
+ ce.toString(),
ce);
}
logger.error("HttpClientAdapterServiceRest] Connection Exception (" + vo.getAdapterGroupName() + ") : "
+ ce.toString(), ce);
throw ce;
} catch (Exception e) {
if (vo.getTraceLevel() >= 3) {
HttpMemoryLogger.error(vo.getAdapterGroupName() + vo.getAdapterName(), e.toString(), e);
}
logger.error("HttpClientAdapterServiceRest] Exception (" + vo.getAdapterGroupName() + ") : " + e.toString(),
e);
throw e;
} finally {
if (status != HttpStatus.SC_OK) {
String[] msgArgs = new String[1];
msgArgs[0] = String.valueOf(status);
String resMsg = ExceptionUtil.make("RDCEAIAHA013", msgArgs);
if (logger.isDebug()) {
logger.debug(resMsg);
}
}
}
}
private Properties assignRelayDataToInbound(Properties prop, Header[] responseHeaders) {
Properties headerProp = new Properties();
if (responseHeaders == null || responseHeaders.length == 0) {
return headerProp;
}
for (Header header : responseHeaders) {
headerProp.put(header.getName(), header.getValue());
}
return headerProp;
}
/**
* 현재 1단계 레이아웃에만 적용되도록 구현됨.
*
* @param method
* @param responseMessage
* @param headerGroupName
* @param encode
* @param messageType
* @param relayHeaderKeys
* @return
* @throws Exception
*/
@SuppressWarnings("unchecked")
private String assignResponseHeaders(HttpUriRequestBase method, byte[] responseMessage, String headerGroupName,
String encode, String messageType, String relayHeaderKeys, int status, Properties responseHeaderProp) throws Exception {
if (!MessageType.JSON.equals(messageType) || StringUtils.isBlank(headerGroupName)
|| StringUtils.isBlank(relayHeaderKeys)) {
return new String(responseMessage, encode);
}
String[] relayKeyArr = org.springframework.util.StringUtils.tokenizeToStringArray(relayHeaderKeys, ",");
JSONObject headerJson = new JSONObject();
for (String key : relayKeyArr) {
String value = responseHeaderProp.getProperty(key);
if (StringUtils.isNotBlank(value)) {
headerJson.put(key, value);
}
}
headerJson.put(HTTP_STATUS, String.valueOf(status));
if (headerJson.size() <= 0) {
return new String(responseMessage, encode);
}
JSONObject message = null;
if (responseMessage == null || responseMessage.length == 0) {
message = new JSONObject();
} else {
message = parseJson(new String(responseMessage, encode));
if (message == null) {
message = new JSONObject();
message.put("Malformed_Response_Message", new String(responseMessage, encode));
}
}
if (StringUtils.isNotBlank(headerGroupName) && message != null)
message.put(headerGroupName, headerJson);
logger.info("------------- message --------------- : {} ", message);
return message.toJSONString();
}
// 2025.04.09 Authorization 헤더값을 프로퍼티(AUTH_TOKEN)에서 지정하여 설정할 수 있도록 수정
private void setAuthHeaders(HttpUriRequestBase method, String authorization) throws Exception {
method.setHeader("Authorization",
String.format("%s", authorization));
}
private void setAuthHeaders(HttpUriRequestBase method, OAuth2AccessTokenVO accessToken) throws Exception {
method.setHeader("Authorization",
String.format("%s %s", AccessTokenVO.BEARER_TYPE, accessToken.getAccessToken()));
}
private boolean checkTokenRetry(byte[] responseMessage, String tokenErrorCodeKey, String tokenErrorCodeValues,
String encode) {
if (responseMessage == null) {
return false;
}
if (StringUtils.isBlank(tokenErrorCodeKey)) {
return false;
}
if (StringUtils.isBlank(tokenErrorCodeValues)) {
return false;
}
try {
DocumentContext jsonContext = JsonPath.parse(new String(responseMessage, encode));
String responseCode = jsonContext.read(tokenErrorCodeKey);
if (StringUtils.isBlank(responseCode)) {
return false;
}
String[] arr = org.springframework.util.StringUtils.tokenizeToStringArray(tokenErrorCodeValues, ",");
return ArrayUtils.contains(arr, responseCode);
} catch (Exception e) {
logger.error("HttpClientAdapterServiceRest] checkTokenRetry error=" + e.getMessage());
}
return false;
}
private void assignRequestHeaders(HttpUriRequestBase method, Object httpHeader) {
if (httpHeader == null) {
return;
}
if (httpHeader instanceof JSONObject) {
JSONObject jsonObject = (JSONObject) httpHeader;
for (Object key : jsonObject.keySet()) {
Object obj = jsonObject.get(key);
if ((obj instanceof JSONObject) || (obj instanceof JSONArray)) {
continue;
}
method.setHeader((String) key, (String) obj);
if (logger.isDebugEnabled()) {
logger.debug("Request Header :" + (String) key + "=[" + (String) obj + "]");
}
}
}
}
@SuppressWarnings("deprecation")
private void assignPostBody(Object eaiBody, String contentType, String charset, HttpUriRequestBase method) {
if (eaiBody == null) {
return;
}
if (StringUtils.contains(contentType, "application/x-www-form-urlencoded")) {
if (eaiBody instanceof JSONObject) {
List<NameValuePair> params = new ArrayList<>();
JSONObject jsonObject = (JSONObject) eaiBody;
for (Object key : jsonObject.keySet()) {
params.add(new BasicNameValuePair((String) key, (String) jsonObject.get(key)));
}
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(params, StandardCharsets.UTF_8);
method.setEntity(entity);
}
} else {
ContentType contentTypeObj = ContentType.create(contentType, charset);
// 요청 본문을 StringEntity 객체로 생성
StringEntity entity = new StringEntity(eaiBody.toString(), contentTypeObj);
// 요청에 본문 추가
method.setEntity(entity);
}
}
private HashMap<String, String> getParameters(Object message) throws Exception {
HashMap<String, String> result = new HashMap<String, String>();
if (message != null) {
if (message instanceof JSONObject) {
JSONObject jsonObject = (JSONObject) message;
for (Object key : jsonObject.keySet()) {
Object obj = jsonObject.get(key);
if ((obj instanceof JSONObject) || (obj instanceof JSONArray)) {
continue;
}
result.put((String) key, getStringValue(obj));
}
} else {
String[] messages = ((String) message).split("&");
String[] data = null;
for (int i = 0; i < messages.length; i++) {
data = messages[i].split("=", 2);
if (data.length == 2) {
result.put(data[0], data[1]);
}
}
}
}
return result;
}
public String getStringValue(Object obj) {
if (obj == null) {
return null;
}
if (obj instanceof String) {
return (String) obj;
} else if (obj instanceof Long) {
return Long.toString((Long) obj);
} else if (obj instanceof BigDecimal) {
return ((BigDecimal) obj).toPlainString();
} else {
return (String) obj;
}
}
public static Object parseJsonGeneric(String jsonData) {
if (StringUtils.isBlank(jsonData)) {
return null;
}
try {
JSONParser parser = new JSONParser();
Object parsedObj = parser.parse(jsonData);
return parsedObj;
} catch (ParseException e) {
e.printStackTrace();
return null;
}
}
@SuppressWarnings("deprecation")
private JSONObject parseJson(String message) throws Exception {
if (message == null) {
return null;
}
return (JSONObject) JSONValue.parse(message);
}
private Document convertXmlDocument(String message) throws Exception {
SAXReader builder = new SAXReader();
Document document = builder.read(new StringReader(message));
return document;
}
@SuppressWarnings("deprecation")
private String changeUrl(String messageType, String url, String restOption, Object sendData) {
if ((MessageType.JSON.equals(messageType) || MessageType.XML.equals(messageType))) {
try {
if (StringUtils.isBlank(restOption)) {
return url;
}
List<String> urlVariableList = new ArrayList<String>();
JSONObject restOptionObject = parseJson(restOption);
if (restOptionObject == null)
throw new Exception("restOption is NULL");
String type = (String) restOptionObject.get("type");
String requestExtraPath = (String) restOptionObject.get("extraPath");
url = getUrl(url, requestExtraPath);
if (TYPE_VARIABLE_URL_REQUEST.equals(type)) {
Pattern p = Pattern.compile("\\{(.*?)\\}");
Matcher m = p.matcher(requestExtraPath);
List<String> uriVariables = new ArrayList<>();
while(m.find()) {
String fieldName = m.group(1);
uriVariables.add(fieldName);
}
if (MessageType.JSON.equals(messageType)) {
JSONObject jsonObject;
if (sendData instanceof JSONObject) {
jsonObject = (JSONObject) sendData;
} else {
jsonObject = (JSONObject) JSONValue.parse((String) sendData);
}
for (Object tempObject : uriVariables) {
String urlVaribleId = (String) tempObject;
String uriVariable = (String) jsonObject.get(urlVaribleId);
urlVariableList.add(uriVariable);
jsonObject.remove(urlVaribleId);
}
} else if (MessageType.XML.equals(messageType)) {
Document doc;
if (sendData instanceof Document) {
doc = (Document) sendData;
} else {
doc = convertXmlDocument((String) sendData);
}
for (Object tempObject : uriVariables) {
String urlVaribleId = (String) tempObject;
Element element = (Element) doc.selectSingleNode("//" + urlVaribleId);
urlVariableList.add(element.getText());
if (doc.getRootElement() == element) {
doc = null;
} else {
element.getParent().remove(element);
}
}
}
if (urlVariableList.size() > 0) {
UriComponents uriComponents = UriComponentsBuilder.fromUriString(url).build();
Object[] urls = urlVariableList.toArray();
url = uriComponents.expand(urls).toUriString();
logger.debug("HttpClientAdapterServiceRest] after extand url=[" + url + "] ");
}
}
} catch (Exception e) {
logger.error("Failed to change url", e);
}
}
return url;
}
private String getUrl(String baseUrl, String extraPath) {
if (StringUtils.isBlank(extraPath)) {
return baseUrl;
}
if (StringUtils.contains(extraPath, "http://") || StringUtils.contains(extraPath, "https://")) {
return extraPath;
}
String targetUrl = baseUrl;
if (!targetUrl.endsWith("/")) {
targetUrl += "/";
}
if (extraPath.startsWith("/")) {
targetUrl += extraPath.substring(1);
} else {
targetUrl += extraPath;
}
logger.debug("HttpClientAdapterServiceRest] after concatenate url=[" + targetUrl + "] ");
return targetUrl;
}
}
@@ -0,0 +1,867 @@
package com.eactive.eai.custom.adapter.http.client.impl;
import java.io.IOException;
import java.io.StringReader;
import java.math.BigDecimal;
import java.net.ConnectException;
import java.net.SocketTimeoutException;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.StopWatch;
import org.apache.hc.client5.http.classic.HttpClient;
import org.apache.hc.client5.http.classic.methods.HttpDelete;
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.client5.http.classic.methods.HttpPut;
import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.entity.UrlEncodedFormEntity;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.Header;
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.http.protocol.BasicHttpContext;
import org.apache.hc.core5.http.protocol.HttpContext;
import org.apache.hc.core5.net.URIBuilder;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
import com.eactive.eai.adapter.AdapterGroupVO;
import com.eactive.eai.adapter.AdapterManager;
import com.eactive.eai.adapter.http.HttpMemoryLogger;
import com.eactive.eai.adapter.http.HttpMethodType;
import com.eactive.eai.adapter.http.client.HttpClient5AdapterServiceSupport;
import com.eactive.eai.adapter.http.client.HttpClientAdapterServiceKey;
import com.eactive.eai.adapter.http.client.HttpClientAdapterVO;
import com.eactive.eai.adapter.http.dynamic.HttpAdapterServiceKey;
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.authoutbound.AccessTokenManagerByDB;
import com.eactive.eai.common.dao.DAOException;
import com.eactive.eai.common.exception.ExceptionUtil;
import com.eactive.eai.common.message.MessageType;
import com.eactive.eai.common.util.CommonLib;
import com.eactive.eai.custom.adapter.http.util.VirtualAccountTransform;
import com.jayway.jsonpath.DocumentContext;
import com.jayway.jsonpath.JsonPath;
import com.openbanking.eai.common.token.AccessTokenManager;
import com.openbanking.eai.common.token.AccessTokenVO;
import com.openbanking.eai.common.token.OAuth2AccessTokenVO;
// Kbank 가상계좌 OUTBOUND
public class HttpClient5AdapterServiceVirtualAccount extends HttpClient5AdapterServiceSupport
implements HttpClientAdapterServiceKey {
public static final String TYPE_VARIABLE_URL_REQUEST = "variableUrlRequest";
public static final String TYPE_SIMPLE_REQUEST = "simpleRequest";
public static final String REST_OPTION = "REST_OPTION";
////public static final String HEADER_CONTENT_TYPE = "Content-Type";
public static final String HEADER_GROUP = "HEADER_GROUP";
public static final String HTTP_STATUS = "HTTP_STATUS";
public static final String HEADER_KEYS = "HEADER_KEYS";
private String getSecurityKey(String adapterGroupName) throws DAOException {
String clientId = null;
String aesKey = null;
AdapterGroupVO adapterGroup = null;;
adapterGroup = AdapterManager.getInstance().getAdapterGroup(adapterGroupName);
clientId = adapterGroup.getClientId();
OAuth2Manager manager = OAuth2Manager.getInstance();
try {
ClientVO client = manager.getClientInfo(clientId);
aesKey = client.getSecurityKey();
return aesKey;
} catch (DAOException e) {
e.printStackTrace();
return null;
}
}
public Object execute(Properties prop, Object data, Properties tempProp) throws Exception {
HttpClientAdapterVO vo = super.setting(prop, tempProp);
String headerGroupName = prop.getProperty(HEADER_GROUP);
String relayResponseHeaderKeys = prop.getProperty(HEADER_KEYS, "");
boolean useAdapterToken = StringUtils.equalsIgnoreCase(prop.getProperty("ADAPTER_TOKEN_USE_YN", "N"), "Y");
boolean useForwardProxy = StringUtils.equalsIgnoreCase(prop.getProperty("FORWARD_PROXY_USE_YN", "N"), "Y");
String forwardProxyUrl = prop.getProperty("FORWARD_PROXY_URL");
// ex) $.dataHeader.GW_RSLT_CD
String tokenErrorCodeKey = prop.getProperty("ADAPTER_TOKEN_ERROR_CODE_KEY");
String tokenErrorCodeValues = prop.getProperty("ADAPTER_TOKEN_ERROR_CODE_VALUES");
String tokenErrorHttpStatusCode = prop.getProperty("ADAPTER_TOKEN_ERROR_HTTP_STATUS_CODE"); // 200 or 400번대
// 레이아웃 메시지 타입 REST URL 추출 시 활용. Default JSON
String messageType = prop.getProperty("MESSAGE_TYPE", MessageType.JSON);
String inboundMethod = tempProp.getProperty(HttpAdapterServiceKey.INBOUND_METHOD, "POST");
/**
* @formatter:off
* TSEAIHE02.RESTOPTION 정보 => JSON 형태로 구성
* - type : simpleRequest, variableUrlRequest
* - extraPath : 어댑터 프로퍼티 URL에 추가될 HTTP REST URL
* - method : get, delete, post, put
* - contentType: application/json(default), application/x-www-form-urlencoded
* - adapterTokenUseYn: Y, N(default)
* ex)
* {"type":"simpleRequest","extraPath":"v2.0/accout/balance","method":"post"}
* @formatter:on
*/
String restOptionData = tempProp.getProperty(REST_OPTION);
JSONObject restOptionObject = parseJson(restOptionData);
if (restOptionObject == null) {
throw new Exception("OptionData parsing result is NULL");
}
String rmethod = (String) restOptionObject.getOrDefault("method", inboundMethod);
String contentType = (String) restOptionObject.getOrDefault("contentType", "application/json");
String adapterTokenUseYn = (String) restOptionObject.get("adapterTokenUseYn");
if (StringUtils.isNotBlank(adapterTokenUseYn)) { // interface 에 설정된게 우선한다.
useAdapterToken = StringUtils.equalsIgnoreCase(adapterTokenUseYn, "Y");
}
HttpClient mclient = this.client;
String sendData = "";
if (data instanceof String) {
sendData = (String) data;
} else if (data instanceof byte[]) {
sendData = new String((byte[]) data, vo.getEncode());
}
// 필드암복호화 처리 (dec CubeOne -> enc AES)
String encPaths = prop.getProperty("ENC_PATHS");
List<String> encFieldList = null;
String aesKey = getSecurityKey(vo.getAdapterGroupName());
if (logger.isDebug()) logger.debug("HttpClientAdapterServiceRest] ENC AdapterGroupName[" + vo.getAdapterGroupName()+ "], aesKey["+aesKey+"]");
if(StringUtils.isNotBlank(aesKey) && StringUtils.isNotBlank(encPaths)) {
String[] items = encPaths.split(",");
encFieldList = Arrays.asList(items);
sendData = VirtualAccountTransform.toAES(aesKey, encFieldList, sendData);
if (logger.isDebug()) logger.debug("HttpClientAdapterServiceRest] after toAES = [" + sendData + "]");
}
////mclient.getParams().setContentCharset(vo.getEncode());
JSONObject dataObject = null;
Object httpHeader = null;
if (MessageType.JSON.equals(messageType)) {
dataObject = parseJson(sendData);
if (dataObject != null && StringUtils.isNotBlank(headerGroupName)) {
httpHeader = dataObject.get(headerGroupName);
dataObject.remove(headerGroupName);
}
if (logger.isDebug()) logger.debug("HttpClientAdapterServiceRest] dataObject1 = [" + dataObject + "]");
}
else {
if (logger.isDebug()) logger.debug("HttpClientAdapterServiceRest] sendData = [" + sendData + "]");
}
// interface 에 설정된게 우선한다.
String uri = null;
if (dataObject == null) {
uri = changeUrl(messageType, vo.getUrl(), restOptionData, sendData);
} else {
uri = changeUrl(messageType, vo.getUrl(), restOptionData, dataObject);
}
if (logger.isDebug()) logger.debug("HttpClientAdapterServiceRest] dataObject2 = [" + dataObject + "]");
//////if (vo.getConnectionTimeout() != 0) {
// mclient.getHttpConnectionManager().getParams().setConnectionTimeout(vo.getConnectionTimeout());
//}
if (logger.isDebug() && "N".equals(vo.getTestCallYn())) {
logger.debug(
"HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") URL=[" + vo.getUrl() + "] ");
logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") PARAMETER_NAME=["
+ vo.getParameterName() + "] ");
logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") ENCODE=["
+ vo.getEncode() + "] ");
logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") RESPONSE_TYPE=["
+ vo.getResponseType() + "] ");
logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") URL_ENCODE_YN=["
+ vo.getUrlEncodeYn() + "] ");
logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") CONNECTION_TIMEOUT=["
+ vo.getConnectionTimeout() + "] ");
logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") REST_OPTION=["
+ restOptionData + "] ");
}
HttpUriRequestBase method = null;
// 메소드 타입에 따라 HttpRequestBase 인스턴스 생성
switch (HttpMethodType.getValue(rmethod)) {
case GET:
method = new HttpGet(uri);
break;
case DELETE:
method = new HttpDelete(uri);
break;
case POST:
method = new HttpPost(uri);
break;
case PUT:
method = new HttpPut(uri);
break;
default:
method = new HttpGet(uri);
break;
}
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);
}
configureHttpClient(requestConfigBuilder, vo, method);
switch (HttpMethodType.getValue(rmethod)) {
case GET:
case DELETE:
HashMap<String, String> h = getParameters(dataObject);
URIBuilder uriBuilder = new URIBuilder(uri);
for (Map.Entry<String, String> entry : h.entrySet()) {
uriBuilder.addParameter(entry.getKey(), entry.getValue());
}
URI fullUri = uriBuilder.build();
if (method instanceof HttpGet) {
((HttpGet) method).setUri(fullUri);
} else if (method instanceof HttpDelete) {
((HttpDelete) method).setUri(fullUri);
}
if (logger.isDebug()) {
logger.debug("HttpClientAdapterServiceRest] (get Method) QueryString = [" + fullUri.getQuery() + "]");
}
break;
case PUT:
case POST:
assignPostBody(dataObject, contentType, vo.getEncode(), method);
break;
default:
break;
}
if (logger.isDebugEnabled()) {
logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") method.getParams()=["
+ method.getEntity() + "] ");
}
OAuth2AccessTokenVO accessToken = null;
if (useAdapterToken) {
AccessTokenManagerByDB tokenManager = AccessTokenManagerByDB.getInstance();
accessToken = (OAuth2AccessTokenVO) tokenManager.getAccessTokenVO(vo.getAdapterGroupName());
// 토큰이 없거나 만료 됐으면 재발급
if (accessToken == null || accessToken.isExpired()) {
String oldToken = accessToken == null ? null : accessToken.getAccessToken();
accessToken = (OAuth2AccessTokenVO) tokenManager.retryAccessTokenVO(vo.getAdapterGroupName(), prop,
oldToken);
}
if (logger.isDebug()) {
logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") TOKEN = ["
+ accessToken + "]");
}
setAuthHeaders(method, accessToken);
if (logger.isDebug()) {
logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName()
+ ") RequestHeader [Authorization=" + method.getHeader("Authorization") + "]");
}
}
// 전달 header 셋팅
// Adapter 레벨 header 보다 data로 넘어온 httpHeader를 우선 적용(header명이 같다면 덮어씀)
assignRequestHeaders(method, httpHeader);
int status = -1;
try {
byte[] responseMessage = null;
Header[] responseHeaders;
if (logger.isDebug() && "N".equals(vo.getTestCallYn())) {
if (dataObject != null) {
logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") = ["
+ dataObject.toJSONString() + "]");
logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") = "
+ CommonLib.getDumpMessage(dataObject.toJSONString()));
} else {
logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") = [" + sendData
+ "]");
logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName() + ") = "
+ CommonLib.getDumpMessage(sendData));
}
}
StopWatch stopWatch = new StopWatch();
stopWatch.start();
if (vo.getTraceLevel() >= 3) {
HttpMemoryLogger.txlog(vo.getAdapterGroupName() + vo.getAdapterName(),
"SEND [" + sendData + "]" + CommonLib.getDumpMessage(sendData));
}
String uuid = tempProp.getProperty(TransactionContextKeys.TRANSACTION_UUID);
HttpContext context = new BasicHttpContext();
context.setAttribute(TransactionContextKeys.TRANSACTION_UUID, uuid);
context.setAttribute(HttpClientAdapterServiceKey.ADAPTER_GROUP_NAME, vo.getAdapterGroupName());
context.setAttribute(HttpClientAdapterServiceKey.ADAPTER_NAME, vo.getAdapterName());
try {
if (logger.isDebug()) {
logger.debug("[method getName]" + method.getMethod());
logger.debug("[method getEntity]" + method.getEntity());
logger.debug("[method getRequestHeaders]" + method.getHeaders().toString());
logger.debug("[method getURI]" + method.getPath());
}
try (CloseableHttpResponse response = (CloseableHttpResponse) mclient.execute(method, context)) {
status = response.getCode();
responseMessage = EntityUtils.toByteArray(response.getEntity());
responseHeaders = response.getHeaders();
if (logger.isDebug()) {
logger.debug("[received status]" + status);
logger.debug("HttpClientAdapterServiceRest] RECV (" + vo.getAdapterGroupName() + ") = ["
+ responseMessage + "]");
}
}
} catch (ConnectException e) {
if (logger.isDebug() && "N".equals(vo.getTestCallYn())) {
logger.debug("HttpClientAdapterServiceRest] responseType =" + vo.getResponseType());
}
throw new Exception("excuteMethod java.net.ConnectException = " + e.getMessage());
}
stopWatch.stop();
/**
* @formatter:off
* OAuth 토큰 응답 체크(Adapter properties로 설정)
* 응답코드에 따라 유효한 토큰 확인(토큰 재발급 여부 확인)
* API 서비스 마다 정책이 다름(보통 400대에서 체크하나 200에서도 체크할 수 있음)
*
* TOKEN_ERROR_CODE_KEY: 응답 json error code key
* __ex) $.dataHeader.resultCode
* TOKEN_ERROR_CODE_VALUES: 토큰 재발급이 필요한 응답 error codes(ex: O0001,O0002,O0003)
* TOKEN_ERROR_HTTP_STATUS_CODE: 보통 400대에서 체크하나 API 제공자에 따라 200에서도 체클할수 있음
* ex) TOKEN_ERROR_HTTP_STATUS_CODE = 200
* @formatter:on
*/
boolean needReissue = false;
Properties responseHeaderProp = assignRelayDataToInbound(tempProp, responseHeaders);
String responseString = new String(responseMessage, vo.getEncode());
Map<String, Object> map = new HashMap<String, Object>();
tempProp.put(HttpAdapterServiceKey.OUTBOUND_PROPERTY_MAP, map);
map.put(HttpAdapterServiceKey.OUTBOUND_RESPONSE_HEADERS, responseHeaderProp);
map.put(HttpAdapterServiceKey.OUTBOUND_RESPONSE_MESSAGE, responseString);
map.put(HttpAdapterServiceKey.OUTBOUND_RESPONSE_STATUS, status);
if (status == 200) {
if (useAdapterToken && StringUtils.equals(tokenErrorHttpStatusCode, "200")) {
needReissue = checkTokenRetry(responseMessage, tokenErrorCodeKey, tokenErrorCodeValues,
vo.getEncode());
}
} else if (status == 302) {
if (!StringUtils.contains(relayResponseHeaderKeys, "Location")) {
relayResponseHeaderKeys += ",Location";
}
} else {
String errMsg = String.format(
"http receive status fail value=%d adapterName=%s.%s uri=%s method=%s response Data=%s",
status, vo.getAdapterGroupName(), vo.getAdapterName(), uri, rmethod, responseString);
// logger.error(errMsg);
if (status >= 400 && status < 500) {
if (useAdapterToken) {
needReissue = checkTokenRetry(responseMessage, tokenErrorCodeKey, tokenErrorCodeValues,
vo.getEncode());
}
if (!needReissue) {
throw new Exception(errMsg);
}
} else {
throw new Exception(errMsg);
}
}
// ----------------------------------------------------------
if (logger.isDebug() && "N".equals(vo.getTestCallYn())) {
logger.debug("HttpClientAdapterServiceRest] responseType =" + vo.getResponseType());
logger.debug("HttpClientAdapterServiceRest] RECV " + vo.getEncode() + "(" + vo.getAdapterGroupName()
+ ") = [" + new String(responseMessage, vo.getEncode()) + "]");
// logger.debug("HttpClientAdapterServiceRest] RECV MS949 (" + vo.getAdapterGroupName() + ") = [" + new String(responseMessage, "MS949") + "]");
// logger.debug("HttpClientAdapterServiceRest] RECV euc-kr (" + vo.getAdapterGroupName() + ") = [" + new String(responseMessage, "euc-kr") + "]");
logger.debug("HttpClientAdapterServiceRest] RECV (" + vo.getAdapterGroupName() + ") = "
+ CommonLib.getDumpMessage(responseMessage));
}
if (useAdapterToken) {
if (responseMessage == null) {
throw new Exception("responseMessage is NULL, HttpStatus=" + status);
}
// OAuth 토큰 재요청 코드 확인
if (needReissue) {
if (logger.isDebug() && "N".equals(vo.getTestCallYn())) {
logger.debug("HttpClientAdapterServiceRest] retry access token response code = ["
+ vo.getResponseType() + "] message = [" + new String(responseMessage, vo.getEncode())
+ "]");
}
// 토큰 재발급
AccessTokenManager tokenManager = AccessTokenManager.getInstance();
String oldToken = accessToken == null ? null : accessToken.getAccessToken();
OAuth2AccessTokenVO newaccessToken = (OAuth2AccessTokenVO) tokenManager
.retryAccessTokenVO(vo.getAdapterGroupName(), prop, oldToken);
method.setHeader("Authorization", newaccessToken.getAuthorization());
setAuthHeaders(method, newaccessToken);
if (logger.isDebug() && "N".equals(vo.getTestCallYn())) {
logger.debug("HttpClientAdapterServiceRest] SEND (" + vo.getAdapterGroupName()
+ ") RETRY TOKEN = [" + newaccessToken + "]");
}
try (CloseableHttpResponse response = (CloseableHttpResponse) mclient.execute(method)) {
status = response.getCode();
responseMessage = EntityUtils.toByteArray(response.getEntity());
if (logger.isDebug()) {
logger.debug("[received status]" + status);
logger.debug("HttpClientAdapterServiceRest] RECV (" + vo.getAdapterGroupName() + ") = ["
+ new String(responseMessage, vo.getEncode()) + "]");
}
} catch (IOException e) {
throw new Exception("retry excuteMethod Exceptioin = " + e.getMessage());
}
if (status == 302) {
if (!StringUtils.contains(relayResponseHeaderKeys, "Location")) {
relayResponseHeaderKeys += ",Location";
}
} else {
String errMsg = String.format(
"http receive status fail value=%d adapterName=%s.%s uri=%s method=%s response Data=%s",
status, vo.getAdapterGroupName(), vo.getAdapterName(), uri, rmethod, responseMessage);
throw new Exception(errMsg);
}
// ----------------------------------------------------------
if (logger.isDebug() && "N".equals(vo.getTestCallYn())) {
logger.debug("HttpClientAdapterServiceRest] RETRY responseType =" + vo.getResponseType());
logger.debug("HttpClientAdapterServiceRest] RETRY RECV " + vo.getEncode() + "("
+ vo.getAdapterGroupName() + ") = [" + new String(responseMessage, vo.getEncode())
+ "]");
logger.debug("HttpClientAdapterServiceRest] RETRY RECV (" + vo.getAdapterGroupName() + ") = "
+ CommonLib.getDumpMessage(responseMessage));
}
// ----------------------------------------------------------
}
}
if (vo.getTraceLevel() >= 3) {
HttpMemoryLogger.txlog(vo.getAdapterGroupName() + vo.getAdapterName(),
"RECV [" + new String(responseMessage, vo.getEncode()) + "]"
+ CommonLib.getDumpMessage(responseMessage));
}
// TODO : toCubeOne
if(StringUtils.isNotBlank(aesKey) && encFieldList != null) {
responseMessage = VirtualAccountTransform.toCubeOne(aesKey, encFieldList, responseMessage);
if (vo.getTraceLevel() >= 3) {
HttpMemoryLogger.txlog(vo.getAdapterGroupName() + vo.getAdapterName(),
"RECV ENC[" + new String(responseMessage, vo.getEncode()) + "]"
+ CommonLib.getDumpMessage(responseMessage));
}
}
return assignResponseHeaders(method, responseMessage, headerGroupName, vo.getEncode(), messageType,
relayResponseHeaderKeys, status, responseHeaderProp);
} catch (SocketTimeoutException ste) { // Read Timeout :: HttpClient 3.1일 경우
if (vo.getTraceLevel() >= 3) {
HttpMemoryLogger.error(vo.getAdapterGroupName() + vo.getAdapterName(),
"HttpClientAdapterServiceRest] SocketTimeoutException (" + vo.getAdapterGroupName() + ") : "
+ ste.toString(),
ste);
}
logger.error("HttpClientAdapterServiceRest] SocketTimeoutException (" + vo.getAdapterGroupName() + ") : "
+ ste.toString(), ste);
throw ste;
} catch (ConnectException ce) {
if (vo.getTraceLevel() >= 3) {
HttpMemoryLogger.error(vo.getAdapterGroupName() + vo.getAdapterName(),
"HttpClientAdapterServiceRest] Connection Exception (" + vo.getAdapterGroupName() + ") : "
+ ce.toString(),
ce);
}
logger.error("HttpClientAdapterServiceRest] Connection Exception (" + vo.getAdapterGroupName() + ") : "
+ ce.toString(), ce);
throw ce;
} catch (Exception e) {
if (vo.getTraceLevel() >= 3) {
HttpMemoryLogger.error(vo.getAdapterGroupName() + vo.getAdapterName(), e.toString(), e);
}
logger.error("HttpClientAdapterServiceRest] Exception (" + vo.getAdapterGroupName() + ") : " + e.toString(),
e);
throw e;
} finally {
if (status != HttpStatus.SC_OK) {
String[] msgArgs = new String[1];
msgArgs[0] = String.valueOf(status);
String resMsg = ExceptionUtil.make("RDCEAIAHA013", msgArgs);
if (logger.isDebug()) {
logger.debug(resMsg);
}
}
}
}
private Properties assignRelayDataToInbound(Properties prop, Header[] responseHeaders) {
Properties headerProp = new Properties();
if (responseHeaders == null || responseHeaders.length == 0) {
return headerProp;
}
for (Header header : responseHeaders) {
headerProp.put(header.getName(), header.getValue());
}
return headerProp;
}
/**
* 현재 1단계 레이아웃에만 적용되도록 구현됨.
*
* @param method
* @param responseMessage
* @param headerGroupName
* @param encode
* @param messageType
* @param relayHeaderKeys
* @return
* @throws Exception
*/
@SuppressWarnings("unchecked")
private String assignResponseHeaders(HttpUriRequestBase method, byte[] responseMessage, String headerGroupName,
String encode, String messageType, String relayHeaderKeys, int status, Properties responseHeaderProp)
throws Exception {
if (!MessageType.JSON.equals(messageType) || StringUtils.isBlank(headerGroupName)
|| StringUtils.isBlank(relayHeaderKeys)) {
return new String(responseMessage, encode);
}
String[] relayKeyArr = org.springframework.util.StringUtils.tokenizeToStringArray(relayHeaderKeys, ",");
JSONObject headerJson = new JSONObject();
for (String key : relayKeyArr) {
String value = responseHeaderProp.getProperty(key);
if (StringUtils.isNotBlank(value)) {
headerJson.put(key, value);
}
}
headerJson.put(HTTP_STATUS, String.valueOf(status));
if (headerJson.size() <= 0) {
return new String(responseMessage, encode);
}
JSONObject message = null;
if (responseMessage == null || responseMessage.length == 0) {
message = new JSONObject();
} else {
message = parseJson(new String(responseMessage, encode));
if (message == null) {
message = new JSONObject();
message.put("Malformed_Response_Message", new String(responseMessage, encode));
}
}
if (StringUtils.isNotBlank(headerGroupName) && message != null)
message.put(headerGroupName, headerJson);
return message.toJSONString();
}
private void setAuthHeaders(HttpUriRequestBase method, OAuth2AccessTokenVO accessToken) throws Exception {
method.setHeader("Authorization",
String.format("%s %s", AccessTokenVO.BEARER_TYPE, accessToken.getAccessToken()));
}
private boolean checkTokenRetry(byte[] responseMessage, String tokenErrorCodeKey, String tokenErrorCodeValues,
String encode) {
if (responseMessage == null) {
return false;
}
if (StringUtils.isBlank(tokenErrorCodeKey)) {
return false;
}
if (StringUtils.isBlank(tokenErrorCodeValues)) {
return false;
}
try {
DocumentContext jsonContext = JsonPath.parse(new String(responseMessage, encode));
String responseCode = jsonContext.read(tokenErrorCodeKey);
if (StringUtils.isBlank(responseCode)) {
return false;
}
String[] arr = org.springframework.util.StringUtils.tokenizeToStringArray(tokenErrorCodeValues, ",");
return ArrayUtils.contains(arr, responseCode);
} catch (Exception e) {
logger.error("HttpClientAdapterServiceRest] checkTokenRetry error=" + e.getMessage());
}
return false;
}
private void assignRequestHeaders(HttpUriRequestBase method, Object httpHeader) {
if (httpHeader == null) {
return;
}
if (httpHeader instanceof JSONObject) {
JSONObject jsonObject = (JSONObject) httpHeader;
for (Object key : jsonObject.keySet()) {
Object obj = jsonObject.get(key);
if ((obj instanceof JSONObject) || (obj instanceof JSONArray)) {
continue;
}
method.setHeader((String) key, (String) obj);
if (logger.isDebugEnabled()) {
logger.debug("Request Header :" + (String) key + "=[" + (String) obj + "]");
}
}
}
}
@SuppressWarnings("deprecation")
private void assignPostBody(Object eaiBody, String contentType, String charset, HttpUriRequestBase method) {
if (eaiBody == null) {
return;
}
if (StringUtils.contains(contentType, "application/x-www-form-urlencoded")) {
if (eaiBody instanceof JSONObject) {
List<NameValuePair> params = new ArrayList<>();
JSONObject jsonObject = (JSONObject) eaiBody;
for (Object key : jsonObject.keySet()) {
params.add(new BasicNameValuePair((String) key, (String) jsonObject.get(key)));
}
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(params, StandardCharsets.UTF_8);
method.setEntity(entity);
}
} else {
ContentType contentTypeObj = ContentType.create(contentType, charset);
// 요청 본문을 StringEntity 객체로 생성
StringEntity entity = new StringEntity(eaiBody.toString(), contentTypeObj);
// 요청에 본문 추가
method.setEntity(entity);
}
}
private HashMap<String, String> getParameters(Object message) throws Exception {
HashMap<String, String> result = new HashMap<String, String>();
if (message != null) {
if (message instanceof JSONObject) {
JSONObject jsonObject = (JSONObject) message;
for (Object key : jsonObject.keySet()) {
Object obj = jsonObject.get(key);
if ((obj instanceof JSONObject) || (obj instanceof JSONArray)) {
continue;
}
result.put((String) key, getStringValue(obj));
}
} else {
String[] messages = ((String) message).split("&");
String[] data = null;
for (int i = 0; i < messages.length; i++) {
data = messages[i].split("=", 2);
if (data.length == 2) {
result.put(data[0], data[1]);
}
}
}
}
return result;
}
public String getStringValue(Object obj) {
if (obj == null) {
return null;
}
if (obj instanceof String) {
return (String) obj;
} else if (obj instanceof Long) {
return Long.toString((Long) obj);
} else if (obj instanceof BigDecimal) {
return ((BigDecimal) obj).toPlainString();
} else {
return (String) obj;
}
}
@SuppressWarnings("deprecation")
private JSONObject parseJson(String message) throws Exception {
if (message == null) {
return null;
}
return (JSONObject) JSONValue.parse(message);
}
private Document convertXmlDocument(String message) throws Exception {
SAXReader builder = new SAXReader();
Document document = builder.read(new StringReader(message));
return document;
}
@SuppressWarnings("deprecation")
private String changeUrl(String messageType, String url, String restOption, Object sendData) {
if ((MessageType.JSON.equals(messageType) || MessageType.XML.equals(messageType))) {
try {
if (StringUtils.isBlank(restOption)) {
return url;
}
List<String> urlVariableList = new ArrayList<String>();
JSONObject restOptionObject = parseJson(restOption);
if (restOptionObject == null)
throw new Exception("restOption is NULL");
String type = (String) restOptionObject.get("type");
String requestExtraPath = (String) restOptionObject.get("extraPath");
url = getUrl(url, requestExtraPath);
if (TYPE_VARIABLE_URL_REQUEST.equals(type)) {
Pattern p = Pattern.compile("\\{(.*?)\\}");
Matcher m = p.matcher(requestExtraPath);
List<String> uriVariables = new ArrayList<>();
while (m.find()) {
String fieldName = m.group(1);
uriVariables.add(fieldName);
}
if (MessageType.JSON.equals(messageType)) {
JSONObject jsonObject;
if (sendData instanceof JSONObject) {
jsonObject = (JSONObject) sendData;
} else {
jsonObject = (JSONObject) JSONValue.parse((String) sendData);
}
for (Object tempObject : uriVariables) {
String urlVaribleId = (String) tempObject;
String uriVariable = (String) jsonObject.get(urlVaribleId);
urlVariableList.add(uriVariable);
jsonObject.remove(urlVaribleId);
}
} else if (MessageType.XML.equals(messageType)) {
Document doc;
if (sendData instanceof Document) {
doc = (Document) sendData;
} else {
doc = convertXmlDocument((String) sendData);
}
for (Object tempObject : uriVariables) {
String urlVaribleId = (String) tempObject;
Element element = (Element) doc.selectSingleNode("//" + urlVaribleId);
urlVariableList.add(element.getText());
if (doc.getRootElement() == element) {
doc = null;
} else {
element.getParent().remove(element);
}
}
}
if (urlVariableList.size() > 0) {
UriComponents uriComponents = UriComponentsBuilder.fromUriString(url).build();
Object[] urls = urlVariableList.toArray();
url = uriComponents.expand(urls).toUriString();
logger.debug("HttpClientAdapterServiceRest] after extand url=[" + url + "] ");
}
}
} catch (Exception e) {
logger.error("Failed to change url", e);
}
}
return url;
}
private String getUrl(String baseUrl, String extraPath) {
if (StringUtils.isBlank(extraPath)) {
return baseUrl;
}
if (StringUtils.contains(extraPath, "http://") || StringUtils.contains(extraPath, "https://")) {
return extraPath;
}
String targetUrl = baseUrl;
if (!targetUrl.endsWith("/")) {
targetUrl += "/";
}
if (extraPath.startsWith("/")) {
targetUrl += extraPath.substring(1);
} else {
targetUrl += extraPath;
}
logger.debug("HttpClientAdapterServiceRest] after concatenate url=[" + targetUrl + "] ");
return targetUrl;
}
}
@@ -1,31 +0,0 @@
package com.eactive.eai.custom.adapter.http.client.impl.filter;
import java.util.Properties;
import com.eactive.eai.adapter.http.client.impl.filter.HttpClientAdapterFilter;
import com.eactive.eai.adapter.http.client.impl.filter.OutCryptoFilter;
/**
* 제주은행 Open API 기본 암복호화 필더
*/
public class EncFieldOutCryptoFilter implements HttpClientAdapterFilter {
private final OutCryptoFilter filter = new OutCryptoFilter();
protected Properties setProp(Properties p) {
p.setProperty(OutCryptoFilter.PROP_DEC_FROM_PATH, "/encryptedData");
p.setProperty(OutCryptoFilter.PROP_ENC_TO_PATH, "/encryptedData");
return p;
}
@Override
public Object doPreFilter(String adptGrpName, String adptName, Properties prop, Object message, Properties tempProp)
throws Exception {
return filter.doPreFilter(adptGrpName, adptName, prop, message, setProp(tempProp));
}
@Override
public Object doPostFilter(String adptGrpName, String adptName, Properties prop, Object message,
Properties tempProp) throws Exception {
return filter.doPostFilter(adptGrpName, adptName, prop, message, setProp(tempProp));
}
}
@@ -1,18 +0,0 @@
package com.eactive.eai.custom.adapter.http.client.impl.filter;
import java.util.Properties;
import com.eactive.eai.adapter.http.client.impl.filter.OutCryptoFilter;
/**
* 카카오뱅크 암복호화에 맟추어 커스텀
*/
public class KakaobankEncFieldOutCryptoFilter extends EncFieldOutCryptoFilter {
protected Properties setProp(Properties p) {
p.setProperty(OutCryptoFilter.PROP_DEC_FROM_PATH, "/encrypted_data");
p.setProperty(OutCryptoFilter.PROP_ENC_TO_PATH, "/encrypted_data");
return p;
}
}
@@ -1,17 +0,0 @@
package com.eactive.eai.custom.adapter.http.client.impl.filter;
import java.util.Properties;
import com.eactive.eai.adapter.http.client.impl.filter.OutCryptoFilter;
/**
* 토스 암복호화에 맟추어 커스텀
*/
public class TossEncFieldOutCryptoFilter extends EncFieldOutCryptoFilter {
protected Properties setProp(Properties p) {
p.setProperty(OutCryptoFilter.PROP_DEC_FROM_PATH, "/encrypted_data");
p.setProperty(OutCryptoFilter.PROP_ENC_TO_PATH, "/encrypted_data");
return p;
}
}
@@ -1,17 +0,0 @@
package com.eactive.eai.custom.adapter.http.client.impl.filter;
import java.util.Properties;
import com.eactive.eai.adapter.http.client.impl.filter.OutCryptoFilter;
/**
* 토스뱅크 암복호화에 맟추어 커스텀
*/
public class TossbankEncFieldOutCryptoFilter extends EncFieldOutCryptoFilter {
protected Properties setProp(Properties p) {
p.setProperty(OutCryptoFilter.PROP_DEC_FROM_PATH, "/encryptedData");
p.setProperty(OutCryptoFilter.PROP_ENC_TO_PATH, "/encryptedData");
return p;
}
}
@@ -1,75 +0,0 @@
package com.eactive.eai.custom.adapter.http.dynamic.filter;
import java.util.Properties;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.http.HttpStatus;
import com.eactive.eai.adapter.http.dynamic.HttpAdapterServiceKey;
import com.eactive.eai.adapter.http.dynamic.filter.FilterCryptoException;
import com.eactive.eai.adapter.http.dynamic.filter.FilterException;
import com.eactive.eai.adapter.http.dynamic.filter.HttpAdapterFilter;
import com.eactive.eai.adapter.http.dynamic.filter.InCryptoFilter;
import com.eactive.eai.common.security.keyderiv.KeyDerivationStrategy;
/**
* 더존 연동용 수신 암복호화 필터.
*
* {@code HttpAdapterServiceKey.INBOUND_HEADER} 프로퍼티로 전달된 수신 헤더 Map에서
* 두 가지 컨텍스트 값을 추출하여 {@link InCryptoFilter}에 위임한다.
*
* x-emp-nm : GCM AAD 값 ({@code CRYPTO_AAD_HEADER} 프로퍼티에 설정)
* x-chnl-nm : DYNAMIC 키 도출 컨텍스트 ({@code contextKey} 프로퍼티에 설정)
* → {@code HsmContextSha256KeyDerivationStrategy}가 채널명 기반으로 키를 동적 생성
*
* 암복호화 필드 경로: {@code /encryptedData}
*
* 필터 타입 등록 (FQCN):
* com.eactive.eai.custom.adapter.http.dynamic.filter.DouzoneEncFieldInCryptoFilter
*/
public class DouzoneEncFieldInCryptoFilter implements HttpAdapterFilter {
public static final String ERROR_DEC_FAIL = "E.DECRYPT_FAIL";
public static final String ERROR_ENC_FAIL = "E.ENCRYPT_FAIL";
private final InCryptoFilter filter = new InCryptoFilter();
protected Properties setProp(Properties p) {
p.setProperty(InCryptoFilter.PROP_DEC_FROM_PATH, "/encryptedData");
p.setProperty(InCryptoFilter.PROP_ENC_TO_PATH, "/encryptedData");
return p;
}
private void setContext(Properties tempProp) {
Properties inboundHeaders = (Properties) tempProp.get(HttpAdapterServiceKey.INBOUND_HEADER);
String empNm = inboundHeaders.getProperty("x-emp-nm");
tempProp.setProperty(InCryptoFilter.PROP_AAD_HEADER, empNm);
String chnlNm = inboundHeaders.getProperty("x-chnl-nm");
tempProp.setProperty(KeyDerivationStrategy.PARAM_CONTEXT_KEY, chnlNm);
}
@Override
public Object doPreFilter(String adptGrpName, String adptName, Object message, Properties prop,
HttpServletRequest request, HttpServletResponse response) throws Exception {
try {
setContext(prop);
return filter.doPreFilter(adptGrpName, adptName, message, setProp(prop), request, response);
} catch (Exception e) {
throw new FilterCryptoException("복호화 오류", ERROR_DEC_FAIL, HttpStatus.INTERNAL_SERVER_ERROR.value(), e);
}
}
@Override
public Object doPostFilter(String adptGrpName, String adptName, Object resultMessage, Properties prop,
HttpServletRequest request, HttpServletResponse response) throws Exception {
try {
setContext(prop);
return filter.doPostFilter(adptGrpName, adptName, resultMessage, setProp(prop), request, response);
} catch (Exception e) {
throw new FilterCryptoException("암호화 오류", ERROR_ENC_FAIL, HttpStatus.INTERNAL_SERVER_ERROR.value(), e);
}
}
}
@@ -1,40 +0,0 @@
package com.eactive.eai.custom.adapter.http.dynamic.filter;
import java.util.Properties;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.eactive.eai.adapter.http.dynamic.filter.InCryptoFilter;
import com.eactive.eai.adapter.http.dynamic.filter.HttpAdapterFilter;
/**
* InCryptoFilter를 멤버변수로 관리하는 HttpAdapterFilter 구현체.
*
* 멤버변수에 세팅된 값을 Properties로 변환하여 {@link InCryptoFilter}에 위임한다.
* 값이 blank인 멤버변수는 Properties에 포함하지 않으며, InCryptoFilter의 기본값이 적용된다.
*/
public class EncFieldInCryptoFilter implements HttpAdapterFilter {
private final InCryptoFilter filter = new InCryptoFilter();
@Override
public Object doPreFilter(String adptGrpName, String adptName, Object message, Properties prop,
HttpServletRequest request, HttpServletResponse response) throws Exception {
return filter.doPreFilter(adptGrpName, adptName, message, setProp(prop), request, response);
}
@Override
public Object doPostFilter(String adptGrpName, String adptName, Object resultMessage, Properties prop,
HttpServletRequest request, HttpServletResponse response) throws Exception {
return filter.doPostFilter(adptGrpName, adptName, resultMessage, setProp(prop), request, response);
}
protected Properties setProp(Properties p) {
p.setProperty(InCryptoFilter.PROP_DEC_FROM_PATH, "/encryptedData");
p.setProperty(InCryptoFilter.PROP_ENC_TO_PATH, "/encryptedData");
return p;
}
}
@@ -0,0 +1,71 @@
package com.eactive.eai.custom.adapter.http.dynamic.filter;
import java.io.UnsupportedEncodingException;
import java.util.Properties;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import org.apache.commons.lang3.StringUtils;
import com.eactive.eai.adapter.AdapterManager;
import com.eactive.eai.adapter.http.dynamic.filter.HttpAdapterFilter;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
public class JsonToSetStatusFilter implements HttpAdapterFilter {
private ObjectMapper mapper = new ObjectMapper();
@Override
public Object doPreFilter(String adptGrpName, String adptName, Object message, Properties prop,
HttpServletRequest request, HttpServletResponse response) throws Exception {
JsonNode rootNode = parseJson(adptGrpName, message);
int httpStatus = 200;
if (rootNode.has("apiRsltCd") && !rootNode.get("apiRsltCd").asText().isEmpty()) {
try {
String statusParam = rootNode.get("apiRsltCd").asText();
httpStatus = Integer.parseInt(statusParam);
} catch (NumberFormatException e) {
httpStatus = 400; // 잘못된 입력일 경우 400 Bad Request 반환
}
// HTTP 상태 코드 설정
response.setStatus(httpStatus);
}
return message;
}
@Override
public Object doPostFilter(String adptGrpName, String adptName, Object resultMessage, Properties prop,
HttpServletRequest request, HttpServletResponse response) throws Exception {
JsonNode rootNode = parseJson(adptGrpName, resultMessage);
int httpStatus = 200;
if (rootNode.has("apiRsltCd") && !rootNode.get("apiRsltCd").asText().isEmpty()) {
try {
String statusParam = rootNode.get("apiRsltCd").asText();
httpStatus = Integer.parseInt(statusParam);
} catch (NumberFormatException e) {
httpStatus = 400; // 잘못된 입력일 경우 400 Bad Request 반환
}
// HTTP 상태 코드 설정
response.setStatus(httpStatus);
}
return resultMessage;
}
public JsonNode parseJson(String adptGrpName, Object message) throws UnsupportedEncodingException, JsonMappingException, JsonProcessingException {
String charset = StringUtils.defaultIfBlank(AdapterManager.getInstance().getAdapterGroupVO(adptGrpName).getMessageEncode(), "UTF-8");
String orgMessageString;
if(message instanceof byte[]) {
orgMessageString = new String((byte[])message, charset);
} else {
orgMessageString = (String) message;
}
return mapper.readTree(orgMessageString);
}
}
@@ -0,0 +1,110 @@
package com.eactive.eai.custom.adapter.http.dynamic.filter;
import java.io.UnsupportedEncodingException;
import java.util.Properties;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import org.apache.commons.lang3.StringUtils;
import com.eactive.eai.adapter.AdapterManager;
import com.eactive.eai.adapter.http.dynamic.HttpAdapterServiceKey;
import com.eactive.eai.adapter.http.dynamic.filter.HttpAdapterFilter;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
public class JsonToStdConverterFilter implements HttpAdapterFilter {
private ObjectMapper mapper = new ObjectMapper();
@Override
public Object doPreFilter(String adptGrpName, String adptName, Object message, Properties prop,
HttpServletRequest request, HttpServletResponse response) throws Exception {
JsonNode rootNode = parseJson(adptGrpName, message);
if (rootNode.has("header_part")) {
JsonNode headerPart = rootNode.path("header_part");
if (!headerPart.has("eaiIntfId") || headerPart.get("eaiIntfId").asText().isEmpty()) {
// /api/v1/public/getUserInfo.svc
String inboundUri = prop.getProperty(HttpAdapterServiceKey.INBOUND_EXTURI);
String apiPath = prop.getProperty(HttpAdapterServiceKey.API_PATH) + "/";
// getUserInfo.svc
String eaiIntfId = org.apache.commons.lang.StringUtils.removeStart(inboundUri, apiPath);
// header_part 아래 eaiIntfId가 추가 또는 수정
if (headerPart instanceof ObjectNode) {
((ObjectNode) headerPart).put("eaiIntfId", eaiIntfId);
// 요청 응답 구분코드 강제 설정 -> API명 찾아갈때 필요
if (!headerPart.has("reqRspnsDscd")) {
((ObjectNode) headerPart).put("reqRspnsDscd", "S");
}
// GUID 강제 설정
String orgnlTx = "";
if (headerPart.has("orgnlTx") && !headerPart.get("orgnlTx").asText().isEmpty()) {
orgnlTx = headerPart.get("orgnlTx").asText();
}
if (!headerPart.has("tlgrWrtnDt")) {
((ObjectNode) headerPart).put("tlgrWrtnDt", orgnlTx);
}
if (!headerPart.has("tlgrCrtnSysNm")) {
((ObjectNode) headerPart).put("tlgrCrtnSysNm", "");
}
if (!headerPart.has("tlgrSrlNo")) {
((ObjectNode) headerPart).put("tlgrSrlNo", "");
}
}
}
}
else {
return message;
}
return rootNode.toString();
}
@Override
public Object doPostFilter(String adptGrpName, String adptName, Object resultMessage, Properties prop,
HttpServletRequest request, HttpServletResponse response) throws Exception {
JsonNode rootNode = parseJson(adptGrpName, resultMessage);
if (rootNode.has("header_part")) {
JsonNode headerPart = rootNode.path("header_part");
if (!headerPart.has("eaiIntfId") || headerPart.get("eaiIntfId").asText().isEmpty()) {
// /api/v1/public/getUserInfo.svc
String inboundUri = prop.getProperty(HttpAdapterServiceKey.INBOUND_EXTURI);
String apiPath = prop.getProperty(HttpAdapterServiceKey.API_PATH) + "/";
// getUserInfo.svc
String eaiIntfId = org.apache.commons.lang.StringUtils.removeStart(inboundUri, apiPath);
// header_part 아래 eaiIntfId가 추가 또는 수정
if (headerPart instanceof ObjectNode) {
((ObjectNode) headerPart).put("eaiIntfId", eaiIntfId);
// 요청 응답 구분코드 강제 설정 -> API명 찾아갈때 필요
((ObjectNode) headerPart).put("reqRspnsDscd", "S");
}
}
}
else {
return resultMessage;
}
return rootNode.toString();
}
public JsonNode parseJson(String adptGrpName, Object message) throws UnsupportedEncodingException, JsonMappingException, JsonProcessingException {
String charset = StringUtils.defaultIfBlank(AdapterManager.getInstance().getAdapterGroupVO(adptGrpName).getMessageEncode(), "UTF-8");
String orgMessageString;
if(message instanceof byte[]) {
orgMessageString = new String((byte[])message, charset);
} else {
orgMessageString = (String) message;
}
return mapper.readTree(orgMessageString);
}
}
@@ -1,18 +0,0 @@
package com.eactive.eai.custom.adapter.http.dynamic.filter;
import java.util.Properties;
import com.eactive.eai.adapter.http.dynamic.filter.InCryptoFilter;
/**
* 카카오뱅크 암복호화에 맟추어 커스텀 필요
*/
public class KakaobankEncFieldInCryptoFilter extends EncFieldInCryptoFilter {
protected Properties setProp(Properties p) {
p.setProperty(InCryptoFilter.PROP_DEC_FROM_PATH, "/encrypted_data");
p.setProperty(InCryptoFilter.PROP_ENC_TO_PATH, "/encrypted_data");
return p;
}
}
@@ -0,0 +1,68 @@
package com.eactive.eai.custom.adapter.http.dynamic.filter;
import java.io.UnsupportedEncodingException;
import java.util.Iterator;
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.AdapterManager;
import com.eactive.eai.adapter.http.dynamic.filter.HttpAdapterFilter;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
public class KbankEaiJsonParseFilter implements HttpAdapterFilter {
private ObjectMapper mapper = new ObjectMapper();
@Override
public Object doPreFilter(String adptGrpName, String adptName, Object message, Properties prop,
HttpServletRequest request, HttpServletResponse response) throws Exception {
JsonNode rootNode = parseJson(adptGrpName, message);
ObjectNode replacedJson = mapper.createObjectNode();
for(Iterator<String> it = rootNode.fieldNames(); it.hasNext();) {
String fieldName = it.next();
String value = rootNode.get(fieldName).asText();
JsonNode jsonNode = mapper.readTree(value);
replacedJson.set(fieldName, jsonNode);
}
return replacedJson.toString();
}
@Override
public Object doPostFilter(String adptGrpName, String adptName, Object resultMessage, Properties prop,
HttpServletRequest request, HttpServletResponse response) throws Exception {
JsonNode rootNode = parseJson(adptGrpName, resultMessage);
ObjectNode replacedJson = mapper.createObjectNode();
for(Iterator<String> it = rootNode.fieldNames(); it.hasNext();) {
String fieldName = it.next();
JsonNode childNode = rootNode.get(fieldName);
String childString = mapper.writeValueAsString(childNode);
replacedJson.put(fieldName, childString);
}
return replacedJson.toString();
}
public JsonNode parseJson(String adptGrpName, Object message) throws UnsupportedEncodingException, JsonMappingException, JsonProcessingException {
String charset = StringUtils.defaultIfBlank(AdapterManager.getInstance().getAdapterGroupVO(adptGrpName).getMessageEncode(), "UTF-8");
String orgMessageString;
if(message instanceof byte[]) {
orgMessageString = new String((byte[])message, charset);
} else {
orgMessageString = (String) message;
}
return mapper.readTree(orgMessageString);
}
}
@@ -0,0 +1,215 @@
package com.eactive.eai.custom.adapter.http.dynamic.filter;
import java.io.UnsupportedEncodingException;
import java.util.Properties;
import java.util.Date;
import java.util.Enumeration;
import java.util.Iterator;
import java.text.SimpleDateFormat;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.Mac;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.eactive.eai.adapter.AdapterManager;
import com.eactive.eai.common.property.PropManager;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonNode;
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.filter.HttpAdapterFilter;
import com.eactive.eai.adapter.http.dynamic.filter.JwtAuthException;
import com.eactive.eai.common.server.Keys;
import com.eactive.eai.common.util.Logger;
import com.fasterxml.jackson.databind.ObjectMapper;
public class KbankHmacSha256VerifyFilter implements HttpAdapterFilter {
static Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);
private static final String SERVICE_CODE_UNKNOWN = "00";
private static final int TIMESTAMP_EXPIRATION_SECONDS = 60*10*1000;
private static final String GROUP_NAME = "HMAC_INFO";
private ObjectMapper mapper = new ObjectMapper();
@Override
public Object doPreFilter(String adptGrpName, String adptName, Object message, Properties prop,
HttpServletRequest request, HttpServletResponse response) throws Exception {
try {
String hmacSignature = getHeaderCaseInsensitive(request, "hmac_signature");
String hmacTimestamp = getHeaderCaseInsensitive(request, "hmac_timestamp");
if(StringUtils.isBlank(hmacSignature) || StringUtils.isBlank(hmacTimestamp)) {
throw new JwtAuthException(String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_UNKNOWN, "00"), "Can not find hmac info");
}
long inputTime = new SimpleDateFormat("yyyyMMddHHmmss").parse(hmacTimestamp).getTime();
long currentTime = new Date().getTime();
String timeStamp = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
logger.debug("### inputTime :"+inputTime);
logger.debug("### currentTime :"+currentTime);
logger.debug("### timeStamp :"+timeStamp);
String systemMode = System.getProperty(Keys.EAI_SYSTEMMODE);
if(currentTime - inputTime > TIMESTAMP_EXPIRATION_SECONDS) {
logger.debug("### systemMode:" + systemMode);
logger.debug("### currentTime - inputTime:" + (currentTime - inputTime));
if("P".equals(systemMode)) {
throw new JwtAuthException(
String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_UNKNOWN,"00"), "Signature verifying failed");
}
}
String sMessage = (String)message;
sMessage = sMessage.replace(" ","");
sMessage = sMessage.replace("\n","");
sMessage = sMessage.replace("\r","");
sMessage = sMessage + hmacTimestamp;
logger.info("### sMessage:"+sMessage);
String alCoId = "AL2021012901001";
JsonNode rootNode = parseJson(adptGrpName, message);
for(Iterator<String> it = rootNode.fieldNames(); it.hasNext();){
String fieldName = it.next();
if(fieldName.equals("ALCO_ID")) alCoId = rootNode.get(fieldName).asText();
}
logger.info("### alCoId:"+alCoId);
Properties hmacProp = PropManager.getInstance().getProperties(GROUP_NAME);
String hmacKey = hmacProp.getProperty(alCoId+"-hmack-key","");
String hmacKek = hmacProp.getProperty(alCoId+"-hmack-kek","");
String hmacIvForKey = hmacProp.getProperty(alCoId+"-hmack-iv-for-key","");
logger.debug("### hmacKey:"+hmacKey);
logger.debug("### hmacIvForKey:"+hmacIvForKey);
logger.debug("### hmacKek:"+hmacKek);
//kbank.payment.hmac-key
int hmacKeyLen = hmacKey.length();
byte[] hmacKeyData = new byte[hmacKeyLen/2];
for(int i=0;i<hmacKeyLen;i+=2){
hmacKeyData[i/2] = (byte)((Character.digit(hmacKey.charAt(i), 16) << 4) + Character.digit(hmacKey.charAt(i+1), 16));
}
//kbank.payment.hmac-kek
int hmacKekLen = hmacKek.length();
byte[] hmacKekData = new byte[hmacKekLen/2];
for(int i=0;i<hmacKekLen;i+=2){
hmacKekData[i/2] = (byte)((Character.digit(hmacKek.charAt(i),16) << 4) + Character.digit(hmacKek.charAt(i+1), 16));
}
//kbank.payment.hmac-iv-for-key
int hmacIvForKeyLen = hmacIvForKey.length();
byte[] hmacIvForKeyData = new byte[hmacIvForKeyLen/2];
for(int i=0;i<hmacIvForKeyLen;i+=2){
hmacIvForKeyData[i/2] = (byte)((Character.digit(hmacIvForKey.charAt(i),16) << 4) + Character.digit(hmacIvForKey.charAt(i+1), 16));
}
byte[] decrypedHmacKey = null;
try{
Cipher cp = Cipher.getInstance("AES/CBC/PKCS5Padding");
cp.init(Cipher.DECRYPT_MODE, new SecretKeySpec(hmacIvForKeyData,"AES"), new IvParameterSpec(hmacIvForKeyData));
decrypedHmacKey = cp.doFinal(hmacKeyData);
} catch (Exception e) {
logger.debug(e.getMessage());
throw new JwtAuthException(
String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_UNKNOWN, "00"), "Signature verifying failed");
}
SecretKeySpec secretKey = new SecretKeySpec(decrypedHmacKey, "HmacSHA256");
String madeSignature = "";
logger.info("### secretKey:"+secretKey);
try{
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(secretKey);
madeSignature= Base64.encodeBase64String(mac.doFinal(sMessage.getBytes()));
} catch (Exception e) {
logger.debug(e.getMessage());
throw new JwtAuthException(String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_UNKNOWN, "00"), "Signature verifying failed");
}
logger.info("### madeSignature:"+madeSignature);
if(!StringUtils.equals(hmacSignature, madeSignature)){
logger.info("### hmacSignature:"+hmacSignature);
logger.info("### madeSignature:"+madeSignature);
throw new JwtAuthException(String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_UNKNOWN, "00"), "Signature verifying failed" );
}
} catch (JwtAuthException e) {
logger.debug(e.getMessage());
throw e;
} catch (Exception e) {
logger.debug(e.getMessage());
throw new JwtAuthException(String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_UNKNOWN, "00"), "Signature verifying failed(Unknown)" );
}
logger.info("### pass: "+"success");
return message;
}
/**
* 대소문자 구분 없이 HTTP 헤더 값을 가져오는 메소드
* @param request HTTP 요청 객체
* @param headerName 찾고자 하는 헤더 이름
* @return 헤더 값 또는 null
*/
public static String getHeaderCaseInsensitive(HttpServletRequest request, String headerName) {
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String header = headerNames.nextElement();
if (header != null && header.equalsIgnoreCase(headerName)) {
return request.getHeader(header);
}
}
return null;
}
public JsonNode parseJson(String adptGrpName, Object message) throws UnsupportedEncodingException, JsonMappingException,JsonProcessingException {
String charset = StringUtils.defaultIfBlank(AdapterManager.getInstance().getAdapterGroupVO(adptGrpName).getMessageEncode(), "UTF-8");
String orgMessageString;
if(message instanceof byte[]) {
orgMessageString = new String((byte[])message, charset);
} else {
orgMessageString = (String) message;
}
return mapper.readTree(orgMessageString);
}
@Override
public Object doPostFilter(String adptGrpName, String adptName, Object resultMessage, Properties prop,
HttpServletRequest request, HttpServletResponse response) throws Exception {
return resultMessage;
}
}
@@ -0,0 +1,205 @@
package com.eactive.eai.custom.adapter.http.dynamic.filter;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Properties;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.codec.digest.HmacAlgorithms;
import org.apache.commons.codec.digest.HmacUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpStatus;
import com.eactive.eai.adapter.http.dynamic.HttpAdapterServiceSupport;
import com.eactive.eai.adapter.http.dynamic.filter.HttpAdapterFilter;
import com.eactive.eai.adapter.http.dynamic.filter.JwtAuthException;
import com.eactive.eai.authserver.service.OAuth2Manager;
import com.eactive.eai.authserver.vo.ClientVO;
import com.eactive.eai.common.util.Logger;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
public class SnapHmacSha512VerifyFilter implements HttpAdapterFilter {
static Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);
private static final String SERVICE_CODE_UNKNOWN = "00";
private static final int X_TIMESTAMP_EXPIRATION_SECONDS = 60 * 5;
@Override
public Object doPreFilter(String adptGrpName, String adptName, Object message, Properties prop,
HttpServletRequest request, HttpServletResponse response) throws Exception {
// @formatter:off
/*
* HMAC_SHA512 (clientSecret, stringToSign)
*
* stringToSign = HTTPMethod:EndpointUrl:AccessToken
* :Lowercase(HexEncode(SHA-256(minify(RequestBody)))):TimeStamp
*/
try {
String xSignature = request.getHeader("X-SIGNATURE");
if (StringUtils.isBlank(xSignature)) {
throw new JwtAuthException(String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_UNKNOWN, "00"), "Can not find X-SIGNATURE");
}
String clientSecret = assignClientSecret(prop, request);
StringBuilder sb = new StringBuilder();
sb.append(request.getMethod())
.append(":")
.append(getEndpointUrl(request))
.append(":")
.append(SnapSimpleOauth2Filter.extractToken(request))
.append(":")
.append(getRequestBody((String)message))
.append(":")
.append(getTimeStamp(request));
// @formatter:on
if (logger.isDebug()) {
logger.debug(sb.toString());
}
String hmac = new HmacUtils(HmacAlgorithms.HMAC_SHA_512, clientSecret).hmacHex(sb.toString());
if (!StringUtils.equals(xSignature, hmac)) {
throw new JwtAuthException(
String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_UNKNOWN, "00"),
"Signature verifying failed");
}
} catch (JwtAuthException e) {
logger.debug(e.getMessage());
throw e;
} catch (Exception e) {
logger.error(e.getMessage());
throw new JwtAuthException(
String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_UNKNOWN, "00"),
"Signature verifying failed(unkown)");
}
return message;
}
private String assignClientSecret(Properties prop, HttpServletRequest request) throws Exception {
String clientId = assignClientId(prop, request);
if (StringUtils.isBlank(clientId)) {
throw new JwtAuthException(
String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_UNKNOWN, "00"),
"Can not find clientId info");
}
ClientVO client = (ClientVO) OAuth2Manager.getInstance().getClientDeatilsStore().get(clientId);
if (client == null) {
throw new JwtAuthException(
String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_UNKNOWN, "00"),
"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;
}
private String getEndpointUrl(HttpServletRequest request) {
String servletPath = request.getServletPath();
String pathInfo = request.getPathInfo();
String queryString = request.getQueryString();
StringBuilder url = new StringBuilder();
url.append(servletPath);
if (pathInfo != null) {
url.append(pathInfo);
}
if (queryString != null) {
url.append("?").append(queryString);
}
return url.toString();
}
private String getRequestBody(String message) throws Exception {
if (StringUtils.isEmpty(message)) {
return "";
}
// Lowercase(HexEncode(SHA-256(minify(RequestBody))))
String body = minifyJson(message);
try {
body = DigestUtils.sha256Hex(body);
} catch (Exception e) {
throw new JwtAuthException(
String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_UNKNOWN, "00"),
"SHA-256 digest error");
}
return body.toLowerCase();
}
private String minifyJson(String json) {
try {
ObjectMapper objectMapper = new ObjectMapper();
JsonNode jsonNode = objectMapper.readValue(json, JsonNode.class);
return jsonNode.toString();
} catch (Exception e) {
// json이 아닐경우
return json;
}
}
private String getTimeStamp(HttpServletRequest request) throws Exception {
String timeStamp = request.getHeader("X-TIMESTAMP");
if (StringUtils.isBlank(timeStamp)) {
throw new JwtAuthException(
String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_UNKNOWN, "00"),
"Can not find X-TIMESTAMP");
}
// 2020-12-23T09:10:11+07:00(현재시간이랑 비교로직 추가-보안)
ZonedDateTime xTimeStamp = ZonedDateTime.parse(timeStamp, DateTimeFormatter.ISO_DATE_TIME);
xTimeStamp = xTimeStamp.plusSeconds(X_TIMESTAMP_EXPIRATION_SECONDS);
ZonedDateTime now = ZonedDateTime.now();
if (xTimeStamp.isBefore(now.withZoneSameInstant(xTimeStamp.getZone()))) {
throw new JwtAuthException(
String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_UNKNOWN, "00"),
"Too old X-TIMESTAMP");
}
return timeStamp;
}
@Override
public Object doPostFilter(String adptGrpName, String adptName, Object resultMessage, Properties prop,
HttpServletRequest request, HttpServletResponse response) throws Exception {
return resultMessage;
}
public static void main(String[] args) {
String str = "POST:/api/test/aaa/type02/555:"
+ "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzY29wZSI6WyJzbmFwIl0sImV4cCI6MTY3NjUyNDI1MSwianRpIjoiNzI0MWMyZGUtZjJhNi00NTFmLWExMjQtZGM3NmI0ZGY1OThjIiwiY2xpZW50X2lkIjoic1NHSkR3bEpzZWw1V2VYb2R6ZDNKM01RMjBLWUNacEwifQ.Obe8t8IwuQ3P7i4yCSASYze-9DLmVwXe0g_gBYlwv_Jzc7V8-ooTwp6SVnaNsM6pp1GV-9lxMpAzQO_CRHQq_hdMeiPI_CXHh3gETvjQThn57QGEGdCNp_lUEVYtMPUxi5u3X6Of07o0_OB83WI7rA1zD0DaP8V67pgfq-jMRe_3IXztOYu_nwMgREbGvs9tqcsjxppRKkEHcK1S8Eq4HzaLwjwyHJAhIlTba27yd4T8ELC7IpFphJBfEPF_t0ZpYW15lOrg5pHPjy1xpTV0Kgipog5wycTZlFUeCWWjfxTrpVmt_1XlEOlZH9X6xAefWrH93_fpbt3OcxwP4u9KhA"
+ ":424058b889b9d860d13b79d90df52ddcbefba2fc9d27607e999c7c3c4d61d9c8:" + "2023-02-16T09:47:07+07:00";
String hmac = new HmacUtils(HmacAlgorithms.HMAC_SHA_512,
"o3Hre3voTrHbLueDrHC0LYM5effHQZILI8t23htbD12TKD5QJUMONqFqv4494iQS46bzHS0xW1fBC5LHo3D0SzhZvQcPUaJZw0iQ79NnzYFUCTrEsoFJRL300dp3Ql4y")
.hmacHex(str);
// System.out.println(hmac);
}
}
@@ -0,0 +1,237 @@
package com.eactive.eai.custom.adapter.http.dynamic.filter;
import java.io.IOException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.text.ParseException;
import java.util.Base64;
import java.util.Date;
import java.util.HashSet;
import java.util.Properties;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpStatus;
import com.eactive.eai.adapter.http.dynamic.HttpAdapterServiceSupport;
import com.eactive.eai.adapter.http.dynamic.filter.HttpAdapterFilter;
import com.eactive.eai.adapter.http.dynamic.filter.JwtAuthException;
import com.eactive.eai.adapter.http.dynamic.filter.JwtAuthFilter;
import com.eactive.eai.authserver.service.OAuth2Manager;
import com.eactive.eai.common.property.PropGroupVO;
import com.eactive.eai.common.property.PropManager;
import com.eactive.eai.common.util.Logger;
import com.eactive.eai.inbound.action.ActionFactory;
import com.eactive.eai.inbound.action.RequestAction;
import com.eactive.eai.inbound.processor.Processor;
import com.eactive.eai.message.StandardMessageUtil;
import com.nimbusds.jose.JWSVerifier;
import com.nimbusds.jose.crypto.RSASSAVerifier;
import com.nimbusds.jose.util.IOUtils;
import com.nimbusds.jwt.SignedJWT;
public class SnapJwtAuthFilter implements HttpAdapterFilter {
static Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);
public static final String PROP_GROUP_AUTH_SERVER = "OAuthServer";
public static final String PROP_KEYSTORE_PATH = "certification.publicKeyPath";
private static final String SERVICE_CODE_UNKNOWN = "00";
public static final String PAYLOAD_PARAM_NAME_SCOPE = "scope";
public static final String PAYLOAD_PARAM_NAME_CLIENT_ID = "client_id";
private JWSVerifier jwsVerifier;
public SnapJwtAuthFilter() {
super();
PropGroupVO vo = PropManager.getInstance().getPropGroupVO(PROP_GROUP_AUTH_SERVER);
String publicKeyPath = "/certificate/elink-oauth-dev.pub";
if (vo != null) {
publicKeyPath = vo.getProperty(PROP_KEYSTORE_PATH);
} else {
logger.warn("The properties has not been set.[" + PROP_GROUP_AUTH_SERVER + "]");
}
Resource resource = new ClassPathResource(publicKeyPath);
String publicKey = null;
try {
publicKey = IOUtils.readInputStreamToString(resource.getInputStream());
publicKey = StringUtils.replace(publicKey, "-----BEGIN PUBLIC KEY-----", "");
publicKey = StringUtils.replace(publicKey, "-----END PUBLIC KEY-----", "");
publicKey = StringUtils.remove(publicKey, "\r");
publicKey = StringUtils.remove(publicKey, "\n");
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKey));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
jwsVerifier = new RSASSAVerifier((RSAPublicKey) keyFactory.generatePublic(keySpec));
} catch (final IOException | NoSuchAlgorithmException | InvalidKeySpecException e) {
throw new RuntimeException(e);
}
}
@Override
public Object doPreFilter(String adptGrpName, String adptName, Object message, Properties prop,
HttpServletRequest request, HttpServletResponse response) throws Exception {
String apiId = assignApiId(adptGrpName, adptName, message, prop, request);
if (StringUtils.isBlank(apiId)) {
throw new JwtAuthException(
String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_UNKNOWN, "00"),
"Can not find apiId(apiCode) info");
}
try {
String token = JwtAuthFilter.extractJWTToken(request);
SignedJWT signedJWT = SignedJWT.parse(token);
if (new Date().after(signedJWT.getJWTClaimsSet().getExpirationTime())) {
throw new JwtAuthException(
String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_UNKNOWN, "01"),
"Invalid Token - expired (B2B)");
}
if (!signedJWT.verify(jwsVerifier)) {
throw new JwtAuthException(
String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_UNKNOWN, "01"),
"Invalid Token (B2B)");
}
OAuth2Manager manager = OAuth2Manager.getInstance();
HashSet<String> scopeSet = manager.getApiScopeMap().get(apiId);
if (!verifyScope(scopeSet, signedJWT)) {
throw new JwtAuthException(
String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_UNKNOWN, "01"),
String.format("Insufficient [Scope](Allowed Scope=%s)", scopeSet.toString()));
}
// header 값으로 전송된 clientId와 토큰 소유 clientId check
if (!verifyClientId(prop, signedJWT)) {
throw new JwtAuthException(
String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_UNKNOWN, "00"),
"client_id not matched(header/token)");
}
} catch (JwtAuthException e) {
logger.debug(e.getMessage());
throw e;
} catch (Exception e) {
logger.error(e.getMessage());
throw new JwtAuthException(
String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_UNKNOWN, "01"),
"Invalid Token (B2B)");
}
return message;
}
private boolean verifyClientId(Properties prop, SignedJWT signedJWT) {
try {
// 이전 filter에서 셋팅한 clientId 확보
String headerClientId = prop.getProperty(HttpAdapterServiceSupport.PROPERTIES_NAME_CLIENT_ID);
String tokenClientId = (String) signedJWT.getJWTClaimsSet().getClaim(PAYLOAD_PARAM_NAME_CLIENT_ID);
// 이전 filter에서 셋팅한 값이 없다면 token 정보에서 확보
if (StringUtils.isBlank(headerClientId)) {
if (StringUtils.isNotBlank(tokenClientId)) {
prop.put(HttpAdapterServiceSupport.PROPERTIES_NAME_CLIENT_ID, tokenClientId);
}
} else { // header로 전달된 clientId가 있을때만 비교
if (!headerClientId.equals(tokenClientId)) {
return false;
}
}
} catch (Exception e) {
logger.error(e.getMessage());
}
return true;
}
private String assignApiId(String adptGrpName, String adptName, Object message, Properties prop,
HttpServletRequest request) {
String apiId = null;
try {
String actionName = prop.getProperty(Processor.REQUEST_ACTION);
RequestAction action = ActionFactory.createAction(actionName);
action.setAdapterInfo(adptGrpName, adptName, prop);
String[] keys = action.perform(message);
apiId = keys[0];
// PathVariable 지원 추가
apiId = StandardMessageUtil.getMatchedKey(apiId, actionName);
} catch (Exception e) {
logger.error(e.getMessage());
}
// // header 에서 확보
// String apiId = request.getHeader(HEADER_NAME_API_CODE);
// if (StringUtils.isBlank(apiId)) {
// // url에서 확보
// // /ONLWeb/api/v1/public/getUserInfo.svc/
// apiId = request.getRequestURI();
// // /ONLWeb/api/v1/public/getUserInfo.svc
// apiId = StringUtils.removeEnd(apiId, "/");
// // getUserInfo.svc
// apiId = StringUtils.substringAfterLast(apiId, "/");
// }
return apiId;
}
@Override
public Object doPostFilter(String adptGrpName, String adptName, Object resultMessage, Properties prop,
HttpServletRequest request, HttpServletResponse response) throws Exception {
return resultMessage;
}
private boolean verifyScope(HashSet<String> scopeSet, SignedJWT signedJWT) throws ParseException {
if (scopeSet == null || scopeSet.isEmpty()) {
return true; // If no scope is specified in the api, all are allowed
}
String[] scopeArr = signedJWT.getJWTClaimsSet().getStringArrayClaim(PAYLOAD_PARAM_NAME_SCOPE);
if (scopeArr == null || scopeArr.length == 0) {
return false;
}
for (String scope : scopeArr) {
if (scopeSet.contains(scope)) {
return true;
}
}
return false;
}
public static String extractJWTToken(HttpServletRequest request) throws JwtAuthException {
String authorization = request.getHeader("Authorization");
if (StringUtils.isBlank(authorization)) {
throw new JwtAuthException(
String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_UNKNOWN, "00"),
"No have header info: Authorization");
}
String[] components = authorization.split("\\s");
if (components.length != 2) {
throw new JwtAuthException(
String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_UNKNOWN, "00"),
"Malformat [Authorization] content");
}
if (!StringUtils.equalsIgnoreCase(components[0], "Bearer")) {
throw new JwtAuthException(
String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_UNKNOWN, "00"),
"[Bearer] is needed");
}
return components[1].trim();
}
}
@@ -0,0 +1,205 @@
package com.eactive.eai.custom.adapter.http.dynamic.filter;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.TokenStore;
import com.eactive.eai.adapter.http.dynamic.HttpAdapterServiceSupport;
import com.eactive.eai.adapter.http.dynamic.filter.HttpAdapterFilter;
import com.eactive.eai.adapter.http.dynamic.filter.JwtAuthException;
import com.eactive.eai.authserver.service.OAuth2Manager;
import com.eactive.eai.authserver.util.BeanUtils;
import com.eactive.eai.common.util.Logger;
import com.eactive.eai.inbound.action.ActionFactory;
import com.eactive.eai.inbound.action.RequestAction;
import com.eactive.eai.inbound.processor.Processor;
import com.eactive.eai.message.StandardMessageUtil;
public class SnapSimpleOauth2Filter implements HttpAdapterFilter {
static Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);
private static final String SERVICE_CODE_UNKNOWN = "00";
private TokenStore tokenStore;
public SnapSimpleOauth2Filter() {
super();
tokenStore = BeanUtils.getBean("tokenStore", TokenStore.class);
}
@Override
public Object doPreFilter(String adptGrpName, String adptName, Object message, Properties prop,
HttpServletRequest request, HttpServletResponse response) throws Exception {
try {
String apiId = assignApiId(adptGrpName, adptName, message, prop, request);
if (StringUtils.isBlank(apiId)) {
throw new JwtAuthException(
String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_UNKNOWN, "00"),
"Can not find apiId(apiCode) info");
}
String tokenStr = extractToken(request);
OAuth2AccessToken token = tokenStore.readAccessToken(tokenStr);
if (token == null) {
throw new JwtAuthException(
String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_UNKNOWN, "03"),
"Token Not Found (B2B)");
}
if (token.isExpired()) {
throw new JwtAuthException(
String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_UNKNOWN, "01"),
"Invalid Token - expired (B2B)");
}
OAuth2Authentication authentication = tokenStore.readAuthentication(token);
if (!authentication.isAuthenticated()) {
throw new JwtAuthException(
String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_UNKNOWN, "01"),
"Invalid Token (B2B)");
}
// check scope
OAuth2Manager manager = OAuth2Manager.getInstance();
HashSet<String> scopeSet = manager.getApiScopeMap().get(apiId);
if (!verifyScope(scopeSet, authentication)) {
throw new JwtAuthException(
String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_UNKNOWN, "01"),
String.format("Insufficient [Scope](Allowed Scope=%s)", scopeSet.toString()));
}
// header 값으로 전송된 clientId와 토큰 소유 clientId check
if (!verifyClientId(prop, authentication)) {
throw new JwtAuthException(
String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_UNKNOWN, "00"),
"client_id not matched(header/token)");
}
} catch (JwtAuthException e) {
logger.debug(e.getMessage());
throw e;
} catch (Exception e) {
logger.error(e.getMessage());
throw new JwtAuthException(
String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_UNKNOWN, "01"),
"Invalid Token (B2B)");
}
return message;
}
private boolean verifyScope(HashSet<String> scopeSet, OAuth2Authentication auth) {
if (scopeSet == null || scopeSet.isEmpty()) {
return true; // If no scope is specified in the api, all are allowed
}
Set<String> scopesInToken = auth.getOAuth2Request().getScope();
if (scopesInToken == null || scopesInToken.isEmpty()) {
return false;
}
for (String scope : scopesInToken) {
if (scopeSet.contains(scope)) {
return true;
}
}
return false;
}
private boolean verifyClientId(Properties prop, Authentication authentication) {
try {
// 이전 filter에서 셋팅한 clientId 확보
String headerClientId = prop.getProperty(HttpAdapterServiceSupport.PROPERTIES_NAME_CLIENT_ID);
String tokenClientId = ((OAuth2Authentication) authentication).getOAuth2Request().getClientId();
// 이전 filter에서 셋팅한 값이 없다면 token 정보에서 확보
if (StringUtils.isBlank(headerClientId)) {
if (StringUtils.isNotBlank(tokenClientId)) {
prop.put(HttpAdapterServiceSupport.PROPERTIES_NAME_CLIENT_ID, tokenClientId);
}
} else { // header로 전달된 clientId가 있을때만 비교
if (!headerClientId.equals(tokenClientId)) {
return false;
}
}
} catch (Exception e) {
logger.error(e.getMessage());
}
return true;
}
private String assignApiId(String adptGrpName, String adptName, Object message, Properties prop,
HttpServletRequest request) {
String apiId = null;
try {
String actionName = prop.getProperty(Processor.REQUEST_ACTION);
RequestAction action = ActionFactory.createAction(actionName);
action.setAdapterInfo(adptGrpName, adptName, prop);
String[] keys = action.perform(message);
apiId = keys[0];
// PathVariable 지원 추가
apiId = StandardMessageUtil.getMatchedKey(apiId, actionName);
} catch (Exception e) {
logger.error(e.getMessage());
}
// // header 에서 확보
// String apiId = request.getHeader(HEADER_NAME_API_CODE);
// if (StringUtils.isBlank(apiId)) {
// // url에서 확보
// // /ONLWeb/api/v1/public/getUserInfo.svc/
// apiId = request.getRequestURI();
// // /ONLWeb/api/v1/public/getUserInfo.svc
// apiId = StringUtils.removeEnd(apiId, "/");
// // getUserInfo.svc
// apiId = StringUtils.substringAfterLast(apiId, "/");
// }
return apiId;
}
public static String extractToken(HttpServletRequest request) throws JwtAuthException {
String authorization = request.getHeader("Authorization");
if (StringUtils.isBlank(authorization)) {
throw new JwtAuthException(
String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_UNKNOWN, "00"),
"No have header info: Authorization");
}
String[] components = authorization.split("\\s");
if (components.length != 2) {
throw new JwtAuthException(
String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_UNKNOWN, "00"),
"Malformat [Authorization] content");
}
if (!StringUtils.equalsIgnoreCase(components[0], "Bearer")) {
throw new JwtAuthException(
String.format("%d%s%s", HttpStatus.UNAUTHORIZED.value(), SERVICE_CODE_UNKNOWN, "00"),
"[Bearer] is needed");
}
return components[1].trim();
}
@Override
public Object doPostFilter(String adptGrpName, String adptName, Object resultMessage, Properties prop,
HttpServletRequest request, HttpServletResponse response) throws Exception {
return resultMessage;
}
}
@@ -1,18 +0,0 @@
package com.eactive.eai.custom.adapter.http.dynamic.filter;
import java.util.Properties;
import com.eactive.eai.adapter.http.dynamic.filter.InCryptoFilter;
/**
* 토스 암복호화에 맟추어 커스텀 필요
*/
public class TossEncFieldInCryptoFilter extends EncFieldInCryptoFilter {
protected Properties setProp(Properties p) {
p.setProperty(InCryptoFilter.PROP_DEC_FROM_PATH, "/encrypted_data");
p.setProperty(InCryptoFilter.PROP_ENC_TO_PATH, "/encrypted_data");
return p;
}
}
@@ -1,18 +0,0 @@
package com.eactive.eai.custom.adapter.http.dynamic.filter;
import java.util.Properties;
import com.eactive.eai.adapter.http.dynamic.filter.InCryptoFilter;
/**
* 토스뱅크 암복호화에 맟추어 커스텀 필요
*/
public class TossbankEncFieldInCryptoFilter extends EncFieldInCryptoFilter {
protected Properties setProp(Properties p) {
p.setProperty(InCryptoFilter.PROP_DEC_FROM_PATH, "/encryptedData");
p.setProperty(InCryptoFilter.PROP_ENC_TO_PATH, "/encryptedData");
return p;
}
}
@@ -0,0 +1,120 @@
package com.eactive.eai.custom.adapter.http.dynamic.filter;
import java.util.Arrays;
import java.util.List;
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.AdapterManager;
import com.eactive.eai.adapter.http.dynamic.HttpAdapterServiceKey;
import com.eactive.eai.adapter.http.dynamic.HttpAdapterServiceSupport;
import com.eactive.eai.adapter.http.dynamic.filter.HttpAdapterFilter;
import com.eactive.eai.authserver.service.OAuth2Manager;
import com.eactive.eai.authserver.vo.ClientVO;
import com.eactive.eai.common.dao.DAOException;
import com.eactive.eai.common.property.PropGroupVO;
import com.eactive.eai.common.property.PropManager;
import com.eactive.eai.custom.adapter.http.util.VirtualAccountBatchUtil;
import com.eactive.eai.custom.adapter.http.util.VirtualAccountTransform;
public class VirtualAccountCryptoFilter implements HttpAdapterFilter {
// FIXME : kbank - 프라퍼티 그릅정의 확정
private static final String BATCH_CHECK_URL = "/batch/";
private static final String BATCH_PROP_GROUP = "VIRTUALACCOUNT_INFO";
private static final String BATCH_FILE_PATH = "BATCH_FILE_PATH";
private static final String FILE_NAME_PATTERN = "FILE_NAME_PATTERN";
private String getAesKey(String clientId) {
OAuth2Manager manager = OAuth2Manager.getInstance();
try {
ClientVO client = manager.getClientInfo(clientId);
return client.getSecurityKey();
} catch (DAOException e) {
e.printStackTrace();
return null;
}
}
private boolean isBatch(HttpServletRequest request) {
String uri = request.getRequestURI();
return uri != null && uri.contains(BATCH_CHECK_URL);
}
private String getRootFilePath() {
PropGroupVO vo = PropManager.getInstance().getPropGroupVO(BATCH_PROP_GROUP);
return vo.getProperty(BATCH_FILE_PATH);
}
private String getFileNamePattern() {
PropGroupVO vo = PropManager.getInstance().getPropGroupVO(BATCH_PROP_GROUP);
return vo.getProperty(FILE_NAME_PATTERN);
}
@Override
public Object doPreFilter(String adptGrpName, String adptName, Object message, Properties prop,
HttpServletRequest request, HttpServletResponse response) throws Exception {
String charset = StringUtils.defaultIfBlank(AdapterManager.getInstance().getAdapterGroupVO(adptGrpName).getMessageEncode(), "UTF-8");
String encPaths = prop.getProperty(HttpAdapterServiceKey.ENC_PATHS);
List<String> encFieldList = null;
String clientId = prop.getProperty(HttpAdapterServiceSupport.PROPERTIES_NAME_CLIENT_ID);
String aesKey = getAesKey(clientId);
String convertedMessage = null;
if(isBatch(request)) {
String rootFilePath = getRootFilePath();
String fileNamePattern = getFileNamePattern();
VirtualAccountBatchUtil batchUtil = new VirtualAccountBatchUtil();
if(message instanceof byte[]) {
convertedMessage = new String((byte[])message, charset);
} else {
convertedMessage = (String) message;
}
batchUtil.unzipAndSave(aesKey, convertedMessage, rootFilePath, fileNamePattern);
}
else {
if(StringUtils.isNotBlank(encPaths)) {
String[] items = encPaths.split(",");
encFieldList = Arrays.asList(items);
if(message instanceof byte[]) {
convertedMessage = new String((byte[])message, charset);
} else {
convertedMessage = (String) message;
}
convertedMessage = VirtualAccountTransform.toCubeOne(aesKey, encFieldList, convertedMessage);
}
}
return convertedMessage;
}
@Override
public Object doPostFilter(String adptGrpName, String adptName, Object resultMessage, Properties prop,
HttpServletRequest request, HttpServletResponse response) throws Exception {
String charset = StringUtils.defaultIfBlank(AdapterManager.getInstance().getAdapterGroupVO(adptGrpName).getMessageEncode(), "UTF-8");
String encPaths = prop.getProperty(HttpAdapterServiceKey.ENC_PATHS);
List<String> encFieldList = null;
String clientId = prop.getProperty(HttpAdapterServiceSupport.PROPERTIES_NAME_CLIENT_ID);
String aesKey = getAesKey(clientId);
if(isBatch(request)) {
return resultMessage;
}
else {
String resMessageString = null;
if(StringUtils.isNotBlank(encPaths)) {
String[] items = encPaths.split(",");
encFieldList = Arrays.asList(items);
if(resultMessage instanceof byte[]) {
resMessageString = new String((byte[])resultMessage, charset);
} else {
resMessageString = (String) resultMessage;
}
resMessageString = VirtualAccountTransform.toAES(aesKey, encFieldList, resMessageString);
}
return resMessageString;
}
}
}
@@ -0,0 +1,53 @@
package com.eactive.eai.custom.adapter.http.util;
import com.eactive.eai.util.TestModeChecker;
import com.cubeone.CubeOneAPI;
public class CubeOneCryptoUtil {
private static boolean testMode = TestModeChecker.isTestMode();
public static String encryptPI(String plainText) throws Exception {
// FIXME : kbank - kbank내애서는 testMode 체크 제거 검토
if (testMode) {
return plainText;
} else {
String encryptedText = null;
byte errbyte[] = new byte[5];
byte[] inbyte;
try {
inbyte = plainText.getBytes();
encryptedText = CubeOneAPI.coencbytes(inbyte, inbyte.length, "AES_PI", 11, null, null, errbyte);
} catch (UnsatisfiedLinkError e) {
throw new Exception("encryptPI error - ", e);
} catch (Exception e) {
throw new Exception("encryptPI error - ", e);
}
//
if (errbyte[0] != 48) {
throw new Exception("encryptPI error - " + new String(errbyte));
}
return encryptedText;
}
}
public static String decryptPI(String encryptedText) throws Exception {
// FIXME : kbank - kbank내애서는 testMode 체크 제거 검토
if (testMode) {
return encryptedText;
} else {
String decryptedText = null;
byte errbyte[] = new byte[5];
byte[] decbyte;
decbyte = CubeOneAPI.codecbyte(encryptedText, "AES_PI", 11, null, null, errbyte);
if (decbyte != null) {
decryptedText = new String(decbyte);
}
if (errbyte[0] != 48) {
throw new Exception("decryptPI error - " + new String(errbyte));
}
return decryptedText;
}
}
}
@@ -0,0 +1,66 @@
package com.eactive.eai.custom.adapter.http.util;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Base64;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
public class GzipBase64Util {
private static final String DEFAULT_CAHRSET = "UTF-8";
public static void main(String[] args) {
String originalString = "압축하고 인코딩할 문자열입니다.";
try {
String base64EncodedData = compress(originalString);
// System.out.println("Original String: " + originalString);
// System.out.println("Base64 Encoded String: " + base64EncodedData);
// Base64 디코딩 후 gzip 압축 해제
String decodedString = decompress(base64EncodedData);
// System.out.println("Decoded String: " + decodedString);
} catch (IOException e) {
e.printStackTrace();
}
}
public static String compress(String plainText) throws IOException {
if (plainText == null || plainText.isEmpty()) {
return null;
}
try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
try (GZIPOutputStream gzipOutputStream = new GZIPOutputStream(byteArrayOutputStream)) {
gzipOutputStream.write(plainText.getBytes(DEFAULT_CAHRSET));
}
byte[] compressedData = byteArrayOutputStream.toByteArray();
return Base64.getEncoder().encodeToString(compressedData);
}
}
public static String decompress(String base64EncodedData) throws IOException {
if (base64EncodedData == null || base64EncodedData.length() == 0) {
return null;
}
byte[] compressedData = Base64.getDecoder().decode(base64EncodedData);
try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(compressedData);
GZIPInputStream gzipInputStream = new GZIPInputStream(byteArrayInputStream)) {
byte[] buffer = new byte[1024];
int len;
while ((len = gzipInputStream.read(buffer)) > 0) {
byteArrayOutputStream.write(buffer, 0, len);
}
}
return byteArrayOutputStream.toString(DEFAULT_CAHRSET);
}
}
}
@@ -0,0 +1,29 @@
package com.eactive.eai.custom.adapter.http.util;
import java.util.HashMap;
import java.util.Map;
import com.eactive.eai.util.CryptoUtil;
import com.eactive.eai.util.json.transformer.ValueTransformer;
public class ToAesTransformer implements ValueTransformer<String, String> {
public static final String AESKEY = "AESKEY";
public static final String PLAIN_VALUE = "PLAIN_VALUE";
private final Map<String, Object> properties = new HashMap<>();
@Override
public void setProperty(String propertyName, Object value) {
properties.put(propertyName, value);
}
@Override
public String getProperty(String propertyName) {
return (String)properties.get(propertyName);
}
@Override
public String transform(String value) throws Exception {
String apiKey = (String) properties.get(ToAesTransformer.AESKEY);
String text = CubeOneCryptoUtil.decryptPI(value);
properties.put(PLAIN_VALUE, text);
return CryptoUtil.encryptAES(apiKey, text);
}
}
@@ -0,0 +1,30 @@
package com.eactive.eai.custom.adapter.http.util;
import java.util.HashMap;
import java.util.Map;
import com.eactive.eai.util.CryptoUtil;
import com.eactive.eai.util.json.transformer.ValueTransformer;
public class ToCubeOneTransformer implements ValueTransformer<String, String> {
public static final String AESKEY = "AESKEY";
public static final String PLAIN_VALUE = "PLAIN_VALUE";
private final Map<String, Object> properties = new HashMap<>();
@Override
public void setProperty(String propertyName, Object value) {
properties.put(propertyName, value);
}
@Override
public String getProperty(String propertyName) {
return (String)properties.get(propertyName);
}
@Override
public String transform(String value) throws Exception {
String apiKey = (String) properties.get(ToCubeOneTransformer.AESKEY);
String text = CryptoUtil.decryptAES(apiKey, value);
properties.put(PLAIN_VALUE, text);
return CubeOneCryptoUtil.encryptPI(text);
}
}
@@ -0,0 +1,81 @@
package com.eactive.eai.custom.adapter.http.util;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.time.LocalDateTime;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.eactive.eai.util.CryptoUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
public class VirtualAccountBatchUtil {
private ObjectMapper mapper = new ObjectMapper();
public String getGzipValue(String jsonString, String path) {
try {
JsonNode rootNode = mapper.readTree(jsonString);
JsonNode childNode = rootNode.get(path);
String gzippedValue = childNode.asText();
return gzippedValue;
}
catch(Exception ex) {
ex.printStackTrace();
return null;
}
}
// AZZ22-%d{yyyyMMdd}-%d{hhmmss}.enc -> AZZ22-20240924-093257.enc
public static String updateDatePattern(String patterm) {
String str[] = patterm.split("%d");
String converted = "";
for(int i=0; i<str.length; i++){
if(i<1){
converted = str[i];
}else{
String newMsg = "%d"+str[i];
Matcher matcher = Pattern.compile("%d\\{([a-zA-Z \\-]+)\\}").matcher(newMsg);
if (matcher.find()) {
String datePattern = matcher.group(1);
DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern(datePattern);
converted = converted + newMsg.replaceAll("%d\\{([a-zA-Z \\-]+)\\}", dateFormat.format(ZonedDateTime.now()));
}
}
}
return converted;
}
// FIXME : kbank - 배치 파일명 생성 규칙에 따라 수정
public String unzipAndSave(String aesKey, String jsonString, String rootFilePath, String fileNamePattern) throws Exception {
// gzippedValue 추출
String gzippedValue = getGzipValue(jsonString, "encData");
// System.out.println("gzippedValue[" + gzippedValue + "]");
// 압축 해제
String unziped = GzipBase64Util.decompress(gzippedValue);
// AES 복호화
String text = CryptoUtil.decryptAES(aesKey, unziped);
// 현재 처리 시각을 기반으로 파일명 생성
// DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss");
// String fileName = LocalDateTime.now().format(formatter) + ".txt";
String fileName = updateDatePattern(fileNamePattern);
// TODO : Add additional pattern logic
String filePath = Paths.get(rootFilePath, fileName).toString();
Files.write(Paths.get(filePath), text.getBytes(), StandardOpenOption.CREATE);
// 결과 JSON 구성
ObjectNode replacedJson = mapper.createObjectNode();
replacedJson.put("filePath", filePath);
replacedJson.put("fileName", fileName);
return replacedJson.toString();
}
}
@@ -0,0 +1,71 @@
package com.eactive.eai.custom.adapter.http.util;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.eactive.eai.common.util.Logger;
import com.eactive.eai.util.json.JsonPathsTransform;
import com.eactive.eai.util.json.transformer.ValueTransformer;
public class VirtualAccountTransform {
protected static Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);
public static String toAES(String aesKey, List<String> encFieldPaths, String message) {
ToAesTransformer encTramsformer = new ToAesTransformer();
encTramsformer.setProperty("AESKEY", aesKey);
Map<String, ValueTransformer<String, String>> pathTransformerMap = new HashMap<>();
for (String path : encFieldPaths) {
pathTransformerMap.put(path.trim(), encTramsformer);
}
String modifiedJsonString = JsonPathsTransform.modifyValuesAtPaths(message, pathTransformerMap, false);
return modifiedJsonString;
}
public static byte[] toCubeOne(String aesKey, List<String> encFieldPaths, byte[] messageBytes) {
String defaultCharset = "utf-8";
try {
String message = new String(messageBytes, defaultCharset);
String retMessage = toCubeOne(aesKey, encFieldPaths, message);
return retMessage.getBytes(defaultCharset);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return messageBytes;
}
}
public static String toCubeOne(String aesKey, List<String> encFieldPaths, String message) {
ToCubeOneTransformer encTramsformer = new ToCubeOneTransformer();
if(logger.isDebug()) {
logger.debug("AESKEY=" + aesKey);
}
encTramsformer.setProperty("AESKEY", aesKey);
Map<String, ValueTransformer<String, String>> pathTransformerMap = new HashMap<>();
for (String path : encFieldPaths) {
pathTransformerMap.put(path.trim(), encTramsformer);
}
String modifiedJsonString = JsonPathsTransform.modifyValuesAtPaths(message, pathTransformerMap, false);
return modifiedJsonString;
}
public static void main(String[] args) {
String aesKey = "ONAuROzlqWB4VuBwO5C/FA==";
String text = "Hello AES 한글";
String encText = "3hQyg4xrUrFQt09XKLkmutQ4pZyuKc242LGQ1dyDMTg=";
String json = "{\"encAccount\":\"3hQyg4xrUrFQt09XKLkmutQ4pZyuKc242LGQ1dyDMTg=\"}";
List<String> list = new ArrayList<>();
list.add(" $.encAccount ");
list.add(" $.accounts$.encAccount ");
String cubeOneJson = toCubeOne(aesKey, list, json);
// System.out.println(cubeOneJson);
// System.out.println(toAES(aesKey, list, cubeOneJson));
}
}
@@ -1,62 +0,0 @@
package com.eactive.eai.custom.common.security.keyderiv;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Map;
import javax.crypto.SecretKey;
import com.eactive.eai.common.hsm.HsmCryptoService;
import com.eactive.eai.common.security.keyderiv.DerivedKey;
import com.eactive.eai.common.security.keyderiv.KeyDerivationStrategy;
import com.eactive.eai.common.util.ApplicationContextProvider;
/**
* HSM 마스터키 + 런타임 컨텍스트값을 연결(concatenate)한 뒤 SHA-256 해싱으로 키를 도출하는 전략.
* SHA-256 출력은 항상 32바이트(AES-256)이므로 별도 keyLength 파라미터가 불필요하다.
*
* key_deriv_params (JSON) 예시:
* {
* "hsmKeyAlias" : "MASTER_KEY_AES",
* "contextKey" : "X-Api-Group-Seq" -- runtimeContext에서 꺼낼 키 이름
* }
*/
public class HsmContextSha256KeyDerivationStrategy implements KeyDerivationStrategy {
@Override
public DerivedKey deriveKey(Map<String, String> params, Map<String, String> runtimeContext) throws Exception {
String hsmKeyAlias = required(params, PARAM_HSM_KEY_ALIAS);
String contextKey = required(params, PARAM_CONTEXT_KEY);
SecretKey masterKey = hsmCryptoService().getSecretKey(hsmKeyAlias);
byte[] masterKeyBytes = masterKey.getEncoded();
byte[] contextBytes = runtimeContext.getOrDefault(contextKey, "")
.getBytes(StandardCharsets.UTF_8);
byte[] combined = new byte[masterKeyBytes.length + contextBytes.length];
System.arraycopy(masterKeyBytes, 0, combined, 0, masterKeyBytes.length);
System.arraycopy(contextBytes, 0, combined, masterKeyBytes.length, contextBytes.length);
byte[] derived = MessageDigest.getInstance("SHA-256").digest(combined);
return new DerivedKey(derived, derived);
}
@Override
public String buildCacheKey(String cryptoName, Map<String, String> params, Map<String, String> runtimeContext) {
String contextKey = params.getOrDefault(PARAM_CONTEXT_KEY, "");
String contextValue = runtimeContext.getOrDefault(contextKey, "");
return cryptoName + ":" + contextValue;
}
private String required(Map<String, String> params, String key) {
String value = params.get(key);
if (value == null || value.trim().isEmpty()) {
throw new IllegalArgumentException("key_deriv_params 필수값 누락: " + key);
}
return value;
}
private HsmCryptoService hsmCryptoService() {
return ApplicationContextProvider.getContext().getBean(HsmCryptoService.class);
}
}
@@ -1,41 +0,0 @@
package com.eactive.eai.custom.common.security.keyderiv;
import java.util.Map;
import javax.xml.bind.DatatypeConverter;
import com.eactive.eai.common.security.keyderiv.DerivedKey;
import com.eactive.eai.common.security.keyderiv.strategy.BaseHsmKeyDerivationStrategy;
/**
* HSM에서 키와 IV를 각각 가져오는 전략.
* IV는 HSM에서 가져온 바이트 배열을 hex 문자열로 변환한 뒤 substring(32, 64)를 적용하여 사용한다.
* (32바이트 HSM 키 → 64자 hex → substring(32,64) → 16바이트 IV)
*
* key_deriv_params (JSON) 예시:
* { "hsmKeyAlias" : "MASTER_KEY_AES", "hsmIvAlias" : "MASTER_IV_AES" }
*/
public class HsmKeyAndIvSliceDerivationStrategy extends BaseHsmKeyDerivationStrategy {
private static final int IV_HEX_OFFSET = 32;
private static final int IV_HEX_END = 64;
@Override
public DerivedKey deriveKey(Map<String, String> params, Map<String, String> runtimeContext) throws Exception {
byte[] keyBytes = super.getHsmKey(params);
byte[] rawIv = super.getHsmIv(params);
String ivHex = DatatypeConverter.printHexBinary(rawIv);
if (ivHex.length() < IV_HEX_END) {
throw new IllegalArgumentException(
"hsmIvAlias hex 길이 부족: 최소 " + IV_HEX_END + "자 필요, 실제=" + ivHex.length()
+ " (" + rawIv.length + "바이트)");
}
byte[] iv = DatatypeConverter.parseHexBinary(ivHex.substring(IV_HEX_OFFSET, IV_HEX_END));
return new DerivedKey(keyBytes, keyBytes, iv);
}
@Override
public String buildCacheKey(String cryptoName, Map<String, String> params, Map<String, String> runtimeContext) {
return cryptoName;
}
}
@@ -1,169 +0,0 @@
package com.eactive.eai.custom.inbound.processor;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import com.eactive.eai.common.inflow.Bucket;
import com.eactive.eai.common.inflow.InflowTargetVO;
import com.eactive.eai.common.inflow.dual.DualBucket;
import com.eactive.eai.common.inflow.dual.DualCustomBucket;
import com.eactive.eai.common.message.EAIMessage;
import com.eactive.eai.common.property.PropManager;
import com.eactive.eai.common.util.Logger;
import com.eactive.eai.custom.inflow.ClientDualBucket;
import com.eactive.eai.inbound.processor.ProcessVO;
import com.eactive.eai.inbound.processor.RequestProcessor;
import com.eactive.eai.message.StandardItem;
import com.eactive.eai.message.StandardMessage;
/**
* 어댑터/인터페이스/클라이언트 유량제어 차단 시 어느 버킷(PER_SECOND/THRESHOLD)에서
* 차단됐는지 에러 메시지에 포함하는 RequestProcessor 확장.
*
* <p>ClientDualInflowControlManager와 함께 사용:
* <pre>
* inflow.control.bucket.className=com.eactive.eai.common.inflow.dual.ClientDualInflowControlManager
* </pre>
*
* <p>Spring Bean 등록 후 기존 RequestProcessor 대신 이 클래스를 어댑터에 설정한다.
*/
public class DJBRequestProcessor extends RequestProcessor {
static Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT);
@Override
protected void afterFoundEaiMessage(EAIMessage eaiMsg, ProcessVO vo, Properties prop) {
logger.debug("DJBRequestProcessor interface found.");
try {
this.resolveStandardHeaderMapping(eaiMsg, prop);
} catch (Exception e) {
logger.warn("DJBRequestProcessor header set failed.", e);
}
}
private void resolveStandardHeaderMapping(EAIMessage eaiMsg, Properties prop) {
StandardMessage standardMessage = eaiMsg.getStandardMessage();
HashMap<String, String> headerMapping = getPropertyMap("DJB_IN_HTTHEAD_STDHEAD_MAPPING", eaiMsg.getSngSysItfTp());
for (Entry<String, String> entry : headerMapping.entrySet()) {
String stdHeaderPath = entry.getKey();
int dotIdx = stdHeaderPath.indexOf('.');
if(dotIdx > 0) {
String firstPath = stdHeaderPath.substring(0, dotIdx);
StandardItem topLevelItem = standardMessage.findItem(firstPath);
topLevelItem.setHidden(false);
}
StandardItem item = standardMessage.findItem(entry.getKey());
String value = resolveNestedValue(prop, entry.getValue());
if (item != null && value != null)
item.setValue(value);
else
logger.debug("header mapping item not found : {} ", entry);
}
}
private HashMap<String, String> getPropertyMap(String propertyGroupName, String prefix) {
Properties prop = PropManager.getInstance().getProperties(propertyGroupName);
Set<String> keys = prop.stringPropertyNames();
HashMap<String, String> tmpToJsonArrayInterface = new HashMap<>();
for (String key : keys) {
if (StringUtils.startsWith(key, prefix + ".")) {
String propValue = prop.getProperty(key);
String replacedKey = StringUtils.replace(key, prefix + ".", "");
tmpToJsonArrayInterface.put(replacedKey, propValue);
}
}
return tmpToJsonArrayInterface;
}
private String resolveNestedValue(Map<?, ?> map, String keyPath) {
int dotIdx = keyPath.indexOf('.');
if (dotIdx < 0) {
Object val = map.get(keyPath);
if (val == null)
return null;
if (val instanceof String)
return (String) val;
return val.toString();
}
String first = keyPath.substring(0, dotIdx);
String rest = keyPath.substring(dotIdx + 1);
Object val = map.get(first);
if (val instanceof Map) {
return resolveNestedValue((Map<?, ?>) val, rest);
}
// 폴백: 전체 keyPath를 단일 키로 조회
Object direct = map.get(keyPath);
if (direct == null)
return null;
if (direct instanceof String)
return (String) direct;
return direct.toString();
}
@Override
protected String checkCustomInflow(Bucket bucket, EAIMessage eaiMsg, Properties prop) {
String clientId = eaiMsg.getClientId();
if (StringUtils.isBlank(clientId)) return null;
if (bucket instanceof ClientDualBucket) {
return ((ClientDualBucket) bucket).isClientPassDetail(clientId);
}
return null;
}
@Override
protected InflowTargetVO resolveCustomInflowThreshold(Bucket bucket, EAIMessage eaiMsg, Properties prop) {
if (bucket instanceof ClientDualBucket) {
return ((ClientDualBucket) bucket).getClientInflowThreshold(eaiMsg.getClientId());
}
return null;
}
@Override
protected String checkAdapterInflow(Bucket bucket, String adapterGroupName) {
if (bucket instanceof DualBucket) {
return ((DualBucket) bucket).isAdapterPassDetail(adapterGroupName);
}
return super.checkAdapterInflow(bucket, adapterGroupName);
}
@Override
protected String checkInterfaceInflow(Bucket bucket, String eaiSvcCd) {
if (bucket instanceof DualBucket) {
return ((DualBucket) bucket).isInterfacePassDetail(eaiSvcCd);
}
return super.checkInterfaceInflow(bucket, eaiSvcCd);
}
/**
* 차단 원인에 따라 에러 메시지에 한도 정보를 포함.
* <ul>
* <li>PER_SECOND: "API 호출 한도 초과 [name: Nreq/sec]"</li>
* <li>THRESHOLD: "API 호출 한도 초과 [name: Nreq/UNIT]"</li>
* <li>그 외: "API 호출 한도 초과 [name]" (기존 동작)</li>
* </ul>
*/
@Override
protected String buildInflowTargetErrorMsg(InflowTargetVO inflowTargetVO, String blockedType) {
if (DualCustomBucket.RESULT_BLOCKED_PER_SECOND.equals(blockedType)) {
return String.format("API 호출 한도 초과 [%s: %dreq/sec]",
inflowTargetVO.getName(),
inflowTargetVO.getThresholdPerSecond());
}
if (DualCustomBucket.RESULT_BLOCKED_THRESHOLD.equals(blockedType)) {
return String.format("API 호출 한도 초과 [%s: %dreq/%s]",
inflowTargetVO.getName(),
inflowTargetVO.getThreshold(),
inflowTargetVO.getThresholdTimeUnit());
}
return super.buildInflowTargetErrorMsg(inflowTargetVO, blockedType);
}
}
@@ -1,23 +0,0 @@
package com.eactive.eai.custom.inflow;
import com.eactive.eai.common.inflow.InflowTargetVO;
import com.eactive.eai.common.inflow.dual.DualBucket;
/**
* DualBucket에 클라이언트 유량제어 메서드를 추가한 인터페이스.
* {@link ClientDualInflowControlManager}가 구현한다.
*/
public interface ClientDualBucket extends DualBucket {
/**
* 클라이언트 유량제어 체크 — 차단 원인 포함.
* @return null이면 통과, "PER_SECOND" 또는 "THRESHOLD"이면 해당 버킷에서 차단
*/
String isClientPassDetail(String clientId);
/**
* 클라이언트 유량제어 설정 조회 — 에러 메시지 생성용.
* @return 등록된 설정이 없으면 null
*/
InflowTargetVO getClientInflowThreshold(String clientId);
}
@@ -1,114 +0,0 @@
package com.eactive.eai.custom.inflow;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.stereotype.Component;
import com.eactive.eai.common.inflow.Bucket;
import com.eactive.eai.common.inflow.InflowTargetVO;
import com.eactive.eai.common.inflow.InflowType;
import com.eactive.eai.common.inflow.dual.DualCustomBucket;
import com.eactive.eai.common.inflow.dual.DualInflowControlManager;
import com.eactive.eai.common.lifecycle.LifecycleException;
import com.eactive.eai.common.util.ApplicationContextProvider;
import com.eactive.eai.common.util.Logger;
/**
* 어댑터/인터페이스 이중 버킷(DualInflowControlManager)에 클라이언트 유량제어를 추가한 관리자.
*
* <p>적용 방법: INFLOW.properties에 아래 한 줄 추가.
* <pre>inflow.control.bucket.className=com.eactive.eai.common.inflow.dual.ClientDualInflowControlManager</pre>
*/
@Component
public class ClientDualInflowControlManager extends DualInflowControlManager implements ClientDualBucket {
private static final Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT);
public static synchronized Bucket getBucket() {
return ApplicationContextProvider.getContext().getBean(ClientDualInflowControlManager.class);
}
private volatile Map<String, DualCustomBucket> clientBucketMap = new ConcurrentHashMap<>();
private final ConcurrentHashMap<String, TargetMetrics> clientMetricsMap = new ConcurrentHashMap<>();
// -------------------------------------------------------------------------
// Lifecycle
// -------------------------------------------------------------------------
@Override
public void start() throws LifecycleException {
super.start();
try {
initClients();
} catch (Exception e) {
throw new LifecycleException("ClientDualInflowControlManager init failed: " + e.getMessage());
}
}
// -------------------------------------------------------------------------
// 초기화 / 리로드
// -------------------------------------------------------------------------
private void initClients() throws Exception {
this.clientBucketMap = loadBucketMap(InflowType.CLIENT);
if (logger.isInfo()) logger.info("ClientDualInflowControlManager] initClients: " + clientBucketMap.size() + " buckets");
}
public synchronized void reloadClient() throws Exception {
initClients();
clientMetricsMap.clear();
}
public synchronized void reloadClient(String clientId) throws Exception {
reloadBucketMap(clientBucketMap, InflowType.CLIENT, clientId);
TargetMetrics m = clientMetricsMap.get(clientId);
if (m != null) m.reset();
}
public synchronized void removeClient(String clientId) {
clientBucketMap.remove(clientId);
clientMetricsMap.remove(clientId);
}
// -------------------------------------------------------------------------
// 클라이언트 메트릭 접근자
// -------------------------------------------------------------------------
public TargetMetrics getClientMetrics(String clientId) { return clientMetricsMap.get(clientId); }
public Map<String, TargetMetrics> getAllClientMetrics() { return Collections.unmodifiableMap(clientMetricsMap); }
public void resetClientMetrics(String clientId) { TargetMetrics m = clientMetricsMap.get(clientId); if (m != null) m.reset(); }
public void resetAllClientMetrics() { clientMetricsMap.values().forEach(TargetMetrics::reset); }
// -------------------------------------------------------------------------
// ClientDualBucket — 클라이언트 유량제어
// -------------------------------------------------------------------------
@Override
public String isClientPassDetail(String clientId) {
DualCustomBucket b = clientBucketMap.get(clientId);
if (b == null) return DualCustomBucket.RESULT_PASS;
String result = b.tryConsume();
recordMetrics(clientMetricsMap, clientId, result);
return result;
}
@Override
public InflowTargetVO getClientInflowThreshold(String clientId) {
DualCustomBucket b = clientBucketMap.get(clientId);
return (b == null) ? null : b.getInflowTargetVo();
}
// -------------------------------------------------------------------------
// 모니터링용 접근자
// -------------------------------------------------------------------------
public DualCustomBucket getClientBucket(String clientId) {
return clientBucketMap.get(clientId);
}
public Map<String, DualCustomBucket> getClientBucketMap() {
return clientBucketMap;
}
}
@@ -1,63 +0,0 @@
package com.eactive.eai.custom.inflow;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.eactive.eai.manage.inflow.TargetBucketStatusDTO;
/**
* 클라이언트 버킷 상태 모니터링 API.
*
* ClientDualInflowControlManager 가 활성화된 환경에서만 정상 응답한다.
*
* GET /manage/inflow/client/bucket-status → 전체 클라이언트 버킷 상태
* GET /manage/inflow/client/{clientId}/bucket-status → 특정 클라이언트 버킷 상태
*/
@RestController
@RequestMapping("/manage/inflow")
public class ClientInflowTargetBucketController {
@Autowired
private ClientInflowTargetBucketService bucketService;
@GetMapping("/client/bucket-status")
public ResponseEntity<?> getAllClientBucketStatus() {
List<TargetBucketStatusDTO> list = bucketService.getAllClientBucketStatus();
return ok(list);
}
@GetMapping("/client/{clientId}/bucket-status")
public ResponseEntity<?> getClientBucketStatus(@PathVariable String clientId) {
TargetBucketStatusDTO dto = bucketService.getClientBucketStatus(clientId);
return single(dto, "클라이언트를 찾을 수 없습니다: " + clientId);
}
private ResponseEntity<?> ok(List<TargetBucketStatusDTO> list) {
Map<String, Object> result = new HashMap<>();
result.put("success", true);
result.put("data", list);
result.put("count", list.size());
return ResponseEntity.ok(result);
}
private ResponseEntity<?> single(TargetBucketStatusDTO dto, String notFoundMsg) {
if (dto == null) {
Map<String, Object> error = new HashMap<>();
error.put("success", false);
error.put("message", notFoundMsg);
return ResponseEntity.ok(error);
}
Map<String, Object> result = new HashMap<>();
result.put("success", true);
result.put("data", dto);
return ResponseEntity.ok(result);
}
}
@@ -1,78 +0,0 @@
package com.eactive.eai.custom.inflow;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.springframework.stereotype.Service;
import com.eactive.eai.common.inflow.AbstractInflowControlManager;
import com.eactive.eai.common.inflow.InflowControlUtil;
import com.eactive.eai.common.inflow.InflowTargetVO;
import com.eactive.eai.common.inflow.dual.DualCustomBucket;
import com.eactive.eai.manage.inflow.GroupBucketStatusDTO;
import com.eactive.eai.manage.inflow.TargetBucketStatusDTO;
/**
* 클라이언트 버킷 상태 조회 서비스.
*
* ClientDualInflowControlManager 가 활성화된 경우에만 동작한다.
*/
@Service
public class ClientInflowTargetBucketService {
public TargetBucketStatusDTO getClientBucketStatus(String clientId) {
ClientDualInflowControlManager manager = getClientDualManager();
if (manager == null) return null;
DualCustomBucket bucket = manager.getClientBucket(clientId);
if (bucket == null) return null;
return toDto(clientId, "CLIENT", bucket);
}
public List<TargetBucketStatusDTO> getAllClientBucketStatus() {
ClientDualInflowControlManager manager = getClientDualManager();
if (manager == null) return new ArrayList<>();
return toDtoList("CLIENT", manager.getClientBucketMap());
}
protected ClientDualInflowControlManager getClientDualManager() {
AbstractInflowControlManager base = InflowControlUtil.getInflowControlManager();
return (base instanceof ClientDualInflowControlManager) ? (ClientDualInflowControlManager) base : null;
}
private TargetBucketStatusDTO toDto(String targetId, String targetType, DualCustomBucket bucket) {
InflowTargetVO vo = bucket.getInflowTargetVo();
List<GroupBucketStatusDTO.BucketInfo> buckets = new ArrayList<>();
if (vo.getThresholdPerSecond() > 0) {
long capacity = vo.getThresholdPerSecond();
long available = bucket.getPerSecondAvailableTokens();
if (available < 0) available = capacity;
buckets.add(new GroupBucketStatusDTO.BucketInfo(
"perSecond", capacity, Math.min(available, capacity), "SEC"));
}
if (vo.getThreshold() > 0) {
long capacity = vo.getThreshold();
long available = bucket.getThresholdAvailableTokens();
if (available < 0) available = capacity;
buckets.add(new GroupBucketStatusDTO.BucketInfo(
"threshold", capacity, Math.min(available, capacity), vo.getThresholdTimeUnit()));
}
TargetBucketStatusDTO dto = new TargetBucketStatusDTO();
dto.setTargetId(targetId);
dto.setTargetType(targetType);
dto.setActivate(vo.isActivate());
dto.setBuckets(buckets);
return dto;
}
private List<TargetBucketStatusDTO> toDtoList(String targetType, Map<String, DualCustomBucket> bucketMap) {
List<TargetBucketStatusDTO> result = new ArrayList<>();
for (Map.Entry<String, DualCustomBucket> entry : bucketMap.entrySet()) {
result.add(toDto(entry.getKey(), targetType, entry.getValue()));
}
return result;
}
}
@@ -1,94 +0,0 @@
package com.eactive.eai.custom.inflow;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.eactive.eai.manage.inflow.TargetMetricDTO;
/**
* 클라이언트 유량제어 메트릭 API.
*
* ClientDualInflowControlManager 가 활성화된 환경에서만 정상 응답한다.
*
* GET /manage/inflow/client/metric → 전체 클라이언트 메트릭
* GET /manage/inflow/client/{clientId}/metric → 특정 클라이언트 메트릭
* POST /manage/inflow/client/metric/reset → 전체 클라이언트 메트릭 초기화
* POST /manage/inflow/client/{clientId}/metric/reset → 특정 클라이언트 메트릭 초기화
*/
@RestController
@RequestMapping("/manage/inflow")
public class ClientInflowTargetMetricController {
@Autowired
private ClientInflowTargetMetricService metricService;
@GetMapping("/client/metric")
public ResponseEntity<?> getAllClientMetrics() {
List<TargetMetricDTO> list = metricService.getAllClientMetrics();
return okList(list);
}
@GetMapping("/client/{clientId}/metric")
public ResponseEntity<?> getClientMetric(@PathVariable String clientId) {
TargetMetricDTO dto = metricService.getClientMetric(clientId);
return okSingle(dto, "클라이언트를 찾을 수 없습니다: " + clientId);
}
@PostMapping("/client/metric/reset")
public ResponseEntity<?> resetAllClientMetrics() {
metricService.resetAllClientMetrics();
return okReset("전체 클라이언트 메트릭 초기화 완료");
}
@PostMapping("/client/{clientId}/metric/reset")
public ResponseEntity<?> resetClientMetric(@PathVariable String clientId) {
boolean ok = metricService.resetClientMetric(clientId);
return okResetSingle(ok, clientId, "클라이언트");
}
private ResponseEntity<?> okList(List<TargetMetricDTO> list) {
Map<String, Object> result = new HashMap<>();
result.put("success", true);
result.put("data", list);
result.put("count", list.size());
return ResponseEntity.ok(result);
}
private ResponseEntity<?> okSingle(TargetMetricDTO dto, String notFoundMsg) {
if (dto == null) {
Map<String, Object> error = new HashMap<>();
error.put("success", false);
error.put("message", notFoundMsg);
return ResponseEntity.ok(error);
}
Map<String, Object> result = new HashMap<>();
result.put("success", true);
result.put("data", dto);
return ResponseEntity.ok(result);
}
private ResponseEntity<?> okReset(String message) {
Map<String, Object> result = new HashMap<>();
result.put("success", true);
result.put("message", message);
return ResponseEntity.ok(result);
}
private ResponseEntity<?> okResetSingle(boolean ok, String targetId, String targetLabel) {
Map<String, Object> result = new HashMap<>();
result.put("success", ok);
result.put("message", ok
? "초기화 완료: " + targetId
: targetLabel + "를 찾을 수 없습니다: " + targetId);
return ResponseEntity.ok(result);
}
}
@@ -1,78 +0,0 @@
package com.eactive.eai.custom.inflow;
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Service;
import com.eactive.eai.common.inflow.AbstractInflowControlManager;
import com.eactive.eai.common.inflow.InflowControlUtil;
import com.eactive.eai.common.inflow.InflowTargetVO;
import com.eactive.eai.common.inflow.dual.DualInflowControlManager;
import com.eactive.eai.manage.inflow.TargetMetricDTO;
@Service
public class ClientInflowTargetMetricService {
public TargetMetricDTO getClientMetric(String clientId) {
ClientDualInflowControlManager manager = getClientDualManager();
if (manager == null) return null;
InflowTargetVO vo = manager.getClientInflowThreshold(clientId);
if (vo == null) return null;
return buildDTO(clientId, "CLIENT", vo, manager.getClientMetrics(clientId));
}
public List<TargetMetricDTO> getAllClientMetrics() {
ClientDualInflowControlManager manager = getClientDualManager();
if (manager == null) return new ArrayList<>();
List<TargetMetricDTO> result = new ArrayList<>();
for (String id : manager.getClientBucketMap().keySet()) {
TargetMetricDTO dto = getClientMetric(id);
if (dto != null) result.add(dto);
}
return result;
}
public boolean resetClientMetric(String clientId) {
ClientDualInflowControlManager manager = getClientDualManager();
if (manager == null) return false;
if (manager.getClientInflowThreshold(clientId) == null) return false;
manager.resetClientMetrics(clientId);
return true;
}
public void resetAllClientMetrics() {
ClientDualInflowControlManager manager = getClientDualManager();
if (manager != null) manager.resetAllClientMetrics();
}
protected ClientDualInflowControlManager getClientDualManager() {
AbstractInflowControlManager base = InflowControlUtil.getInflowControlManager();
return (base instanceof ClientDualInflowControlManager) ? (ClientDualInflowControlManager) base : null;
}
private TargetMetricDTO buildDTO(String targetId, String targetType,
InflowTargetVO vo,
DualInflowControlManager.TargetMetrics m) {
TargetMetricDTO dto = new TargetMetricDTO();
dto.setTargetId(targetId);
dto.setTargetType(targetType);
dto.setActivate(vo.isActivate());
if (m != null) {
long allowed = m.allowed.get();
long rejPs = m.rejectedPerSecond.get();
long rejTh = m.rejectedThreshold.get();
long total = allowed + rejPs + rejTh;
dto.setAllowed(allowed);
dto.setRejectedPerSecond(rejPs);
dto.setRejectedThreshold(rejTh);
dto.setTotalRequests(total);
dto.setRejectRatio(total > 0
? Math.round((double)(rejPs + rejTh) / total * 10000) / 100.0
: 0.0);
dto.setLastResetTime(m.lastResetTime);
}
return dto;
}
}
@@ -0,0 +1,123 @@
package com.eactive.eai.custom.kjb.adapter.http.client.filter;
import java.util.Arrays;
import java.util.Map;
import java.util.Properties;
import java.util.TreeMap;
import java.util.stream.Stream;
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.common.TransactionContextKeys;
import com.eactive.eai.common.property.PropManager;
import com.eactive.eai.common.util.Logger;
public class AllHeaderFilter implements HttpClientAdapterFilter, HttpAdapterServiceKey {
protected static Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);
public static final String PROPERTIES_GROUP_NAME = "HttpHeaderFilter";
public static final String HEADER_KEY_NAMES = "AllHeaderFilter.blackList";
String[] HttpHeaderRelayBlackList = {
"Content-Length",
"Transfer-Encoding",
"Host",
"Authorization",
"Accept",
"Host",
"Cookie",
"Connection",
"Keep-Alive",
"Proxy-Authenticate",
"Proxy-Authorization",
"TE",
"Trailer",
"Transfer-Encoding",
"Upgrade",
"Content-Type",
"Content-Encoding",
"Content-Language",
"Content-Location",
"Content-MD5",
"Expect",
};
@Override
public Object doPreFilter(String adptGrpName, String adptName, Properties prop, Object message, Properties tempProp)
throws Exception {
logger.debug("doPreFilter Processing Start.");
Properties filterHeaders = (Properties) tempProp.get("FILTERHEADER");
if (filterHeaders == null) {
filterHeaders = new Properties();
tempProp.put("FILTERHEADER", filterHeaders);
}
Map<String, String> inboundHeaderMap = getInboundHeaderProp(tempProp);
String propValue = PropManager.getInstance().getProperty(PROPERTIES_GROUP_NAME, HEADER_KEY_NAMES, "").trim();
String[] userSettingBlackList = propValue.split(",");
if( userSettingBlackList.length > 0 ) {
HttpHeaderRelayBlackList = Stream
.concat(Arrays.stream(HttpHeaderRelayBlackList), Arrays.stream(userSettingBlackList))
.toArray(String[]::new);
}
for(String keyName : inboundHeaderMap.keySet() ) {
if( StringUtils.equalsAnyIgnoreCase( keyName, HttpHeaderRelayBlackList ) ) {
logger.debug("Skip Processing Key ["+keyName+"], value ["+inboundHeaderMap.get(keyName)+"] in HttpHeaderRelayBlackList");
continue;
}
filterHeaders.put(keyName, inboundHeaderMap.get(keyName));
logger.debug("Processing Key ["+keyName+"], value ["+inboundHeaderMap.get(keyName)+"]");
}
logger.debug("doPreFilter Processing End.");
return message;
}
public Map<String, String> getInboundHeaderProp(Properties prop) {
Properties header = new Properties();
Map<String, String> inboundHeaderMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
try {
Object headerObj = prop.get(HttpAdapterServiceKey.INBOUND_HEADER);
if (headerObj instanceof Properties) { //tomcat
header = (Properties) headerObj;
for (String name : header.stringPropertyNames()) {
inboundHeaderMap.put(name, header.getProperty(name));
}
} else if (headerObj instanceof String) { //weblogic
String str = (String) headerObj;
str = str.substring(1, str.length() - 1);
// key=value 쌍 분리
String[] entries = str.split(",");
for (String entry : entries) {
String[] kv = entry.split("=", 2);
if (kv.length == 2) {
inboundHeaderMap.put(kv[0].trim(), kv[1].trim());
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return inboundHeaderMap;
}
@Override
public Object doPostFilter(String adptGrpName, String adptName, Properties prop, Object message,
Properties tempProp) throws Exception {
return message;
}
}
@@ -0,0 +1,118 @@
package com.eactive.eai.custom.kjb.adapter.http.client.filter;
import java.util.Arrays;
import java.util.Map;
import java.util.Properties;
import java.util.TreeMap;
import org.apache.commons.lang3.StringUtils;
import com.eactive.eai.adapter.http.dynamic.HttpAdapterServiceKey;
import com.eactive.eai.common.TransactionContextKeys;
import com.eactive.eai.common.property.PropManager;
import com.eactive.eai.common.util.Logger;
public class AsyncReponseFilter implements HttpClientAdapterFilter, HttpAdapterServiceKey {
protected static Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);
public static final String X_TRACE_ID = "partnerTraceId";
public static final String TRACE_ID = "traceId";
public static final String PROPERTIES_GROUP_NAME = "HttpHeaderFilter";
public static final String HEADER_KEY_NAMES = "AsyncReponseFilter";
@Override
public Object doPreFilter(String adptGrpName, String adptName, Properties prop, Object message, Properties tempProp)
throws Exception {
logger.debug("AsyncReponseFilter] PreFilter Processing Start!!");
String traceId = tempProp.getProperty(TransactionContextKeys.TRANSACTION_UUID, "");
Properties filterHeaders = (Properties) tempProp.get("FILTERHEADER");
if (filterHeaders == null) {
filterHeaders = new Properties();
tempProp.put("FILTERHEADER", filterHeaders);
}
String propValue = PropManager.getInstance().getProperty(PROPERTIES_GROUP_NAME, HEADER_KEY_NAMES, "").trim();
String[] headerKeyNames = propValue.split(",");
headerKeyNames = Arrays.stream(headerKeyNames)
.map(String::trim)
.toArray(String[]::new);
try {
Map<String, String> inboundHeaderMap = getInboundHeaderProp(tempProp);
if (!inboundHeaderMap.isEmpty()) {
if (inboundHeaderMap.containsKey(X_TRACE_ID)) {
filterHeaders.put(X_TRACE_ID, inboundHeaderMap.get(X_TRACE_ID));
logger.debug("AsyncReponseFilter] Processing Key ["+X_TRACE_ID+"], value ["+inboundHeaderMap.get(X_TRACE_ID)+"]");
}
if (inboundHeaderMap.containsKey(TransactionContextKeys.X_LOAN_TOKEN)) {
filterHeaders.put(TransactionContextKeys.X_LOAN_TOKEN, inboundHeaderMap.get(TransactionContextKeys.X_LOAN_TOKEN));
logger.debug("AsyncReponseFilter] Processing Key ["+TransactionContextKeys.X_LOAN_TOKEN+"], value ["+inboundHeaderMap.get(TransactionContextKeys.X_LOAN_TOKEN)+"]");
}
}
filterHeaders.setProperty(TRACE_ID, traceId);
logger.debug("AsyncReponseFilter] Processing Key ["+TRACE_ID+"], value ["+traceId+"]");
for (String keyName : inboundHeaderMap.keySet()) {
if (StringUtils.equalsAnyIgnoreCase(keyName, headerKeyNames)) {
String value = inboundHeaderMap.get(keyName);
filterHeaders.put(keyName, value);
logger.debug("AsyncReponseFilter] Processing Key [" + keyName + "], value [" + value + "]");
}
}
} catch (Exception e) {
logger.error(e.getMessage());
}
return message;
}
public Map<String, String> getInboundHeaderProp(Properties prop) {
Properties header = new Properties();
Map<String, String> inboundHeaderMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
try {
Object headerObj = prop.get(HttpAdapterServiceKey.INBOUND_HEADER);
if (headerObj instanceof Properties) { //tomcat
header = (Properties) headerObj;
for (String name : header.stringPropertyNames()) {
inboundHeaderMap.put(name, header.getProperty(name));
}
} else if (headerObj instanceof String) { //weblogic
String str = (String) headerObj;
str = str.substring(1, str.length() - 1);
// key=value 쌍 분리
String[] entries = str.split(",");
for (String entry : entries) {
String[] kv = entry.split("=", 2);
if (kv.length == 2) {
inboundHeaderMap.put(kv[0].trim(), kv[1].trim());
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return inboundHeaderMap;
}
@Override
public Object doPostFilter(String adptGrpName, String adptName, Properties prop, Object message,
Properties tempProp) throws Exception {
return message;
}
}
@@ -0,0 +1,115 @@
package com.eactive.eai.custom.kjb.adapter.http.client.filter;
import java.util.concurrent.ConcurrentHashMap;
import com.eactive.eai.common.util.Logger;
public class HttpClient5AdapterFilterFactory {
static Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);
static String basePackage = "com.eactive.eai.custom.kjb.adapter.http.client.filter";
private static ConcurrentHashMap<String, HttpClientAdapterFilter> h = new ConcurrentHashMap<>();
private static ConcurrentHashMap<String, HttpClientAdapterExceptionFilter> eh = new ConcurrentHashMap<>();
private HttpClient5AdapterFilterFactory() {
throw new IllegalStateException("Utility class");
}
public static HttpClientAdapterFilter createFilter(String type) {
if (h.containsKey(type)) {
return h.get(type);
}
HttpClientAdapterFilter filter = null;
switch (HttpClient5AdapterFilterType.getValue(type)) {
case SIMPLEFRAMEWORK:
filter = new SimpleFrameworkFilter();
break;
case SIMPLEFRAMEWORKBODY:
filter = new SimpleframeworkBodyFilter();
break;
case JBOBP:
filter = new JBOBPFilter();
break;
case KFTCFACE:
filter = new KFTCFaceFilter();
break;
case KFTCP2P:
filter = new KFTCP2PFilter();
break;
case KAKAOBANK:
filter = new KAKAOBankFilter();
break;
case NAVERFIN:
filter = new NAVERFinFilter();
break;
case NICECREDIT:
filter = new NICECreditFilter();
break;
case TRACEBODY:
filter = new TraceBodyFilter();
break;
case TOSSBANK:
filter = new TOSSBankFilter();
break;
case ASYNCRESPONSE:
filter = new AsyncReponseFilter();
break;
case ALLHEADER:
filter = new AllHeaderFilter();
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 HttpClientAdapterFilter classForName(String type) {
try {
Class<?> cl = Class.forName(type);
return (HttpClientAdapterFilter) cl.newInstance();
} catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {
logger.error("Cannot create a HttpClientAdapterFilter. - {}", type);
return null;
}
}
public static HttpClientAdapterExceptionFilter createExceptionFilter(String type) {
if (eh.containsKey(type)) {
return eh.get(type);
}
HttpClientAdapterExceptionFilter filter = classForExceptionFilterName(type);
if (filter == null) {
filter = classForExceptionFilterName(basePackage + "." + type);
}
if (filter != null) {
eh.put(type, filter);
return filter;
} else {
return null;
}
}
private static HttpClientAdapterExceptionFilter classForExceptionFilterName(String type) {
try {
Class<?> cl = Class.forName(type);
return (HttpClientAdapterExceptionFilter) cl.newInstance();
} catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {
logger.error("Cannot create a HttpClientAdapterExceptionFilter. - {}", type);
return null;
}
}
}
@@ -0,0 +1,27 @@
package com.eactive.eai.custom.kjb.adapter.http.client.filter;
public enum HttpClient5AdapterFilterType {
SIMPLEFRAMEWORK,
SIMPLEFRAMEWORKBODY,
JBOBP,
KFTCFACE,
KFTCP2P,
KAKAOBANK,
NAVERFIN,
NICECREDIT,
TOSSBANK,
TRACEBODY,
ASYNCRESPONSE,
ALLHEADER,
UNKNOWN;
public static HttpClient5AdapterFilterType getValue(String type) {
try {
return valueOf(type.toUpperCase());
} catch (NullPointerException e) {
return UNKNOWN;
} catch (Exception e) {
return UNKNOWN;
}
}
}
@@ -0,0 +1,62 @@
package com.eactive.eai.custom.kjb.adapter.http.client.filter;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import com.eactive.eai.common.util.Logger;
public abstract class HttpClientAdapterBaseFilter implements HttpClientAdapterFilter {
protected static Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);
protected String filterName = "";
protected List<HttpClientAdapterFilter> filters;
protected HttpClientAdapterBaseFilter(String filterName) {
this.filterName = filterName;
filters = new ArrayList<>();
logger.info("{}] filter created.", filterName);
}
protected void addFilter(HttpClientAdapterFilter filter) {
filters.add(filter);
logger.info("{}] sub filter({}) added. filters= {}", filterName, filter, filters);
}
@Override
public Object doPreFilter(String adptGrpName, String adptName, Properties prop, Object message, Properties tempProp)
throws Exception {
try {
Object filteredMessage = message;
for(HttpClientAdapterFilter subFilter : filters) {
logger.debug("{}] sub filter({}) doPreFilter start.", filterName, subFilter);
filteredMessage = subFilter.doPreFilter(adptGrpName, adptName, prop, filteredMessage, tempProp);
logger.debug("{}] sub filter({}) doPreFilter end.", filterName, subFilter);
}
return filteredMessage;
} catch (Exception e) {
logger.error(this.filterName + "] doPreFilter error.", e);
throw e;
}
}
@Override
public Object doPostFilter(String adptGrpName, String adptName, Properties prop, Object message,
Properties tempProp) throws Exception {
try {
Object filteredMessage = message;
for(HttpClientAdapterFilter subFilter : filters) {
logger.debug("{}] sub filter({}) doPostFilter start.", filterName, subFilter);
filteredMessage = subFilter.doPostFilter(adptGrpName, adptName, prop, filteredMessage, tempProp);
logger.debug("{}] sub filter({}) doPostFilter end.", filterName, subFilter);
}
return filteredMessage;
} catch (Exception e) {
logger.error(this.filterName + "] doPostFilter error.", e);
throw e;
}
}
}
@@ -0,0 +1,10 @@
package com.eactive.eai.custom.kjb.adapter.http.client.filter;
import java.util.Properties;
public interface HttpClientAdapterExceptionFilter {
public Object doExceptionFilter(String adptGrpName, String adptName, Properties prop, Object message,
Properties tempProp, Exception e) throws Exception;
}
@@ -0,0 +1,12 @@
package com.eactive.eai.custom.kjb.adapter.http.client.filter;
import java.util.Properties;
public interface HttpClientAdapterFilter {
public Object doPreFilter(String adptGrpName, String adptName, Properties prop, Object message, Properties tempProp)
throws Exception;
public Object doPostFilter(String adptGrpName, String adptName, Properties prop, Object message,
Properties tempProp) throws Exception;
}
@@ -0,0 +1,35 @@
package com.eactive.eai.custom.kjb.adapter.http.client.filter;
import java.util.Properties;
import lombok.Getter;
public class HttpClientAdapterFilterException extends RuntimeException {
private static final long serialVersionUID = -1208983360831093751L;
@Getter
private final String code;
@Getter
private final Properties prop;
@Getter
private final Properties tempProp;
public HttpClientAdapterFilterException(String code, String msg, Properties prop, Properties tmepProp) {
super(msg);
this.code = code;
this.prop = prop;
this.tempProp = tmepProp;
}
public HttpClientAdapterFilterException(String code, String msg, Properties prop, Properties tempProp, Throwable cause) {
super(msg, cause);
this.code = code;
this.prop = prop;
this.tempProp = tempProp;
}
@Override
public String toString() {
return "HttpClientAdapterFilterException [code=" + code + ", prop=" + prop + ", tempProp=" + tempProp + ", parent=" + super.toString() + "]";
}
}
@@ -0,0 +1,106 @@
package com.eactive.eai.custom.kjb.adapter.http.client.filter;
import java.util.Map;
import java.util.Properties;
import java.util.TreeMap;
import org.apache.commons.lang3.StringUtils;
import com.eactive.eai.adapter.http.dynamic.HttpAdapterServiceKey;
import com.eactive.eai.common.TransactionContextKeys;
import com.eactive.eai.common.property.PropGroupVO;
import com.eactive.eai.common.property.PropManager;
import com.eactive.eai.common.util.Logger;
public class JBOBPFilter implements HttpClientAdapterFilter, HttpAdapterServiceKey {
protected static Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);
public static final String KJB_HEADER = "KJB_Header";
public static final String X_OBP_PARTNERCODE = "x-obp-partnercode";
public static final String X_OBP_TXID = "x-obp-txid";
public static final String X_OBP_TRUST_SYSTEM = "x-obp-trust-system";
String systemTrustKey = "APIMGW-0001-QVBJTUdXLTAwMDE=";
@Override
public Object doPreFilter(String adptGrpName, String adptName, Properties prop, Object message, Properties tempProp)
throws Exception {
logger.debug("JBOBPFilter] PreFilter Processing Start!!");
PropGroupVO propGroupVo = PropManager.getInstance().getPropGroupVO(KJB_HEADER);
if (propGroupVo != null) systemTrustKey = propGroupVo.getProperty(X_OBP_TRUST_SYSTEM, systemTrustKey);
Properties filterHeaders = (Properties) tempProp.get("FILTERHEADER");
if (filterHeaders == null) {
filterHeaders = new Properties();
tempProp.put("FILTERHEADER", filterHeaders);
}
try {
String obpPartnerCode = "000000-00";
Map<String, String> inboundHeaderMap = getInboundHeaderProp(tempProp);
if (!inboundHeaderMap.isEmpty()) {
if (inboundHeaderMap.containsKey(X_OBP_PARTNERCODE)) {
filterHeaders.put(X_OBP_PARTNERCODE, inboundHeaderMap.get(X_OBP_PARTNERCODE));
logger.debug("JBOBPFilter] Processing Key ["+X_OBP_PARTNERCODE+"], value ["+inboundHeaderMap.get(X_OBP_PARTNERCODE)+"]");
} else {
filterHeaders.put(X_OBP_PARTNERCODE, obpPartnerCode);
logger.debug("JBOBPFilter] Processing Key ["+X_OBP_PARTNERCODE+"], value ["+obpPartnerCode+"]");
}
} else {
filterHeaders.put(X_OBP_PARTNERCODE, obpPartnerCode);
logger.debug("JBOBPFilter] Processing Key ["+X_OBP_PARTNERCODE+"], value ["+obpPartnerCode+"]");
}
String uuid = tempProp.getProperty(TransactionContextKeys.TRANSACTION_UUID, "");
if (StringUtils.isNotBlank(uuid)) {
filterHeaders.setProperty(X_OBP_TXID, uuid); // JB OBP Framework용
logger.debug("JBOBPFilter] Processing Key ["+X_OBP_TXID+"], value ["+uuid+"]");
}
if (StringUtils.isNotBlank(systemTrustKey)) {
filterHeaders.setProperty(X_OBP_TRUST_SYSTEM, systemTrustKey); // JB OBP Framework용
logger.debug("JBOBPFilter] Processing Key ["+X_OBP_TRUST_SYSTEM+"], value ["+systemTrustKey+"]");
}
} catch (Exception e) {
logger.error(e.getMessage());
}
return message;
}
public Map<String, String> getInboundHeaderProp(Properties prop) {
Properties header = new Properties();
Map<String, String> inboundHeaderMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
try {
Object headerObj = prop.get(HttpAdapterServiceKey.INBOUND_HEADER);
if (headerObj instanceof Properties) { //tomcat
header = (Properties) headerObj;
for (String name : header.stringPropertyNames()) {
inboundHeaderMap.put(name, header.getProperty(name));
}
} else if (headerObj instanceof String) { //weblogic
String str = (String) headerObj;
str = str.substring(1, str.length() - 1);
// key=value 쌍 분리
String[] entries = str.split(",");
for (String entry : entries) {
String[] kv = entry.split("=", 2);
if (kv.length == 2) {
inboundHeaderMap.put(kv[0].trim(), kv[1].trim());
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return inboundHeaderMap;
}
@Override
public Object doPostFilter(String adptGrpName, String adptName, Properties prop, Object message,
Properties tempProp) throws Exception {
return message;
}
}
@@ -0,0 +1,135 @@
package com.eactive.eai.custom.kjb.adapter.http.client.filter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.Properties;
import java.util.TreeMap;
import com.eactive.eai.adapter.http.dynamic.HttpAdapterServiceKey;
import com.eactive.eai.common.property.PropGroupVO;
import com.eactive.eai.common.property.PropManager;
import com.eactive.eai.common.util.Logger;
public class KAKAOBankFilter implements HttpClientAdapterFilter, HttpAdapterServiceKey {
protected static Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);
public static final String KJB_HEADER = "KJB_Header";
public static final String PROP_PREFIX = "kakaobank";
public static final String X_KKB_PARTNER_CODE = "X-KKB-PARTNER-CODE";
public static final String X_KKB_API_NAME = "X-KKB-API-NAME";
public static final String X_KKB_API_TX_ID = "X-KKB-API-TX-ID";
public static final String X_KKB_TX_TIME = "X-KKB-TX-TIME";
public static final String X_KKB_CMPR_MGMT_NO = "X-KKB-CMPR-MGMT-NO";
@Override
public Object doPreFilter(String adptGrpName, String adptName, Properties prop, Object message, Properties tempProp)
throws Exception {
logger.debug("KAKAOBankFilter] PreFilter Processing Start!!");
Object returnMessage = message;
String partnerCode = "KJB";
String apiName = "status_change";
String apiTxId = "0";
String txTime = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
String cmprMgmtNo = "0";
PropGroupVO propGroupVo = PropManager.getInstance().getPropGroupVO(KJB_HEADER);
if (propGroupVo != null) {
partnerCode = propGroupVo.getProperty(PROP_PREFIX + "." + X_KKB_PARTNER_CODE);
apiName = propGroupVo.getProperty(PROP_PREFIX + "." + X_KKB_API_NAME);
apiTxId = propGroupVo.getProperty(PROP_PREFIX + "." + X_KKB_API_TX_ID);
cmprMgmtNo = propGroupVo.getProperty(PROP_PREFIX + "." + X_KKB_CMPR_MGMT_NO);
}
Properties filterHeaders = (Properties) tempProp.get("FILTERHEADER");
if (filterHeaders == null) {
filterHeaders = new Properties();
tempProp.put("FILTERHEADER", filterHeaders);
}
try {
Map<String, String> inboundHeaderMap = getInboundHeaderProp(tempProp);
if (!inboundHeaderMap.isEmpty()) {
if (inboundHeaderMap.containsKey(X_KKB_PARTNER_CODE)) {
filterHeaders.put(X_KKB_PARTNER_CODE, inboundHeaderMap.get(X_KKB_PARTNER_CODE));
} else {
filterHeaders.put(X_KKB_PARTNER_CODE, partnerCode);
}
if (inboundHeaderMap.containsKey(X_KKB_API_NAME)) {
filterHeaders.put(X_KKB_API_NAME, inboundHeaderMap.get(X_KKB_API_NAME));
} else {
filterHeaders.put(X_KKB_API_NAME, apiName);
}
if (inboundHeaderMap.containsKey(X_KKB_API_TX_ID)) {
filterHeaders.put(X_KKB_API_TX_ID, inboundHeaderMap.get(X_KKB_API_TX_ID));
} else {
filterHeaders.put(X_KKB_API_TX_ID, apiTxId);
}
if (inboundHeaderMap.containsKey(X_KKB_TX_TIME)) {
filterHeaders.put(X_KKB_TX_TIME, inboundHeaderMap.get(X_KKB_TX_TIME));
} else {
filterHeaders.put(X_KKB_TX_TIME, txTime);
}
if (inboundHeaderMap.containsKey(X_KKB_CMPR_MGMT_NO)) {
filterHeaders.put(X_KKB_CMPR_MGMT_NO, inboundHeaderMap.get(X_KKB_CMPR_MGMT_NO));
} else {
filterHeaders.put(X_KKB_CMPR_MGMT_NO, cmprMgmtNo);
}
} else {
filterHeaders.put(X_KKB_PARTNER_CODE, partnerCode);
filterHeaders.put(X_KKB_API_NAME, apiName);
filterHeaders.put(X_KKB_API_TX_ID, apiTxId);
filterHeaders.put(X_KKB_TX_TIME, txTime);
filterHeaders.put(X_KKB_CMPR_MGMT_NO, cmprMgmtNo);
}
logger.debug("KAKAOBankFilter] Processing Key ["+X_KKB_PARTNER_CODE+"], value ["+filterHeaders.getProperty(X_KKB_PARTNER_CODE)+"]");
logger.debug("KAKAOBankFilter] Processing Key ["+X_KKB_API_NAME+"], value ["+filterHeaders.getProperty(X_KKB_API_NAME)+"]");
logger.debug("KAKAOBankFilter] Processing Key ["+X_KKB_API_TX_ID+"], value ["+filterHeaders.getProperty(X_KKB_API_TX_ID)+"]");
logger.debug("KAKAOBankFilter] Processing Key ["+X_KKB_TX_TIME+"], value ["+filterHeaders.getProperty(X_KKB_TX_TIME)+"]");
logger.debug("KAKAOBankFilter] Processing Key ["+X_KKB_CMPR_MGMT_NO+"], value ["+filterHeaders.getProperty(X_KKB_CMPR_MGMT_NO)+"]");
} catch (Exception e) {
logger.error(e.getMessage());
}
return returnMessage;
}
public Map<String, String> getInboundHeaderProp(Properties prop) {
Properties header = new Properties();
Map<String, String> inboundHeaderMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
try {
Object headerObj = prop.get(HttpAdapterServiceKey.INBOUND_HEADER);
if (headerObj instanceof Properties) { //tomcat
header = (Properties) headerObj;
for (String name : header.stringPropertyNames()) {
inboundHeaderMap.put(name, header.getProperty(name));
}
} else if (headerObj instanceof String) { //weblogic
String str = (String) headerObj;
str = str.substring(1, str.length() - 1);
// key=value 쌍 분리
String[] entries = str.split(",");
for (String entry : entries) {
String[] kv = entry.split("=", 2);
if (kv.length == 2) {
inboundHeaderMap.put(kv[0].trim(), kv[1].trim());
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return inboundHeaderMap;
}
@Override
public Object doPostFilter(String adptGrpName, String adptName, Properties prop, Object message,
Properties tempProp) throws Exception {
return message;
}
}
@@ -0,0 +1,154 @@
package com.eactive.eai.custom.kjb.adapter.http.client.filter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.Properties;
import java.util.TreeMap;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
import com.eactive.eai.adapter.AdapterGroupVO;
import com.eactive.eai.adapter.AdapterManager;
import com.eactive.eai.adapter.http.dynamic.HttpAdapterServiceKey;
import com.eactive.eai.common.property.PropGroupVO;
import com.eactive.eai.common.property.PropManager;
import com.eactive.eai.common.server.EAIServerManager;
import com.eactive.eai.common.util.Logger;
public class KFTCFaceFilter implements HttpClientAdapterFilter, HttpAdapterServiceKey {
protected static Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);
public static final String KJB_HEADER = "KJB_Header";
public static final String PROP_PREFIX = "kftcface";
public static final String H_CLIENT_ID = "Client-Id";
public static final String B_ORG_CODE = "org_code";
public static final String B_TRANSACTION_ID = "transaction_id";
public static final String B_REQUEST_DATETIME = "request_datetime";
public static String instId = null;
static {
EAIServerManager eaiServerManager = EAIServerManager.getInstance();
instId = eaiServerManager.getInstId();
}
@SuppressWarnings({ "unchecked" })
@Override
public Object doPreFilter(String adptGrpName, String adptName, Properties prop, Object message, Properties tempProp)
throws Exception {
logger.debug("KFTCFaceFilter] PreFilter Processing Start!!");
JSONObject returnMessage = null;
String clientId = "";
String orgCode = "034"; // 광주은행 행코드
PropGroupVO propGroupVo = PropManager.getInstance().getPropGroupVO(KJB_HEADER);
if (propGroupVo != null) {
if (StringUtils.isEmpty(clientId))
clientId = propGroupVo.getProperty(PROP_PREFIX + "." + H_CLIENT_ID);
orgCode = propGroupVo.getProperty(PROP_PREFIX + "." + B_ORG_CODE);
}
String request_datetime = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
String reqDate = new SimpleDateFormat("yyMMdd").format(new Date());
String reqTime = new SimpleDateFormat("HHmmss").format(new Date());
// 기관코드(3) + 요청일자(6) -- 금결원 표준화 항목, 고정 9자리.
// 요청시간(6) + 인스턴스ID(2) + RandomString(3) -- 기관별 생성 거래고유번호(11자리)
// 기관코드(3) + 요청일자(6) + 요청시간(6) + 인스턴스ID(2) + RandomString(3) -- 20자리.
String transaction_id = orgCode + reqDate + reqTime + instId + RandomStringUtils.random(3, true, true).toUpperCase();
Properties filterHeaders = (Properties) tempProp.get("FILTERHEADER");
if (filterHeaders == null) {
filterHeaders = new Properties();
tempProp.put("FILTERHEADER", filterHeaders);
}
try {
Map<String, String> inboundHeaderMap = getInboundHeaderProp(tempProp);
if (!inboundHeaderMap.isEmpty()) {
if (inboundHeaderMap.containsKey(H_CLIENT_ID)) {
filterHeaders.put(H_CLIENT_ID, inboundHeaderMap.get(H_CLIENT_ID));
logger.debug("KFTCFaceFilter] Processing Key ["+H_CLIENT_ID+"], value ["+inboundHeaderMap.get(H_CLIENT_ID)+"]");
} else {
filterHeaders.put(H_CLIENT_ID, clientId);
logger.debug("KFTCFaceFilter] Processing Key ["+H_CLIENT_ID+"], value ["+clientId+"]");
}
} else {
filterHeaders.setProperty(H_CLIENT_ID, clientId);
logger.debug("KFTCFaceFilter] Processing Key ["+H_CLIENT_ID+"], value ["+clientId+"]");
}
} catch (Exception e) {
logger.error(e.getMessage());
}
AdapterManager adapterManager = AdapterManager.getInstance();
AdapterGroupVO adptGrpVO = adapterManager.getAdapterGroupVO(adptGrpName);
String encode = adptGrpVO.getMessageEncode();
if (message == null) {
returnMessage = new JSONObject();
} else {
if (message instanceof JSONObject) {
returnMessage = (JSONObject) message;
} else if (message instanceof String) {
returnMessage = parseJson((String) message);
} else if (message instanceof byte[]) {
returnMessage = parseJson(new String((byte[]) message, encode));
}
}
returnMessage.put(B_ORG_CODE, orgCode);
returnMessage.put(B_TRANSACTION_ID, transaction_id);
returnMessage.put(B_REQUEST_DATETIME, request_datetime);
return returnMessage.toJSONString();
}
private JSONObject parseJson(String message) throws Exception {
if (message == null) {
return null;
}
return (JSONObject) JSONValue.parse(message);
}
public Map<String, String> getInboundHeaderProp(Properties prop) {
Properties header = new Properties();
Map<String, String> inboundHeaderMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
try {
Object headerObj = prop.get(HttpAdapterServiceKey.INBOUND_HEADER);
if (headerObj instanceof Properties) { //tomcat
header = (Properties) headerObj;
for (String name : header.stringPropertyNames()) {
inboundHeaderMap.put(name, header.getProperty(name));
}
} else if (headerObj instanceof String) { //weblogic
String str = (String) headerObj;
str = str.substring(1, str.length() - 1);
// key=value 쌍 분리
String[] entries = str.split(",");
for (String entry : entries) {
String[] kv = entry.split("=", 2);
if (kv.length == 2) {
inboundHeaderMap.put(kv[0].trim(), kv[1].trim());
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return inboundHeaderMap;
}
@Override
public Object doPostFilter(String adptGrpName, String adptName, Properties prop, Object message,
Properties tempProp) throws Exception {
return message;
}
}
@@ -0,0 +1,122 @@
package com.eactive.eai.custom.kjb.adapter.http.client.filter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.Properties;
import java.util.TreeMap;
import org.apache.commons.lang3.RandomStringUtils;
import com.eactive.eai.adapter.http.dynamic.HttpAdapterServiceKey;
import com.eactive.eai.common.property.PropGroupVO;
import com.eactive.eai.common.property.PropManager;
import com.eactive.eai.common.server.EAIServerManager;
import com.eactive.eai.common.util.Logger;
public class KFTCP2PFilter implements HttpClientAdapterFilter, HttpAdapterServiceKey {
protected static Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);
public static final String KJB_HEADER = "KJB_Header";
public static final String PROP_PREFIX = "kftcp2p";
public static final String H_ORG_CODE = "org_code";
public static final String H_TRX_NO = "api_trx_no";
public static final String H_TRX_DTM = "api_trx_dtm";
public static String instId = null;
static {
EAIServerManager eaiServerManager = EAIServerManager.getInstance();
instId = eaiServerManager.getInstId();
}
@Override
public Object doPreFilter(String adptGrpName, String adptName, Properties prop, Object message, Properties tempProp)
throws Exception {
logger.debug("KFTCP2PFilter] PreFilter Processing Start!!");
String orgCode = "D210400012"; // 광주은행 개발 기관코드(운영: K210800020)
PropGroupVO propGroupVo = PropManager.getInstance().getPropGroupVO(KJB_HEADER);
if (propGroupVo != null) {
orgCode = propGroupVo.getProperty(PROP_PREFIX + "." + H_ORG_CODE);
}
String apiTrxDtm = new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date());
String apiTrxTm = new SimpleDateFormat("HHmmss").format(new Date());
// 기관코드(10) + 인스턴스ID(2) + 일시(6) + RandomString(2) -- 총 20자리
String apiTrxNo = orgCode + instId + apiTrxTm + RandomStringUtils.random(2, true, true).toUpperCase();
Properties filterHeaders = (Properties) tempProp.get("FILTERHEADER");
if (filterHeaders == null) {
filterHeaders = new Properties();
tempProp.put("FILTERHEADER", filterHeaders);
}
try {
Map<String, String> inboundHeaderMap = getInboundHeaderProp(tempProp);
if (!inboundHeaderMap.isEmpty()) {
if (inboundHeaderMap.containsKey(H_TRX_NO)) {
filterHeaders.put(H_TRX_NO, inboundHeaderMap.get(H_TRX_NO));
logger.debug("KFTCP2PFilter] Processing Key ["+H_TRX_NO+"], value ["+inboundHeaderMap.get(H_TRX_NO)+"]");
} else {
filterHeaders.put(H_TRX_NO, apiTrxNo);
logger.debug("KFTCP2PFilter] Processing Key ["+H_TRX_NO+"], value ["+apiTrxNo+"]");
}
if (inboundHeaderMap.containsKey(H_TRX_DTM)) {
filterHeaders.put(H_TRX_DTM, inboundHeaderMap.get(H_TRX_DTM));
logger.debug("KFTCP2PFilter] Processing Key ["+H_TRX_DTM+"], value ["+inboundHeaderMap.get(H_TRX_DTM)+"]");
} else {
filterHeaders.put(H_TRX_DTM, apiTrxDtm);
logger.debug("KFTCP2PFilter] Processing Key ["+H_TRX_DTM+"], value ["+apiTrxDtm+"]");
}
} else {
filterHeaders.put(H_TRX_NO, apiTrxNo);
filterHeaders.put(H_TRX_DTM, apiTrxDtm);
}
logger.debug("KFTCP2PFilter] Processing Key ["+H_TRX_NO+"], value ["+filterHeaders.getProperty(H_TRX_NO)+"]");
logger.debug("KFTCP2PFilter] Processing Key ["+H_TRX_DTM+"], value ["+filterHeaders.getProperty(H_TRX_DTM)+"]");
} catch (Exception e) {
logger.error(e.getMessage());
}
return message;
}
public Map<String, String> getInboundHeaderProp(Properties prop) {
Properties header = new Properties();
Map<String, String> inboundHeaderMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
try {
Object headerObj = prop.get(HttpAdapterServiceKey.INBOUND_HEADER);
if (headerObj instanceof Properties) { //tomcat
header = (Properties) headerObj;
for (String name : header.stringPropertyNames()) {
inboundHeaderMap.put(name, header.getProperty(name));
}
} else if (headerObj instanceof String) { //weblogic
String str = (String) headerObj;
str = str.substring(1, str.length() - 1);
// key=value 쌍 분리
String[] entries = str.split(",");
for (String entry : entries) {
String[] kv = entry.split("=", 2);
if (kv.length == 2) {
inboundHeaderMap.put(kv[0].trim(), kv[1].trim());
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return inboundHeaderMap;
}
@Override
public Object doPostFilter(String adptGrpName, String adptName, Properties prop, Object message,
Properties tempProp) throws Exception {
return message;
}
}
@@ -0,0 +1,107 @@
package com.eactive.eai.custom.kjb.adapter.http.client.filter;
import java.util.Map;
import java.util.Properties;
import java.util.TreeMap;
import com.eactive.eai.adapter.http.dynamic.HttpAdapterServiceKey;
import com.eactive.eai.common.property.PropGroupVO;
import com.eactive.eai.common.property.PropManager;
import com.eactive.eai.common.util.Logger;
public class NAVERFinFilter implements HttpClientAdapterFilter, HttpAdapterServiceKey {
protected static Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);
public static final String KJB_HEADER = "KJB_Header";
public static final String PROP_PREFIX = "naverfin";
public static final String X_PARTNER_ID = "X-Partner-Id";
public static final String X_FINTECH_ID = "X-Fintech-Id";
@Override
public Object doPreFilter(String adptGrpName, String adptName, Properties prop, Object message, Properties tempProp)
throws Exception {
logger.debug("NAVERFinFilter] PreFilter Processing Start!!");
String partnerID = "r0jtqwC9gAW5";
String fintechId = "NF";
PropGroupVO propGroupVo = PropManager.getInstance().getPropGroupVO(KJB_HEADER);
if (propGroupVo != null) {
partnerID = propGroupVo.getProperty(PROP_PREFIX + "." + X_PARTNER_ID);
fintechId = propGroupVo.getProperty(PROP_PREFIX + "." + X_FINTECH_ID);
}
Properties filterHeaders = (Properties) tempProp.get("FILTERHEADER");
if (filterHeaders == null) {
filterHeaders = new Properties();
tempProp.put("FILTERHEADER", filterHeaders);
}
try {
Map<String, String> inboundHeaderMap = getInboundHeaderProp(tempProp);
if (!inboundHeaderMap.isEmpty()) {
if (inboundHeaderMap.containsKey(X_PARTNER_ID)) {
filterHeaders.put(X_PARTNER_ID, inboundHeaderMap.get(X_PARTNER_ID));
logger.debug("NAVERFinFilter] Processing Key ["+X_PARTNER_ID+"], value ["+inboundHeaderMap.get(X_PARTNER_ID)+"]");
} else {
filterHeaders.put(X_PARTNER_ID, partnerID);
logger.debug("NAVERFinFilter] Processing Key ["+X_PARTNER_ID+"], value ["+partnerID+"]");
}
if (inboundHeaderMap.containsKey(X_FINTECH_ID)) {
filterHeaders.put(X_FINTECH_ID, inboundHeaderMap.get(X_FINTECH_ID));
logger.debug("NAVERFinFilter] Processing Key ["+X_FINTECH_ID+"], value ["+inboundHeaderMap.get(X_FINTECH_ID)+"]");
} else {
filterHeaders.put(X_FINTECH_ID, fintechId);
logger.debug("NAVERFinFilter] Processing Key ["+X_FINTECH_ID+"], value ["+fintechId+"]");
}
} else {
filterHeaders.put(X_PARTNER_ID, partnerID);
filterHeaders.put(X_FINTECH_ID, fintechId);
}
logger.debug("NAVERFinFilter] Processing Key ["+X_PARTNER_ID+"], value ["+filterHeaders.getProperty(X_PARTNER_ID)+"]");
logger.debug("NAVERFinFilter] Processing Key ["+X_FINTECH_ID+"], value ["+filterHeaders.getProperty(X_FINTECH_ID)+"]");
} catch (Exception e) {
logger.error(e.getMessage());
}
return message;
}
public Map<String, String> getInboundHeaderProp(Properties prop) {
Properties header = new Properties();
Map<String, String> inboundHeaderMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
try {
Object headerObj = prop.get(HttpAdapterServiceKey.INBOUND_HEADER);
if (headerObj instanceof Properties) { //tomcat
header = (Properties) headerObj;
for (String name : header.stringPropertyNames()) {
inboundHeaderMap.put(name, header.getProperty(name));
}
} else if (headerObj instanceof String) { //weblogic
String str = (String) headerObj;
str = str.substring(1, str.length() - 1);
// key=value 쌍 분리
String[] entries = str.split(",");
for (String entry : entries) {
String[] kv = entry.split("=", 2);
if (kv.length == 2) {
inboundHeaderMap.put(kv[0].trim(), kv[1].trim());
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return inboundHeaderMap;
}
@Override
public Object doPostFilter(String adptGrpName, String adptName, Properties prop, Object message,
Properties tempProp) throws Exception {
return message;
}
}
@@ -0,0 +1,138 @@
package com.eactive.eai.custom.kjb.adapter.http.client.filter;
import java.util.Map.Entry;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.Properties;
import java.util.TreeMap;
import org.apache.commons.lang3.StringUtils;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
import com.eactive.eai.adapter.AdapterGroupVO;
import com.eactive.eai.adapter.AdapterManager;
import com.eactive.eai.adapter.http.dynamic.HttpAdapterServiceKey;
import com.eactive.eai.common.property.PropGroupVO;
import com.eactive.eai.common.property.PropManager;
import com.eactive.eai.common.util.Logger;
public class NICECreditFilter implements HttpClientAdapterFilter, HttpAdapterServiceKey {
protected static Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);
public static final String KJB_HEADER = "KJB_Header";
public static final String PROP_PREFIX = "nicecredit";
public static final String H_PRODUCTID = "ProductID";
public static final String B_REQ_DTM = "req_dtm";
public static final String B_GOODS_CLS = "goods_cls";
@SuppressWarnings({ "unchecked" })
@Override
public Object doPreFilter(String adptGrpName, String adptName, Properties prop, Object message, Properties tempProp)
throws Exception {
JSONObject returnMessage = null;
String productId = "2303102120";
String reqDtm = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
String goodsCls = "KJBAPI0001";
PropGroupVO propGroupVo = PropManager.getInstance().getPropGroupVO(KJB_HEADER);
if (propGroupVo != null) {
productId = propGroupVo.getProperty(PROP_PREFIX + "." + H_PRODUCTID);
goodsCls = propGroupVo.getProperty(PROP_PREFIX + "." + B_GOODS_CLS);
}
Properties filterHeaders = (Properties) tempProp.get("FILTERHEADER");
if (filterHeaders == null) {
filterHeaders = new Properties();
tempProp.put("FILTERHEADER", filterHeaders);
}
try {
Map<String, String> inboundHeaderMap = getInboundHeaderProp(tempProp);
if (inboundHeaderMap != null && inboundHeaderMap.containsKey(H_PRODUCTID)) {
for (Entry<String, String> e : inboundHeaderMap.entrySet()) {
String key = (String) e.getKey();
String value = (String) e.getValue();
if (StringUtils.equalsIgnoreCase(key, H_PRODUCTID)) {
filterHeaders.setProperty(key, value);
}
if (logger.isDebugEnabled()) {
logger.debug("Request Header :" + key + "=[" + value + "]");
}
}
} else {
filterHeaders.setProperty(H_PRODUCTID, productId);
}
} catch (Exception e) {
logger.error(e.getMessage());
}
AdapterManager adapterManager = AdapterManager.getInstance();
AdapterGroupVO adptGrpVO = adapterManager.getAdapterGroupVO(adptGrpName);
String encode = adptGrpVO.getMessageEncode();
if (message == null) {
returnMessage = new JSONObject();
} else {
if (message instanceof JSONObject) {
returnMessage = (JSONObject) message;
} else if (message instanceof String) {
returnMessage = parseJson((String) message);
} else if (message instanceof byte[]) {
returnMessage = parseJson(new String((byte[]) message, encode));
}
}
returnMessage.put(B_REQ_DTM, reqDtm);
returnMessage.put(B_GOODS_CLS, goodsCls);
return returnMessage.toJSONString();
}
private JSONObject parseJson(String message) throws Exception {
if (message == null) {
return null;
}
return (JSONObject) JSONValue.parse(message);
}
public Map<String, String> getInboundHeaderProp(Properties prop) {
Properties header = new Properties();
Map<String, String> inboundHeaderMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
try {
Object headerObj = prop.get(HttpAdapterServiceKey.INBOUND_HEADER);
if (headerObj instanceof Properties) { //tomcat
header = (Properties) headerObj;
for (String name : header.stringPropertyNames()) {
inboundHeaderMap.put(name, header.getProperty(name));
}
} else if (headerObj instanceof String) { //weblogic
String str = (String) headerObj;
str = str.substring(1, str.length() - 1);
// key=value 쌍 분리
String[] entries = str.split(",");
for (String entry : entries) {
String[] kv = entry.split("=", 2);
if (kv.length == 2) {
inboundHeaderMap.put(kv[0].trim(), kv[1].trim());
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return inboundHeaderMap;
}
@Override
public Object doPostFilter(String adptGrpName, String adptName, Properties prop, Object message,
Properties tempProp) throws Exception {
return message;
}
}
@@ -0,0 +1,109 @@
package com.eactive.eai.custom.kjb.adapter.http.client.filter;
import java.util.Map;
import java.util.Properties;
import java.util.TreeMap;
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.common.TransactionContextKeys;
import com.eactive.eai.common.util.Logger;
public class SimpleFrameworkFilter implements HttpClientAdapterFilter, HttpAdapterServiceKey {
protected static Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);
public static final String CLIENTID = "clientId";
public static final String TRACEID = "traceId";
public static final String GUID = "guid";
public static final String RECEIVEDTIMESTAMP = "receivedTimestamp";
public static final String PARTNER_TRACE_ID = "partnerTraceId";
@Override
public Object doPreFilter(String adptGrpName, String adptName, Properties prop, Object message, Properties tempProp)
throws Exception {
logger.debug("SimpleFrameworkFilter] PreFilter Processing Start!!");
Properties filterHeaders = (Properties) tempProp.get("FILTERHEADER");
if (filterHeaders == null) {
filterHeaders = new Properties();
tempProp.put("FILTERHEADER", filterHeaders);
}
Map<String, String> inboundHeaderMap = getInboundHeaderProp(tempProp);
try {
/* 업체정보 */
String clientId = tempProp.getProperty(HttpAdapterServiceSupport.PROPERTIES_NAME_CLIENT_ID);
if (StringUtils.isEmpty(clientId)) clientId = "";
filterHeaders.put(CLIENTID, clientId);
logger.debug("SimpleFrameworkFilter] Processing Key ["+CLIENTID+"], value ["+clientId+"]");
/* APIM 거래추적자 */
String uuid = tempProp.getProperty(TransactionContextKeys.TRANSACTION_UUID, "");
filterHeaders.put(TRACEID, uuid);
logger.debug("SimpleFrameworkFilter] Processing Key ["+TRACEID+"], value ["+uuid+"]");
/* GUID */
String guid = tempProp.getProperty(TransactionContextKeys.GUID, "");
filterHeaders.put(GUID, guid);
logger.debug("SimpleFrameworkFilter] Processing Key ["+GUID+"], value ["+guid+"]");
/* 최초요청시간 */
String receivedTimestamp = tempProp.getProperty(HttpAdapterServiceKey.INBOUND_REQUESTED_TIME, "");
filterHeaders.put(RECEIVEDTIMESTAMP, receivedTimestamp);
logger.debug("SimpleFrameworkFilter] Processing Key ["+RECEIVEDTIMESTAMP+"], value ["+receivedTimestamp+"]");
String partnerTraceId = inboundHeaderMap.getOrDefault(PARTNER_TRACE_ID, "");
filterHeaders.put(PARTNER_TRACE_ID, partnerTraceId);
logger.debug("SimpleFrameworkFilter] Processing Key ["+PARTNER_TRACE_ID+"], value ["+partnerTraceId+"]");
String x_loan_token = inboundHeaderMap.getOrDefault(TransactionContextKeys.X_LOAN_TOKEN, "");
filterHeaders.put(TransactionContextKeys.X_LOAN_TOKEN, x_loan_token);
logger.debug("SimpleFrameworkFilter] Processing Key ["+TransactionContextKeys.X_LOAN_TOKEN+"], value ["+x_loan_token+"]");
} catch (Exception e) {
logger.error(e.getMessage());
}
return message;
}
public Map<String, String> getInboundHeaderProp(Properties prop) {
Properties header = new Properties();
Map<String, String> inboundHeaderMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
try {
Object headerObj = prop.get(HttpAdapterServiceKey.INBOUND_HEADER);
if (headerObj instanceof Properties) { //tomcat
header = (Properties) headerObj;
for (String name : header.stringPropertyNames()) {
inboundHeaderMap.put(name, header.getProperty(name));
}
} else if (headerObj instanceof String) { //weblogic
String str = (String) headerObj;
str = str.substring(1, str.length() - 1);
// key=value 쌍 분리
String[] entries = str.split(",");
for (String entry : entries) {
String[] kv = entry.split("=", 2);
if (kv.length == 2) {
inboundHeaderMap.put(kv[0].trim(), kv[1].trim());
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return inboundHeaderMap;
}
@Override
public Object doPostFilter(String adptGrpName, String adptName, Properties prop, Object message,
Properties tempProp) throws Exception {
return message;
}
}
@@ -0,0 +1,28 @@
package com.eactive.eai.custom.kjb.adapter.http.client.filter;
import java.util.Properties;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
import com.eactive.eai.common.TransactionContextKeys;
public class SimpleframeworkBodyFilter extends SimpleFrameworkFilter {
@Override
public Object doPreFilter(String adptGrpName, String adptName, Properties prop, Object message, Properties tempProp)
throws Exception {
Object returnMessage = super.doPreFilter(adptGrpName, adptName, prop, message, tempProp);
JSONObject bizJsonStr = null;
if ( returnMessage instanceof String ) {
bizJsonStr = (JSONObject) JSONValue.parse( (String)returnMessage);
if( bizJsonStr != null ) {
bizJsonStr.put("GUID", tempProp.getOrDefault(TransactionContextKeys.GUID, "") );
}
}
return bizJsonStr != null ? bizJsonStr.toJSONString() : returnMessage;
}
}
@@ -0,0 +1,97 @@
package com.eactive.eai.custom.kjb.adapter.http.client.filter;
import java.util.Map;
import java.util.Properties;
import java.util.TreeMap;
import com.eactive.eai.adapter.http.dynamic.HttpAdapterServiceKey;
import com.eactive.eai.common.TransactionContextKeys;
import com.eactive.eai.common.property.PropGroupVO;
import com.eactive.eai.common.property.PropManager;
import com.eactive.eai.common.util.Logger;
public class TOSSBankFilter implements HttpClientAdapterFilter, HttpAdapterServiceKey {
protected static Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);
public static final String KJB_HEADER = "KJB_Header";
public static final String PROP_PREFIX = "tossbank";
public static final String X_TOSSBANK_LOAN_TRACE_ID = "X-TOSSBANK-LOAN-TRACE-ID";
@Override
public Object doPreFilter(String adptGrpName, String adptName, Properties prop, Object message, Properties tempProp)
throws Exception {
logger.debug("TOSSBankFilter] PreFilter Processing Start!!");
String traceId = tempProp.getProperty(TransactionContextKeys.TRANSACTION_UUID, "");
PropGroupVO propGroupVo = PropManager.getInstance().getPropGroupVO(KJB_HEADER);
if (propGroupVo != null) {
}
Properties filterHeaders = (Properties) tempProp.get("FILTERHEADER");
if (filterHeaders == null) {
filterHeaders = new Properties();
tempProp.put("FILTERHEADER", filterHeaders);
}
try {
Map<String, String> inboundHeaderMap = getInboundHeaderProp(tempProp);
if (!inboundHeaderMap.isEmpty()) {
if (inboundHeaderMap.containsKey(X_TOSSBANK_LOAN_TRACE_ID)) {
filterHeaders.put(X_TOSSBANK_LOAN_TRACE_ID, inboundHeaderMap.get(X_TOSSBANK_LOAN_TRACE_ID));
logger.debug("TOSSBankFilter] Processing Key ["+X_TOSSBANK_LOAN_TRACE_ID+"], value ["+inboundHeaderMap.get(X_TOSSBANK_LOAN_TRACE_ID)+"]");
} else {
filterHeaders.put(X_TOSSBANK_LOAN_TRACE_ID, traceId);
logger.debug("TOSSBankFilter] Processing Key ["+X_TOSSBANK_LOAN_TRACE_ID+"], value ["+traceId+"]");
}
} else {
/* APIM 거래추적자 */
filterHeaders.setProperty(X_TOSSBANK_LOAN_TRACE_ID, traceId);
logger.debug("TOSSBankFilter] Processing Key ["+X_TOSSBANK_LOAN_TRACE_ID+"], value ["+traceId+"]");
}
logger.debug("TOSSBankFilter] Processing Key ["+X_TOSSBANK_LOAN_TRACE_ID+"], value ["+filterHeaders.getProperty(X_TOSSBANK_LOAN_TRACE_ID)+"]");
} catch (Exception e) {
logger.error(e.getMessage());
}
return message;
}
public Map<String, String> getInboundHeaderProp(Properties prop) {
Properties header = new Properties();
Map<String, String> inboundHeaderMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
try {
Object headerObj = prop.get(HttpAdapterServiceKey.INBOUND_HEADER);
if (headerObj instanceof Properties) { //tomcat
header = (Properties) headerObj;
for (String name : header.stringPropertyNames()) {
inboundHeaderMap.put(name, header.getProperty(name));
}
} else if (headerObj instanceof String) { //weblogic
String str = (String) headerObj;
str = str.substring(1, str.length() - 1);
// key=value 쌍 분리
String[] entries = str.split(",");
for (String entry : entries) {
String[] kv = entry.split("=", 2);
if (kv.length == 2) {
inboundHeaderMap.put(kv[0].trim(), kv[1].trim());
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return inboundHeaderMap;
}
@Override
public Object doPostFilter(String adptGrpName, String adptName, Properties prop, Object message,
Properties tempProp) throws Exception {
return message;
}
}
@@ -0,0 +1,71 @@
package com.eactive.eai.custom.kjb.adapter.http.client.filter;
import java.util.Properties;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
import com.eactive.eai.adapter.http.dynamic.HttpAdapterServiceKey;
import com.eactive.eai.common.TransactionContextKeys;
import com.eactive.eai.common.util.Logger;
import com.nimbusds.jose.shaded.gson.JsonObject;
public class TraceBodyFilter implements HttpClientAdapterFilter, HttpAdapterServiceKey {
protected static Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);
public static final String BIZ_HEADER = "header";
public static final String GUID = "guid";
public static final String TRACEID = "traceId";
@Override
public Object doPreFilter(String adptGrpName, String adptName, Properties prop, Object message, Properties tempProp)
throws Exception {
JSONObject bizJsonStr = null;
if (message instanceof String) {
bizJsonStr = (JSONObject) JSONValue.parse((String) message);
if ( bizJsonStr == null ) return message; //json string이 아님.
if (bizJsonStr.containsKey(BIZ_HEADER)) {
JSONObject bizHeader = (JSONObject) bizJsonStr.get(BIZ_HEADER);
putBizHeaderData(bizHeader, tempProp);
}else {
JSONObject bizHeader = new JSONObject();
bizJsonStr.put(BIZ_HEADER, bizHeader);
putBizHeaderData(bizHeader, tempProp);
}
}
return bizJsonStr != null ? bizJsonStr.toJSONString() : message;
}
static private void putBizHeaderData(JSONObject bizHeader, Properties tempProp) {
String traceId = tempProp.getProperty(TransactionContextKeys.TRANSACTION_UUID, "");
String guid = tempProp.getProperty(TransactionContextKeys.GUID, "");
bizHeader.put(GUID, guid);
logger.debug("Processing Key [" + GUID + "], value [" + guid + "]");
bizHeader.put(TRACEID, traceId);
logger.debug("Processing Key [" + TRACEID + "], value [" + traceId + "]");
}
@Override
public Object doPostFilter(String adptGrpName, String adptName, Properties prop, Object message,
Properties tempProp) throws Exception {
// TODO Auto-generated method stub
return message;
}
}

Some files were not shown because too many files have changed in this diff Show More