85 Commits

Author SHA1 Message Date
curry772 15ac0fa2a2 BigTech 어댑터 로그 출력 부분 수정 2026-07-10 10:11:46 +09:00
curry772 e0039aca81 BigTech signature 생성 파라미터(UR) 로그 추가 2026-07-10 10:10:33 +09:00
curry772 5c094ef9b4 Merge branch 'master' of https://git.eactive.synology.me:8090/eapim/djbank/eapim-online.git 2026-07-10 09:46:51 +09:00
curry772 3772805301 BigTech 커스텀 어댑터 개발(커스텀 헤더 추가) 2026-07-10 09:46:33 +09:00
Rinjae d31b7a343c Merge remote-tracking branch 'origin/master' 2026-07-09 16:31:28 +09:00
curry772 47cb243ff4 REST Header 로그에 송신 Body 출력할 수 있는 기능 추가 2026-07-09 16:25:19 +09:00
Rinjae e3fb52343d "Jenkinsfile 개선:
- Tomcat 배포 구조 → WebLogic 기반으로 변경
- stage별 agent 명시 및 불필요한 환경변수 제거
- Webhook 알림 로직 추가"
2026-07-09 16:20:54 +09:00
curry772 46463a27a4 UnknownMessageLogUtils 클래스명 오타 수정 2026-07-09 14:55:23 +09:00
curry772 7e1af96fcf 사용하지 않는 단위테스트 삭제 2026-07-09 14:15:50 +09:00
curry772 f91b50a121 빅테크 Out Rest Adapter 생성 2026-07-09 14:15:35 +09:00
curry772 9cf16c9f49 일부 수정 2026-07-09 14:15:15 +09:00
curry772 7e83c36a79 로그 메시지 수정 2026-07-08 12:26:44 +09:00
curry772 c5577fd973 HttpDynamicInAdapterUri 출력 로그 레벨 변경 2026-07-07 14:49:26 +09:00
curry772 0dd3c89238 Content-Length 헤더 전달하지 않도록 수정 2026-07-07 11:41:33 +09:00
curry772 6343ff23b7 oAuth 서버 /dj 뿐만 아니라 모든 /*/oauth/token 대응하도록 변경 2026-07-06 09:24:06 +09:00
curry772 6597464515 암복호화 JacksonUtil 사용으로 변경 2026-07-03 09:01:40 +09:00
curry772 72a39b8d9c JSONObject 사용 로직 Jackson으로 변경 2026-07-01 17:07:42 +09:00
curry772 4094058745 토큰 발급 시 trust 체크 하지 않도록 변경 2026-07-01 11:43:40 +09:00
curry772 0a692f8a07 암복호화 기능 수정 2026-06-30 16:24:15 +09:00
curry772 0dfab56d0c ApiRequestBodyFilter 사용 제거 2026-06-30 10:11:13 +09:00
curry772 c38643b099 개발 환경 맞춤 2026-06-30 09:46:12 +09:00
curry772 1ba8de1e3b /를 \/로 escaping 하지 않도록 Jackson으로 변경 2026-06-30 09:45:52 +09:00
curry772 e5fe9b853f 사용하지 않는 클래스 제거 2026-06-30 09:44:00 +09:00
curry772 918c4b5c3e HSM 암복화 테스트 중 추가 기능 수정 2026-06-29 13:18:02 +09:00
curry772 c12ead1f97 전체 수신 @RequestMapping 추가 2026-06-29 13:17:34 +09:00
curry772 ebeaa6af17 Merge branch 'master' of https://git.eactive.synology.me:8090/eapim/djbank/eapim-online.git 2026-06-25 09:59:30 +09:00
curry772 133c482ef8 더존 암호화 파라미터 이름 변경 - 관리포털에서서 contextKey이름과도 맞추어야 함 2026-06-25 09:59:00 +09:00
curry772 f72348ae67 카카오페이 isagreed 필드 체크 로직 필터 개발 2026-06-25 09:57:52 +09:00
curry772 704a9ff90e Erp nsmapi 토큰발급 개발 2026-06-25 09:57:11 +09:00
Rinjae db78cbac2e WAR 파일 해시 파일 확장자 변경 및 MD5SUM 추가 2026-06-23 13:58:02 +09:00
curry772 403b201168 미확인 오류 출력 수정 2026-06-23 11:26:08 +09:00
curry772 ec0cdfd221 OAuthServer 프러퍼티 그룹 암호화 적용 2026-06-23 08:53:12 +09:00
curry772 d277e37798 Merge branch 'master' of https://git.eactive.synology.me:8090/eapim/djbank/eapim-online.git 2026-06-19 17:58:59 +09:00
curry772 e4851999ba HSM key 가져오는 필터 개발 2026-06-19 17:58:03 +09:00
rinjae 110b0c0039 Jenkinsfile 업데이트 2026-06-18 12:18:53 +09:00
rinjae d85642c5a8 의존 프로젝트 감지 후 빌드로 변경 2026-06-18 12:06:23 +09:00
rinjae 49620c5e21 Jenkinsfile 추가 2026-06-18 12:01:57 +09:00
curry772 1ab25d634e DJB 표준 전문 관련 수정 2026-06-17 19:22:39 +09:00
curry772 984be8b1ee KJB 소스 정리 2026-06-17 19:22:10 +09:00
curry772 cc315f2ad0 에러응답 처리 수정 2026-06-17 13:53:46 +09:00
curry772 7b9862e34b DJB 요건에 맞춤
- EZDATA_SVR_TX_SEQNO 생성 로직 변경
- 환경구분 변경이 운영환경인 경우, P로 설정되도록 수정
2026-06-17 09:25:05 +09:00
curry772 ec9c6d8553 KJB 필터 및 Util 정리 2026-06-15 15:02:47 +09:00
curry772 a01ee024c0 djbank에 커스텀 될 AdapterErrorMsgHandler 추가 2026-06-15 14:47:01 +09:00
curry772 1240b24c90 BIZ_DATA 항목 추가 2026-06-12 09:08:02 +09:00
curry772 7346c3070d eaiSvcCd 추출 로직 변경
- StandarMessage에 이미 eaiSvcCd가 설정되어 있으면 그것을 이용(비표준)
- 표준전문에서 조합하도록 수정(표준)
2026-06-12 09:07:40 +09:00
curry772 9a597a52ac afterFoundEaiMessage 함수에 프러퍼티 이용 헤더매핑 로직 추가
- DJB_IN_HTTHEAD_STDHEAD_MAPPING 프러퍼티 그룹 이용
2026-06-12 09:05:50 +09:00
curry772 5653a183eb GUID 생성 시 시스템코드 OPA 사용으로 변경 2026-06-12 09:01:27 +09:00
curry772 0e3a612bc0 TemplateAdapterErrorMsgHandler 를 이용하여 오류메시지 생성할 수 있도록 변경 2026-06-09 13:49:32 +09:00
curry772 68c29b962e feat: StandardMessageCoordinatorDJB에 callProp refValue 값 주입 기능 추가
- coordinateAfterCreateMessageByRule에서 StandardItem의 refValue가
   \${callProp.키} 패턴이면 Properties에서 값을 꺼내 setValue로 설정한다.
- \${callProp.키:기본값} 구문으로 기본값 지정 가능
- 점(.) 구분으로 Properties/Map 중첩 구조 탐색 지원
- GROUP 타입 재귀 순회, GRID/FARRAY는 건너뜀
2026-06-05 17:20:09 +09:00
curry772 dafe80f8bc fix: SiftingAppender 파일 락 해결 및 거래 로그 on/off 제어 추가
- DJErpApiAdapterController: MDC 제어를 TxSiftContext.begin/end로 교체
- TxSiftContext.end()를 별도 finally로 분리하여 항상 실행 보장
- elink-online-core: Logger SiftingAppender 개선 (submodule)
- elink-online-common: TxSiftContext 추가 (submodule)
2026-06-05 14:06:40 +09:00
curry772 9e78a0e199 ApiRequestBodyFilter 테스트를 위한 url pattern /dj/* 추가 2026-06-02 18:08:11 +09:00
curry772 3ec18b17e8 테스크 코드 이동 2026-06-02 18:06:55 +09:00
curry772 90464ee33f 성능 튜닝 2026-06-02 18:06:29 +09:00
curry772 60e6b30c9b api id, eai svc cd 추출 부분 ApiKeyExtractFilter로 이동 2026-06-02 18:06:19 +09:00
curry772 7d89d6d43e XML 메시지 지원 2026-06-02 08:30:51 +09:00
curry772 7109c56623 DJBank에 필요없는 클래스 정리
- 타 사이트 설정파일 및 표준전문 클래스
2026-06-01 09:57:06 +09:00
curry772 6a53e29710 eai.tableowner AGWADM -> AGWAPP 변경 2026-06-01 09:41:10 +09:00
curry772 eff7b31490 암복호화 함수 이름 변경 및 단위테스트 추가
- 클래스이름 오타 수정
- Parser 단위 테스트 추가
2026-06-01 08:58:08 +09:00
curry772 e2950a339f API 수신 RequestMapping 추가 2026-05-28 13:31:35 +09:00
curry772 01e4e36345 HsmContextSha256KeyDerivationStrategy 추가
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 13:30:37 +09:00
curry772 698b3ca7b6 JWT 서명 알고리즘 RS256 → PS256 교체 — AuthorizationServerConfig
PssJwtAccessTokenConverter 적용

- jwtAccessTokenConverter()에서 PssJwtAccessTokenConverter 사용으로 변경
- 기존 JwtAccessTokenConverter + setKeyPair() → new
PssJwtAccessTokenConverter(privateKey, publicKey)
- PEM 도출·setVerifierKey() 호출은 생성자 내부로 이전
- elink-online-common 서브모듈 업데이트 (RsaPssSigner, RsaPssVerifier,
PssJwtAccessTokenConverter 추가)
2026-05-28 13:28:41 +09:00
curry772 c74d1777df Dynamic IV 도출 지원 — DerivedKey iv 필드 추가, HsmKeyAndIvDerivationStrategy 구현 2026-05-27 13:48:23 +09:00
curry772 c95307901e DJBErp -> DJB 클래스 이름 첨자 변경 2026-05-27 10:49:22 +09:00
curry772 c12ff0a64a chore: 서브모듈들 master HEAD로 업데이트 2026-05-27 10:34:25 +09:00
curry772 1c82f0121c 설계서 ignore 추가 2026-05-27 10:33:23 +09:00
curry772 82998b475c DJBRequestProcessor Override 메소드 시그니쳐 변경 2026-05-27 10:32:08 +09:00
curry772 7336267b32 Reload/RemoveInflowClientControlCommand 패키지 이동 2026-05-27 10:30:54 +09:00
curry772 8d69c459d9 클라이언트 유량제어 관련 단위테스트 eapim-online 이동 2026-05-26 16:36:16 +09:00
curry772 4f54ee5259 클라이언트 유량제어 eapim-online 이동 2026-05-26 16:33:15 +09:00
curry772 912a282b54 body 전체 암복호화 대상 필터 (설계서 작성 코딩) 2026-05-26 15:16:54 +09:00
curry772 e8c379f3c7 대상 없는 암복호화 필터 제거 2026-05-26 13:43:10 +09:00
curry772 6ab138b63a 전체 body 암호화를 사용하지 않는 기관의 필터 제거
- 카카오페이, 네이버페이
2026-05-26 13:41:29 +09:00
curry772 c2ad7be7e4 전체암호화를 상용하지 않는 기관의 필터 제거
- 카카오페이, 네이버페이
2026-05-26 13:40:52 +09:00
curry772 aea4134437 Erp 응답용 외부 암호화 없음 2026-05-26 13:28:32 +09:00
curry772 bf91d02be7 암복호화 function 개발
- 암호화모듈서비스(CryptoModuleService)를 호출하여 암복호화 수행하는 function
2026-05-26 10:15:22 +09:00
curry772 9fcce55966 coordinateAfterParsing 수정 2026-05-26 09:54:53 +09:00
curry772 1f285c3bfd DJBank 암복호화필터 설계
- 기관별 InCryptoFilter를 상속한 필터 설계
- 기관별 OutCryptoFilter를 상송한 필터 설계
- 대상 기관 : 카카오뱅크, 카카오페이, 네이버Fn, 토스뱅크, 제주은행, DJErp
2026-05-26 09:51:53 +09:00
curry772 13608ebc8e chore: elink-online-common/core 서브모듈을 master HEAD로 업데이트 2026-05-19 11:22:18 +09:00
curry772 202538c50c 거래파일로그(SIFT_LOGGER) 출력기능 개발
- UUID별로 거래파일로그 생성
2026-05-19 11:19:39 +09:00
curry772 ade012a1f7 JSON 메시지 validation 기능 수정
- 기본 동작 변경 : transformer.validation=true
2026-05-18 14:01:42 +09:00
curry772 abe3756f69 chore: elink-online-common 서브모듈을 master HEAD로 업데이트 2026-05-18 10:58:50 +09:00
curry772 65fc0d0df4 context.xml rename 2026-05-18 10:49:49 +09:00
curry772 c34a2c019b 분별 통계 메트릭 로직 변경
- totoalCnt는 400시점으로 변경
- 시간 메트릭 생성 동작 기본값 false로 기본 처리하지 않도록 변경
2026-05-18 10:46:43 +09:00
curry772 6dbe557584 chore: elink-online-common 서브모듈을 master HEAD로 업데이트
feature/cryptofilter 머지 후 생성된 머지 커밋 반영

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 15:24:54 +09:00
curry772 1185329b78 Merge branch 'feature/cryptofilter' 2026-05-14 15:24:20 +09:00
146 changed files with 5068 additions and 12371 deletions
+2
View File
@@ -239,3 +239,5 @@ gradle-app.setting
# Claude Code
.claude/
/작업내역*.md
/설계서*.md
/분석서*.md
Vendored
+157
View File
@@ -0,0 +1,157 @@
pipeline {
agent none
options {
timestamps()
disableConcurrentBuilds()
skipDefaultCheckout() // stage별 agent의 자동 SCM checkout 방지 (Deploy 노드는 git 접근 불필요, unstash로만 WAR 수신)
buildDiscarder(logRotator(numToKeepStr: '20', artifactNumToKeepStr: '20'))
}
environment {
GIT_SSH_COMMAND = 'ssh -o StrictHostKeyChecking=accept-new'
WEBHOOK_URL = 'http://172.30.1.50:18000/api/hooks/01ba51b01d3c4c98ae8f3183a23a84ed713197857d024b5da4f303458195eb32'
}
stages {
stage('Build (djb-vm)') {
agent { label 'djb-vm' }
environment {
JAVA_HOME = '/apps/opts/jdk8'
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}"
}
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 -Pprofile=weblogic' }
post {
success {
sh '''
set -eu
cd build/libs
sha256sum eapim-online.war > eapim-online.war.sha256
'''
archiveArtifacts artifacts: 'build/libs/eapim-online.war,build/libs/eapim-online.war.sha256', fingerprint: true
stash name: 'war', includes: 'build/libs/eapim-online.war'
}
}
}
}
}
stage('Deploy (weblogic)') {
agent { label 'weblogic' }
environment {
JENKINS_NODE_COOKIE = 'dontKillMe' // background weblogic를 빌드 종료 시 죽이지 않게
WL_HOME = '/app/eapim/apigw'
WL_DEPLOY_DIR = '/app/eapim/apigw'
WL_WAR_NAME = 'eapim-online.war'
WL_HTTP_PORT = '39110'
WL_NOHUP = '/logs/weblogic/domains/eapimDomain/nohup.agwSvr11.out'
}
stages {
stage('Stop WebLogic') {
steps {
sh '"$WL_HOME/stopAgw11.sh"' // 동기: 완전 종료까지 블록, 미기동 시에도 안전
}
}
stage('Deploy WAR') {
steps {
unstash 'war'
sh '''
set -eu
cp build/libs/eapim-online.war "$WL_DEPLOY_DIR/$WL_WAR_NAME"
ls -la "$WL_DEPLOY_DIR"
'''
}
}
stage('Start WebLogic and readiness') {
steps {
sh '''
set -eu
"$WL_HOME/startAgw11.sh" # nohup & 로 백그라운드 기동, 즉시 리턴
DEADLINE=$(($(date +%s) + 300))
STATUS=000
while [ "$(date +%s)" -lt "$DEADLINE" ]; do
STATUS=$(curl -sS -o /dev/null -w '%{http_code}' --max-time 3 \
"http://localhost:$WL_HTTP_PORT/" 2>/dev/null || echo "000")
case "$STATUS" in
200|302|401|403|404) echo "Readiness OK (/ HTTP $STATUS)"; break ;;
esac
sleep 3
done
case "$STATUS" in
200|302|401|403|404) ;;
*)
echo "Readiness failed within 300s, last HTTP=$STATUS"
[ -f "$WL_NOHUP" ] && tail -120 "$WL_NOHUP"
exit 1
;;
esac
'''
}
}
}
}
}
post {
success {
node('weblogic') {
sh '''
set +e
payload=$(printf '{"text":"[%s #%s](%s) SUCCESS"}' "$JOB_NAME" "$BUILD_NUMBER" "$BUILD_URL")
curl -sS --fail --max-time 5 -H 'Content-Type: application/json' -X POST --data "$payload" "$WEBHOOK_URL" >/dev/null || echo "Webhook failed"
'''
}
}
failure {
node('weblogic') {
sh '''
set +e
payload=$(printf '{"text":"[%s #%s](%s) FAILURE"}' "$JOB_NAME" "$BUILD_NUMBER" "$BUILD_URL")
curl -sS --fail --max-time 5 -H 'Content-Type: application/json' -X POST --data "$payload" "$WEBHOOK_URL" >/dev/null || echo "Webhook failed"
'''
}
}
}
}
@@ -13,10 +13,10 @@ hibernate.dialect=org.hibernate.dialect.Oracle12cDialect
#inst.Name=${HOSTNAME}
inst.Name=agwSvr11
eai.jdbc.Name=jdbc/dsOBP_AGW
eai.rmiport=30111
eai.rmiserviceport=30112
eai.rmiport=39111
eai.rmiserviceport=39112
eai.systemmode=D
# EAI FEP MCI GW ...
eai.systemtype=API
eai.tableowner=AGWADM
eai.tableowner=AGWAPP
eai.server.extractor=[0,2],[0,2],[-2]
@@ -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=false
transformer.validation=true
+4 -4
View File
@@ -23,15 +23,15 @@
<url-pattern>/mgr/*</url-pattern>
</filter-mapping>
<filter>
<!-- <filter>
<filter-name>ApiRequestBodyFilter</filter-name>
<filter-class>com.eactive.eai.adapter.controller.ApiRequestBodyFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>ApiRequestBodyFilter</filter-name>
<url-pattern>/api/*</url-pattern> <!-- 필요한 URL 패턴 -->
<url-pattern>/mapi/*</url-pattern> <!-- 필요한 URL 패턴 -->
</filter-mapping>
<url-pattern>/api/*</url-pattern> 필요한 URL 패턴
<url-pattern>/dj/*</url-pattern> 필요한 URL 패턴
</filter-mapping> -->
<context-param>
-26
View File
@@ -1,26 +0,0 @@
# 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
@@ -1,41 +0,0 @@
#-----------------------------------------------------------------------------
# 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 , , ,
@@ -1,11 +0,0 @@
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
@@ -1,27 +0,0 @@
# 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
@@ -1,16 +0,0 @@
# 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
@@ -1,26 +0,0 @@
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=
@@ -1,29 +0,0 @@
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
@@ -1,27 +0,0 @@
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
@@ -1,26 +0,0 @@
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
@@ -1,380 +0,0 @@
//package com.eactive.eai.adapter.controller;
//
//import java.util.ArrayList;
//import java.util.Collections;
//import java.util.Date;
//import java.util.Enumeration;
//import java.util.HashMap;
//import java.util.List;
//import java.util.Map;
//import java.util.Properties;
//
//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.util.AntPathMatcher;
//import org.springframework.web.bind.annotation.RequestMapping;
//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.Keys;
//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.HttpDynamicInAdapterManager;
//import com.eactive.eai.adapter.http.dynamic.HttpDynamicInAdapterUri;
//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.ApiAdapterService;
//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.UUIDGenerator;
//import com.eactive.eai.inbound.action.ActionException;
//import com.eactive.eai.inbound.action.ActionFactory;
//import com.eactive.eai.inbound.action.RequestAction;
//import com.eactive.eai.inbound.error.InboundErrorInfoVO;
//
//@RestController
//public class ApiAdapterController implements HttpAdapterServiceKey {
// static Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);
//
// @Autowired
// ApiAdapterService service;
//
// /**
// * KJBANK 요구사항으로 /mapi/oauth2/token 과 같은 형태로 토큰 발급거래를 수행해야해서 예외처리함
// *
// * @See com.eactive.eai.authserver.config.AuthorizationServerConfig.configure(AuthorizationServerEndpointsConfigurer
// * endpoints)
// */
// @RequestMapping(value = { "/api/*", "/mapi/*", "/api/*/{path:^(?!.*oauth).*$}/**",
// "/mapi/{path:^(?!.*oauth2).*$}/**" })
//// @RequestMapping(value = {"/api/**"})
// public ResponseEntity<String> callApi(HttpServletRequest servletRequest, HttpServletResponse servletResponse)
// throws Exception {
// // /ONLWeb/api/v1/public/getUserInfo.svc
// String apiUri = servletRequest.getRequestURI();
// // /api/v1/public/getUserInfo.svc
// apiUri = StringUtils.removeStart(apiUri, servletRequest.getContextPath());
// apiUri = StringUtils.removeEnd(apiUri, "/");
//
// HttpDynamicInAdapterUri adptUri = findAdptUri(apiUri, HttpDynamicInAdapterManager.getInstance(), "");
//
// if (logger.isDebug()) {
// logger.debug("ApiAdapterController] service request uri : " + servletRequest.getRequestURI());
// }
//
// if (adptUri == null) {
// logError(servletRequest);
// String errorMsg = MessageUtil.makeJsonErrorMessage(MessageUtil.ERROR_CODE_SERVICE_NOT_FOUND,
// "can not find Adapter Uri");
// return ResponseEntity.status(HttpStatus.NOT_FOUND).contentType(MediaType.APPLICATION_JSON).body(errorMsg);
// }
//
// // Received time , jwhong
// long receivedTimeMillis = System.currentTimeMillis(); // 밀리세컨 단위로 보내야함
// String receivedTimeStr = String.valueOf(receivedTimeMillis);
//
// String adapterGroupName = adptUri.getAdptGrpName();
// String adapterName = adptUri.getAdptName();
// AdapterGroupVO adapterGroupVO = AdapterManager.getInstance().getAdapterGroupVO(adapterGroupName);
// AdapterVO adapterVO = AdapterManager.getInstance().getAdapterVO(adapterGroupName, adapterName);
// if (adapterVO == null) {
// String errorMsg = MessageUtil.makeJsonErrorMessage(MessageUtil.ERROR_CODE_AP_ERROR,
// "Adapter not found error");
// return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).contentType(MediaType.APPLICATION_JSON)
// .body(errorMsg);
// }
//
// Properties httpProp = AdapterPropManager.getInstance().getProperties(adapterVO.getPropGroupName());
//
// String adptMsgType = adapterVO.getAdapterGroupVO().getMessageType();
// String encode = StringUtils.defaultIfBlank(adapterGroupVO.getMessageEncode(), "UTF-8");
// MediaType mediaType = MediaType.valueOf("application/json;charset=" + encode);
// String errorResponseFormat = httpProp.getProperty(ERROR_RESPONSE_FORMAT);
//
// String responseType = httpProp.getProperty(RESPONSE_TYPE, "SYNC");
// ResponseEntity responseEntity = null;
// Properties transactionProp = new Properties();
// // jwhong, put api received time, eaiSvcCode
// transactionProp.put(INBOUND_REQUESTED_TIME, receivedTimeStr);
//
// // ** 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 {
// // 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);
// // if (RESPONSE_TYPE_ASYNC.equals(responseType)) { // table의 값이 ASYNC 로 들어가 있는데
// // response_type_async는 ASYN 로 되어 있어 아래처럼 비교함 jwhong 2025
// if ("ASYNC".equals(responseType)) {
// // responseEntity = ResponseEntity.ok("dummy"); // comment by jwhong
// // servletResponse.addHeader("traceId", responseData); // uuid를 response header에
// servletResponse.addHeader("traceId",
// transactionProp.getProperty(TransactionContextKeys.TRANSACTION_UUID)); // uuid를 response header에
// int httpStatus = servletResponse.getStatus();
// if (httpStatus == 200) {
// // 업체별 aync response message 가 다르다.
// String asyncMsgStyle = httpProp.getProperty("ASYNC_RTNMSG_TYPE", "");
// String responseBody = "";
// boolean encryptAsyncAckApply = StringUtils
// .equalsIgnoreCase(httpProp.getProperty("ASYNC_ENCRYPT_ACK", "N"), "Y");
//
// if (asyncMsgStyle == null || asyncMsgStyle.trim().isEmpty()) {
// responseBody = makeResponseBodyMsg();
// } else {
// responseBody = asyncMsgStyle;
// }
// if (encryptAsyncAckApply) {
// responseBody = service.doPostEncryption(responseBody, transactionProp, servletRequest);
// }
//
// responseEntity = ResponseEntity.status(httpStatus).contentType(mediaType).body(responseBody);
// // jwhong
// } else {
// responseEntity = ResponseEntity.status(httpStatus).contentType(mediaType).body(responseData);
// }
// } else {
//// responseEntity = ResponseEntity.ok().contentType(mediaType).body(responseData);
//// 버즈빌 포인트 적립 처리하기 위하여 정상응답이더라도 응답코드(apiRsltCd) 값이 200이 아닌 경우 처리를 위하여 수정
//// Filter에서 설정한 response status값을 responseEntity 생성시 적용 modify by lwk 2025.03.24
//
// servletResponse.addHeader("traceId",
// transactionProp.getProperty(TransactionContextKeys.TRANSACTION_UUID)); // uuid를 response header에
// int httpStatus = servletResponse.getStatus();
// if (httpStatus != 200) {
// responseEntity = ResponseEntity.status(httpStatus).contentType(mediaType).body(responseData);
// } else {
// responseEntity = ResponseEntity.ok().contentType(mediaType).body(responseData);
// }
// }
//
// } catch (Exception e) {
//
//
// logger.error(adapterGroupName + "-" + adapterName + ">>" + e.getMessage(), e);
//
// String errorMsg = null;
//
// 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 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 {
// /**
// * 로깅 인터셉터에 데이터를 전달하기 위한 처리 이미 거래처리가 끝나서 영향도가 없을만한 servletRequest에 Attribute로 전달
// * 추후 더 좋은방법이 생길경우 개선 요망
// */
// try {
//
// 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);
// }
// }
// return responseEntity;
// }
//
// /**
// * url에서 뒤쪽 /이후를 제거하면서 찾는다.<br>
// * /api/v1/public/getUserInfo.svc
// *
// * @param apiUri
// * @return
// */
// private HttpDynamicInAdapterUri findAdptUri(String apiUri, HttpDynamicInAdapterManager manager,
// String adapterGrpName) throws Exception {
// if (StringUtils.isBlank(apiUri)) {
// return null;
// }
//
// // * manager 출력
// logger.warn("===== adptUriMap 상세 내용 =====");
// for (Map.Entry<String, HttpDynamicInAdapterUri> entry : manager.adptUriMap.entrySet()) {
// HttpDynamicInAdapterUri uriObj = entry.getValue();
// logger.warn("GroupName=" + uriObj.getAdptGrpName() + ", AdapterName=" + uriObj.getAdptName() + ", URI="
// + uriObj.getUri());
// }
// logger.warn("================================");
//
// //
// for (int i = 0; i < 10; i++) {
// HttpDynamicInAdapterUri adptUri = manager.getAdptUri(apiUri);
// if (adptUri != null) {
// if (StringUtils.isNotEmpty(adapterGrpName)) {
// if(adptUri.getAdptGrpName().equals(adapterGrpName)) {
// return adptUri;
// }
// } else {
// return adptUri;
// }
// }
//
// // /api/v1/public
// apiUri = StringUtils.substringBeforeLast(apiUri, "/");
// if (apiUri.length() < 4) { // /api
// return null;
// }
// }
//
// return null;
// }
//
// private void logError(HttpServletRequest request) {
// InboundErrorInfoVO errorInfoVO = new InboundErrorInfoVO();
//
// EAIServerManager eaiServerManager = EAIServerManager.getInstance();
// String serverName = eaiServerManager.getLocalServerName();
// String uuid = null;
// StringBuffer sb = new StringBuffer();
//
// 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(String asyncMsgStyle) {
// *
// * Map<String, Object> dataMap = new HashMap<>(); dataMap.put("result", 1);
// * Map<String, Object> map = new HashMap<>();
// *
// * switch (asyncMsgStyle) { case "TB_SUC": map.put("rspCode", "TB_SUC_000");
// * map.put("rspMsg", "정상"); map.put("data", dataMap); break; case "ONLYDATA":
// * map.put("data", dataMap); map.put("success", "true"); break; default:
// * map.put("code", "200"); map.put("data", dataMap); map.put("message",
// * "정상 처리 되었습니다."); break; }
// *
// * JSONObject json = new JSONObject(); json.putAll(map);
// *
// * return json.toJSONString(); }
// */
//}
@@ -21,6 +21,9 @@ 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 {
@@ -32,17 +35,11 @@ public class ApiRequestBodyFilter extends OncePerRequestFilter {
HttpServletResponse response,
FilterChain filterChain)
throws ServletException, IOException {
if (!isTarget(request)) {
if (!isTarget(request)) {
filterChain.doFilter(request, response);
return;
}
String encoding = getInboundGroupAdapterEncoding(request);
//원본 바디를 byte[]로 읽기 (개행 포함 그대로) 왜? 뒤에 필터에서 HMAC값 계산할수도 있어서 변조되면 안됨. 개행문자도 그대로 가야함.
byte[] rawBody = readBodyAsBytes(request);
@@ -54,14 +51,16 @@ 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);
String bodyStr = new String(rawBody, encoding);
Object json = JSONValue.parse(bodyStr);
if (json instanceof JSONArray) {
bodyStr = DJB_ROOTLESS_ARRAY + bodyStr + "}";
finalBody = bodyStr.getBytes(encoding);
}
if (json instanceof JSONArray) {
bodyStr = DJB_ROOTLESS_ARRAY + bodyStr + "}";
finalBody = bodyStr.getBytes(encoding);
}
}
} catch (Exception e) {
// JSON 파싱 실패 시 원본 유지
@@ -74,6 +73,14 @@ 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,353 @@
package com.eactive.eai.adapter.controller;
import java.util.Date;
import java.util.Map;
import java.util.Properties;
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.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
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.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.JwtAuthException;
import com.eactive.eai.adapter.service.DJBApiAdapterService;
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.util.DatetimeUtil;
import com.eactive.eai.common.util.HttpAdapterExtraLogUtil;
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.RestSendBodyLogUtils;
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 {
static Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);
@Autowired
DJBApiAdapterService service;
/**
* DJErp OAuth 조기 적용을 위한 Controller
*
* @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).*$}/**",
"/**/{path:^(?!oauth).*$}", "/**/{path:^(?!oauth).*$}/**",
})
public ResponseEntity<String> callApi(HttpServletRequest servletRequest, HttpServletResponse servletResponse)
throws Exception {
// /ONLWeb/api/v1/public/getUserInfo.svc
String apiUri = servletRequest.getRequestURI();
// /api/v1/public/getUserInfo.svc
apiUri = StringUtils.removeStart(apiUri, servletRequest.getContextPath());
apiUri = StringUtils.removeEnd(apiUri, "/");
HttpDynamicInAdapterUri adptUri = findAdptUri(apiUri, HttpDynamicInAdapterManager.getInstance(), "");
if (logger.isDebug()) {
logger.debug("ApiAdapterController] service request uri : " + servletRequest.getRequestURI());
}
if (adptUri == null) {
logError(servletRequest);
String errorMsg = MessageUtil.makeJsonErrorMessage(MessageUtil.ERROR_CODE_SERVICE_NOT_FOUND,
"can not find Adapter Uri");
return ResponseEntity.status(HttpStatus.NOT_FOUND).contentType(MediaType.APPLICATION_JSON).body(errorMsg);
}
// Received time , jwhong
long receivedTimeMillis = System.currentTimeMillis(); // 밀리세컨 단위로 보내야함
String receivedTimeStr = String.valueOf(receivedTimeMillis);
String adapterGroupName = adptUri.getAdptGrpName();
String adapterName = adptUri.getAdptName();
AdapterGroupVO adapterGroupVO = AdapterManager.getInstance().getAdapterGroupVO(adapterGroupName);
AdapterVO adapterVO = AdapterManager.getInstance().getAdapterVO(adapterGroupName, adapterName);
if (adapterVO == null) {
String errorMsg = MessageUtil.makeJsonErrorMessage(MessageUtil.ERROR_CODE_AP_ERROR,
"Adapter not found error");
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).contentType(MediaType.APPLICATION_JSON)
.body(errorMsg);
}
Properties httpProp = AdapterPropManager.getInstance().getProperties(adapterVO.getPropGroupName());
String adptMsgType = adapterVO.getAdapterGroupVO().getMessageType();
String encode = StringUtils.defaultIfBlank(adapterGroupVO.getMessageEncode(), "UTF-8");
MediaType mediaType = MediaType.valueOf("application/json;charset=" + encode);
String errorResponseFormat = httpProp.getProperty(ERROR_RESPONSE_FORMAT);
ResponseEntity<String> 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 = "";
try {
responseData = service.callApi(servletRequest, servletResponse, httpProp, adapterGroupVO, adapterVO,
transactionProp);
servletResponse.addHeader("traceId",
transactionProp.getProperty(TransactionContextKeys.TRANSACTION_UUID)); // uuid를 response header에
int httpStatus = servletResponse.getStatus();
if (httpStatus != 200) {
responseEntity = ResponseEntity.status(httpStatus).contentType(mediaType).body(responseData);
} else {
responseEntity = ResponseEntity.ok().contentType(mediaType).body(responseData);
}
} catch (Exception e) {
logger.error(adapterGroupName + "-" + adapterName + ">>" + e.getMessage(), e);
String responseErrorMsg = null;
if (e instanceof FilterException) {
responseErrorMsg = genErrorResponseMessage(adapterGroupName, adapterName,
transactionProp, MessageUtil.ERROR_CODE_AP_ERROR, e, adptMsgType, encode, errorResponseFormat);
FilterException e1 = (FilterException) e;
this.logError(servletRequest, adapterGroupName, e1.getCode(), responseErrorMsg, transactionProp, e);
logger.error("ApiAdapterController] " + adapterGroupName + "-" + adapterName + ">>" + e.getMessage());
responseEntity = ResponseEntity.status(e1.getStatus()).contentType(mediaType).body(responseErrorMsg);
} else if (e instanceof JwtAuthException) {
responseErrorMsg = genErrorResponseMessage(adapterGroupName, adapterName,
transactionProp, MessageUtil.ERROR_CODE_AP_ERROR, e, adptMsgType, encode, errorResponseFormat);
JwtAuthException e1 = (JwtAuthException) e;
this.logError(servletRequest, adapterGroupName, e1.getCode(), responseErrorMsg, transactionProp, e);
logger.error("ApiAdapterController] " + adapterGroupName + "-" + adapterName + ">>" + e.getMessage(), e);
responseEntity = ResponseEntity.status(HttpStatus.UNAUTHORIZED).contentType(mediaType).body(responseErrorMsg);
} else if (e instanceof HttpStatusException) {
responseErrorMsg = genErrorResponseMessage(adapterGroupName, adapterName,
transactionProp, MessageUtil.ERROR_CODE_AUTH_FAIL, e, adptMsgType, encode, errorResponseFormat);
logger.error("ApiAdapterController] " + adapterGroupName + "-" + adapterName + ">>" + e.getMessage());
HttpStatusException e1 = (HttpStatusException) e;
responseEntity = ResponseEntity.status(e1.getStatus()).contentType(mediaType).body(responseErrorMsg);
} else {
responseErrorMsg = genErrorResponseMessage(adapterGroupName, adapterName,
transactionProp, MessageUtil.ERROR_CODE_AP_ERROR, e, adptMsgType, encode, errorResponseFormat);
this.logError(servletRequest, adapterGroupName, MessageUtil.ERROR_CODE_AP_ERROR, responseErrorMsg, transactionProp, e);
logger.error(adapterGroupName + "-" + adapterName + ">>" + e.getMessage(), e);
responseEntity = ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).contentType(mediaType).body(responseErrorMsg);
}
responseData = responseErrorMsg;
} finally {
// 어댑터 HTTP 로그에 응답 body 출력
if (RestSendBodyLogUtils.isBodyLoggingApi(transactionProp.getProperty("API_SERVICE_CODE"))) {
String trimmedBody = RestSendBodyLogUtils.getBodyByMaxSize(responseData);
transactionProp.setProperty(HttpAdapterExtraLogUtil.BODY_FIELD_NAME, trimmedBody);
}
servletRequest.setAttribute(TransactionContextKeys.TRANSACTION_PROP, transactionProp);
/**
* 로깅 인터셉터에 데이터를 전달하기 위한 처리 이미 거래처리가 끝나서 영향도가 없을만한 servletRequest에 Attribute로 전달
* 추후 더 좋은방법이 생길경우 개선 요망
*/
try {
TxFileLogger.logTxFile(transactionProp, responseData, "[IN_SEND]");
servletRequest.setAttribute(TransactionContextKeys.TRANSACTION_PROP, transactionProp);
} 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, String code, 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 {
callProp.put("INBOUND_RESULT_CODE", code);
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,
code, e.getMessage(), errorResponseFormat);
}
/**
* url에서 뒤쪽 /이후를 제거하면서 찾는다.<br>
* /api/v1/public/getUserInfo.svc
*
* @param apiUri
* @return
*/
private HttpDynamicInAdapterUri findAdptUri(String apiUri, HttpDynamicInAdapterManager manager,
String adapterGrpName) throws Exception {
if (StringUtils.isBlank(apiUri)) {
return null;
}
if(logger.isDebug()) {
logger.debug("===== adptUriMap 상세 내용 =====");
for (Map.Entry<String, HttpDynamicInAdapterUri> entry : manager.adptUriMap.entrySet()) {
HttpDynamicInAdapterUri uriObj = entry.getValue();
logger.debug("GroupName=" + uriObj.getAdptGrpName() + ", AdapterName=" + uriObj.getAdptName() + ", URI="
+ uriObj.getUri());
}
logger.debug("================================");
}
//
for (int i = 0; i < 10; i++) {
HttpDynamicInAdapterUri adptUri = manager.getAdptUri(apiUri);
if (adptUri != null) {
if (StringUtils.isNotEmpty(adapterGrpName)) {
if(adptUri.getAdptGrpName().equals(adapterGrpName)) {
return adptUri;
}
} else {
return adptUri;
}
}
// /api/v1/public
apiUri = StringUtils.substringBeforeLast(apiUri, "/");
if (apiUri.length() < 3) { // /api
return null;
}
}
return null;
}
private void logError(HttpServletRequest request) {
try {
String errorCode = "RECEAIIRP010";
String[] msgArgs = new String[1];
msgArgs[0] = request.getRequestURI();
this.logError(request, "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 adapterGroupName, String errorCode,
String errorMsg, Properties prop, Exception e) {
try {
EAIServerManager eaiServerManager = EAIServerManager.getInstance();
String serverName = eaiServerManager.getLocalServerName();
InboundErrorInfoVO errorInfoVO = new InboundErrorInfoVO();
String txId = prop.getProperty(TransactionContextKeys.TRANSACTION_UUID);
if (StringUtils.isEmpty(txId)) {
String instanceid1 = serverName.substring(0, 2);
String instanceid2 = serverName.substring(serverName.length() - 2, serverName.length());
String instid = instanceid1 + instanceid2;
txId = instid + UUIDGenerator.getUUID();
}
errorInfoVO.setEaiSvcSno(txId); // 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());
errorInfoVO.setBwkDataTxt(sb.toString()); // 업무데이터내용
errorInfoVO.setEaiSvrInstNm(serverName); // EAI서버인스턴스명
InboundErrorLogger.error(errorInfoVO);
} catch (Throwable t) {
logger.warn("inbound logging failed", t);
}
}
// 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) {
*
* Map<String, Object> dataMap = new HashMap<>(); dataMap.put("result", 1);
* Map<String, Object> map = new HashMap<>();
*
* switch (asyncMsgStyle) { case "TB_SUC": map.put("rspCode", "TB_SUC_000");
* map.put("rspMsg", "정상"); map.put("data", dataMap); break; case "ONLYDATA":
* map.put("data", dataMap); map.put("success", "true"); break; default:
* map.put("code", "200"); map.put("data", dataMap); map.put("message",
* "정상 처리 되었습니다."); break; }
*
* JSONObject json = new JSONObject(); json.putAll(map);
*
* return json.toJSONString(); }
*/
}
@@ -1,370 +0,0 @@
package com.eactive.eai.adapter.controller;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
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.util.AntPathMatcher;
import org.springframework.web.bind.annotation.RequestMapping;
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.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.HttpDynamicInAdapterManager;
import com.eactive.eai.adapter.http.dynamic.HttpDynamicInAdapterUri;
import com.eactive.eai.adapter.http.dynamic.filter.HttpAdapterFilter;
import com.eactive.eai.adapter.http.dynamic.filter.HttpAdapterFilterFactory;
import com.eactive.eai.adapter.http.dynamic.filter.JwtAuthException;
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.UUIDGenerator;
import com.eactive.eai.inbound.error.InboundErrorInfoVO;
@RestController
public class DJErpApiAdapterController implements HttpAdapterServiceKey {
static Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);
@Autowired
DJErpApiAdapterService service;
/**
* DJErp OAuth 조기 적용을 위한 Controller
*
* @See com.eactive.eai.authserver.config.AuthorizationServerConfig.configure(AuthorizationServerEndpointsConfigurer
* endpoints)
*/
@RequestMapping(value = { "/dj/{path:^(?!oauth).*$}", "/dj/{path:^(?!oauth).*$}/**" })
public ResponseEntity<String> callApi(HttpServletRequest servletRequest, HttpServletResponse servletResponse)
throws Exception {
// /ONLWeb/api/v1/public/getUserInfo.svc
String apiUri = servletRequest.getRequestURI();
// /api/v1/public/getUserInfo.svc
apiUri = StringUtils.removeStart(apiUri, servletRequest.getContextPath());
apiUri = StringUtils.removeEnd(apiUri, "/");
HttpDynamicInAdapterUri adptUri = findAdptUri(apiUri, HttpDynamicInAdapterManager.getInstance(), "");
if (logger.isDebug()) {
logger.debug("ApiAdapterController] service request uri : " + servletRequest.getRequestURI());
}
if (adptUri == null) {
logError(servletRequest);
String errorMsg = MessageUtil.makeJsonErrorMessage(MessageUtil.ERROR_CODE_SERVICE_NOT_FOUND,
"can not find Adapter Uri");
return ResponseEntity.status(HttpStatus.NOT_FOUND).contentType(MediaType.APPLICATION_JSON).body(errorMsg);
}
// Received time , jwhong
long receivedTimeMillis = System.currentTimeMillis(); // 밀리세컨 단위로 보내야함
String receivedTimeStr = String.valueOf(receivedTimeMillis);
String adapterGroupName = adptUri.getAdptGrpName();
String adapterName = adptUri.getAdptName();
AdapterGroupVO adapterGroupVO = AdapterManager.getInstance().getAdapterGroupVO(adapterGroupName);
AdapterVO adapterVO = AdapterManager.getInstance().getAdapterVO(adapterGroupName, adapterName);
if (adapterVO == null) {
String errorMsg = MessageUtil.makeJsonErrorMessage(MessageUtil.ERROR_CODE_AP_ERROR,
"Adapter not found error");
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).contentType(MediaType.APPLICATION_JSON)
.body(errorMsg);
}
Properties httpProp = AdapterPropManager.getInstance().getProperties(adapterVO.getPropGroupName());
String adptMsgType = adapterVO.getAdapterGroupVO().getMessageType();
String encode = StringUtils.defaultIfBlank(adapterGroupVO.getMessageEncode(), "UTF-8");
MediaType mediaType = MediaType.valueOf("application/json;charset=" + encode);
String errorResponseFormat = httpProp.getProperty(ERROR_RESPONSE_FORMAT);
String responseType = httpProp.getProperty(RESPONSE_TYPE, "SYNC");
ResponseEntity responseEntity = null;
Properties transactionProp = new Properties();
// jwhong, put api received time, eaiSvcCode
transactionProp.put(INBOUND_REQUESTED_TIME, receivedTimeStr);
// ** 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 {
// 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);
// if (RESPONSE_TYPE_ASYNC.equals(responseType)) { // table의 값이 ASYNC 로 들어가 있는데
// response_type_async는 ASYN 로 되어 있어 아래처럼 비교함 jwhong 2025
if ("ASYNC".equals(responseType)) {
// responseEntity = ResponseEntity.ok("dummy"); // comment by jwhong
// servletResponse.addHeader("traceId", responseData); // uuid를 response header에
servletResponse.addHeader("traceId",
transactionProp.getProperty(TransactionContextKeys.TRANSACTION_UUID)); // uuid를 response header에
int httpStatus = servletResponse.getStatus();
if (httpStatus == 200) {
// 업체별 aync response message 가 다르다.
String asyncMsgStyle = httpProp.getProperty("ASYNC_RTNMSG_TYPE", "");
String responseBody = "";
boolean encryptAsyncAckApply = StringUtils
.equalsIgnoreCase(httpProp.getProperty("ASYNC_ENCRYPT_ACK", "N"), "Y");
if (asyncMsgStyle == null || asyncMsgStyle.trim().isEmpty()) {
responseBody = makeResponseBodyMsg();
} else {
responseBody = asyncMsgStyle;
}
if (encryptAsyncAckApply) {
responseBody = service.doPostEncryption(responseBody, transactionProp, servletRequest);
}
responseEntity = ResponseEntity.status(httpStatus).contentType(mediaType).body(responseBody);
// jwhong
} else {
responseEntity = ResponseEntity.status(httpStatus).contentType(mediaType).body(responseData);
}
} else {
// responseEntity = ResponseEntity.ok().contentType(mediaType).body(responseData);
// 버즈빌 포인트 적립 처리하기 위하여 정상응답이더라도 응답코드(apiRsltCd) 값이 200이 아닌 경우 처리를 위하여 수정
// Filter에서 설정한 response status값을 responseEntity 생성시 적용 modify by lwk 2025.03.24
servletResponse.addHeader("traceId",
transactionProp.getProperty(TransactionContextKeys.TRANSACTION_UUID)); // uuid를 response header에
int httpStatus = servletResponse.getStatus();
if (httpStatus != 200) {
responseEntity = ResponseEntity.status(httpStatus).contentType(mediaType).body(responseData);
} else {
responseEntity = ResponseEntity.ok().contentType(mediaType).body(responseData);
}
}
} catch (Exception e) {
logger.error(adapterGroupName + "-" + adapterName + ">>" + e.getMessage(), e);
String errorMsg = null;
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 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 = HttpAdapterFilterFactory.createFilter("HMAC_SHA256");
filter.doPostFilter(adapterGroupName, postFilterNames, errorMsg, transactionProp, servletRequest, servletResponse);
}
} finally {
/**
* 로깅 인터셉터에 데이터를 전달하기 위한 처리 이미 거래처리가 끝나서 영향도가 없을만한 servletRequest에 Attribute로 전달
* 추후 더 좋은방법이 생길경우 개선 요망
*/
try {
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);
}
}
return responseEntity;
}
/**
* url에서 뒤쪽 /이후를 제거하면서 찾는다.<br>
* /api/v1/public/getUserInfo.svc
*
* @param apiUri
* @return
*/
private HttpDynamicInAdapterUri findAdptUri(String apiUri, HttpDynamicInAdapterManager manager,
String adapterGrpName) throws Exception {
if (StringUtils.isBlank(apiUri)) {
return null;
}
// * manager 출력
logger.warn("===== adptUriMap 상세 내용 =====");
for (Map.Entry<String, HttpDynamicInAdapterUri> entry : manager.adptUriMap.entrySet()) {
HttpDynamicInAdapterUri uriObj = entry.getValue();
logger.warn("GroupName=" + uriObj.getAdptGrpName() + ", AdapterName=" + uriObj.getAdptName() + ", URI="
+ uriObj.getUri());
}
logger.warn("================================");
//
for (int i = 0; i < 10; i++) {
HttpDynamicInAdapterUri adptUri = manager.getAdptUri(apiUri);
if (adptUri != null) {
if (StringUtils.isNotEmpty(adapterGrpName)) {
if(adptUri.getAdptGrpName().equals(adapterGrpName)) {
return adptUri;
}
} else {
return adptUri;
}
}
// /api/v1/public
apiUri = StringUtils.substringBeforeLast(apiUri, "/");
if (apiUri.length() < 3) { // /api
return null;
}
}
return null;
}
private void logError(HttpServletRequest request) {
InboundErrorInfoVO errorInfoVO = new InboundErrorInfoVO();
EAIServerManager eaiServerManager = EAIServerManager.getInstance();
String serverName = eaiServerManager.getLocalServerName();
String uuid = null;
StringBuffer sb = new StringBuffer();
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(String asyncMsgStyle) {
*
* Map<String, Object> dataMap = new HashMap<>(); dataMap.put("result", 1);
* Map<String, Object> map = new HashMap<>();
*
* switch (asyncMsgStyle) { case "TB_SUC": map.put("rspCode", "TB_SUC_000");
* map.put("rspMsg", "정상"); map.put("data", dataMap); break; case "ONLYDATA":
* map.put("data", dataMap); map.put("success", "true"); break; default:
* map.put("code", "200"); map.put("data", dataMap); map.put("message",
* "정상 처리 되었습니다."); break; }
*
* JSONObject json = new JSONObject(); json.putAll(map);
*
* return json.toJSONString(); }
*/
}
@@ -1,22 +0,0 @@
package com.eactive.eai.adapter.controller;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
@Controller
public class TokenRedirectController {
@PostMapping("/sample/v1/oauth/token")
public void redirectPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 포워딩할 URL을 지정
String targetUrl = "/api/v1/oauth/token";
// 요청과 응답을 다른 URL로 포워딩
request.getRequestDispatcher(targetUrl).forward(request, response);
}
}
@@ -1,19 +1,22 @@
package com.eactive.eai.adapter.interceptor;
import com.eactive.eai.adapter.http.client.HttpClientAdapterServiceKey;
import com.eactive.eai.adapter.http.dynamic.HttpAdapterServiceKey;
import com.eactive.eai.common.TransactionContextKeys;
import com.eactive.eai.common.util.HttpAdapterExtraLogUtil;
import com.eactive.eai.common.util.Logger;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import com.eactive.eai.adapter.http.client.HttpClientAdapterServiceKey;
import com.eactive.eai.adapter.http.dynamic.HttpAdapterServiceKey;
import com.eactive.eai.common.TransactionContextKeys;
import com.eactive.eai.common.logger.HttpAdapterExtraHeaderVo;
import com.eactive.eai.common.util.HttpAdapterExtraLogUtil;
import com.eactive.eai.common.util.Logger;
public class HttpResponseLoggingInterceptor implements HandlerInterceptor {
@@ -55,6 +58,11 @@ public class HttpResponseLoggingInterceptor implements HandlerInterceptor {
logger.debug(String.format( "httpHeader logging Interceptor headerKey=%s, headerValue=%s", headerName, headerValue));
headerMap.put(headerName, headerValue);
}
String body = transactionProp.getProperty(HttpAdapterExtraLogUtil.BODY_FIELD_NAME);
if (StringUtils.isNotEmpty(body)) {
headerMap.put(HttpAdapterExtraLogUtil.BODY_FIELD_NAME, body);
}
int httpStatusCode = response.getStatus();
HttpAdapterExtraLogUtil.insertHttpAdapterExtraLog(uuid, 400, adapterGroupName, adapterName, headerMap, url, method, httpStatusCode);
@@ -1,847 +0,0 @@
package com.eactive.eai.adapter.service;
import com.eactive.eai.adapter.AdapterGroupVO;
import com.eactive.eai.adapter.AdapterPropManager;
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.JwtAuthFilter;
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;
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.json.simple.parser.JSONParser;
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 javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.net.URLDecoder;
import java.util.*;
//for encrypt/decrypt jwhong
import com.eactive.eai.authserver.service.OAuth2Manager;
import com.kjbank.encrypt.exchange.crypto.AES256Cipher;
import com.kjbank.encrypt.exchange.crypto.AESCipher;
import com.kjbank.encrypt.exchange.crypto.AES256GCMCipher;
import java.nio.charset.StandardCharsets;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import org.springframework.security.oauth2.provider.ClientDetails;
import com.nimbusds.jwt.SignedJWT;
import com.eactive.eai.common.context.ElinkTransactionContext;
@Service
public class ApiAdapterService 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"; // jwhong
// HEADER_GROUP JSON에 추가할 항목 정의, 없으면 전체 header 추가
public static final String HEADER_KEYS = "HEADER_KEYS";
public static final String PROPERTIES_NAME_HTTP_REQUEST_METHOD = "httpRequestMethod";
private static final String JSON_CONTENT_TYPE = "application/json";
private static final String JSON_FIELD_NAME = "json-body";
private static final String FILE_GROUP_NAME = "image-file";
private static final String UPLOAD_ROOT_PATH = "UPLOAD_ROOT_PATH";
public static final String PAYLOAD_PARAM_NAME_CLIENT_ID = "client_id"; // jwhong
private boolean encryptResponseApply; // inbound에 대한 응답 암호화 여부 flag
public String callApi(HttpServletRequest request, HttpServletResponse response, Properties httpProp, AdapterGroupVO adapterGroupVO, AdapterVO adapterVO, Properties transactionProp) throws Exception {
int traceLevel = 0;
AdapterPropManager manager = null;
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_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)); // Client IP 추가
transactionProp.put(Processor.REQUEST_ACTION, adapterVO.getAdapterGroupVO().getRefClass());
transactionProp.put(API_PATH, httpProp.getProperty(API_PATH, ""));
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", ""));
transactionProp.put(ENC_PATHS, httpProp.getProperty(ENC_PATHS, ""));
transactionProp.put(ENCRYPT_ALGORITHM, httpProp.getProperty(ENCRYPT_ALGORITHM, "")); //jwhong
if (getHeaders(request).get(HEADER_NAME_CLIENT_ID) != null) { // jwhong
transactionProp.put(HEADER_NAME_CLIENT_ID, getHeaders(request).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 = 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, "")); //jwhong
transactionProp.put(ENCRYPT_AES256_KEY, httpProp.getProperty(ENCRYPT_AES256_KEY, "")); //jwhong
// 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()) ) { // 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);
paramValue = new String(data, encode);
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;
}
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();
}
}
if (message == null) {
message = "";
}
// 위치변경 : 가공되지 않은 Body값을 저장하기 위하여 위쪽으로 이동.
// transactionProp.put(INBOUND_REQUEST_MESSAGE, message);
// 로컬 서비스 호출 ,encoding 처리 추가
String result = (String) service(adptGrpName, adptName, message, transactionProp, request, 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);
}
}
// 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;
}
// jwhong decrypt
private String doPreDecryption(String eaiBody, Properties transactionProp, HttpServletRequest request) throws Exception {
String decryptAlgorithm = transactionProp.getProperty(ENCRYPT_ALGORITHM, "");
String xElinkClientId = transactionProp.getProperty(HEADER_NAME_CLIENT_ID, ""); // 이 값이 OAuth의 client id 값이다 . http header 에 포함되어 온다. jwhong
String encryptBaseKeyType = transactionProp.getProperty(ENCRYPT_BASE_TYPE);
//String inboundToken2 = transactionProp.getProperty(INBOUND_TOKEN, ""); // 이건 token 값이 아니고 authorization 값이다
String inboundToken = "";
String secretAES256Iv = transactionProp.getProperty("ENCRYPT_AES256_IV");
String secretAES256Key = transactionProp.getProperty("ENCRYPT_AES256_KEY");
if ( decryptAlgorithm == "" ) { // 복호화 대상이 아님
return eaiBody;
}
inboundToken = JwtTokenExtractor(request);
xElinkClientId = JwtClientIdExtractor(inboundToken); // token에서 client id를 추출함
// 암호화 key를 token 내용으로 할지 client secret 내용으로 할지 결정함. adapter property에 정의함
// ENCRYPT_ALGORITHM 을 설정했는데 ENCRYPT_BASE_TYPE을 설정안하면 Decrypt 수행시 Exception 발생함
String clientSecret = "";
if ("TOKEN".equalsIgnoreCase(encryptBaseKeyType)) {
clientSecret = inboundToken;
} else if ( "SECRETKEY".equalsIgnoreCase(encryptBaseKeyType)) {
OAuth2Manager manager = OAuth2Manager.getInstance();
ClientDetails clientDetail = manager.getClientDeatilsStore().get(xElinkClientId);
if ( clientDetail != null ) { // 여기서 not null 이라는 것은 apim oauth server에서 발급된 token 임.
// 즉 KJBank 에서 요청이 들어온 것이다.
clientSecret = clientDetail.getClientSecret();
} // 그럼 업체에서 요청이 들어오면?? 업체도 apim oauth server에서 발급된 token을 사용하는것이다.
}
byte[] decryptedMessage = null;
decryptedMessage = doInboundPreDecrypt(eaiBody, decryptAlgorithm,clientSecret,secretAES256Iv, secretAES256Key );
if (decryptedMessage == null) {
throw new Exception("[ApiAdapterService] Decrypt Error : Invalid encrypted message");
}
String resultMessage = new String(decryptedMessage, StandardCharsets.UTF_8 );
return resultMessage;
//return new String(decryptedMessage, StandardCharsets.UTF_8 );
}
// jwhong encrypt
public String doPostEncryption(String eaiBody, Properties transactionProp, HttpServletRequest request) throws Exception {
String decryptAlgorithm = transactionProp.getProperty(ENCRYPT_ALGORITHM, "");
String xElinkClientId = transactionProp.getProperty(HEADER_NAME_CLIENT_ID, ""); // 이 값이 OAuth의 client id 값이다 . http header 에 포함되어 온다. jwhong
String encryptBaseKeyType = transactionProp.getProperty(ENCRYPT_BASE_TYPE);
String inboundToken = "";
String secretAES256Iv = transactionProp.getProperty("ENCRYPT_AES256_IV");
String secretAES256Key = transactionProp.getProperty("ENCRYPT_AES256_KEY");
if ( decryptAlgorithm == "" ) { // 복호화 대상이 아님
return eaiBody;
}
inboundToken = JwtTokenExtractor(request);
xElinkClientId = JwtClientIdExtractor(inboundToken); // token에서 client id를 추출함
// 암호화 key를 token 내용으로 할지 client secret 내용으로 할지 결정함. adapter property에 정의함
// ENCRYPT_ALGORITHM 을 설정했는데 ENCRYPT_BASE_TYPE을 설정안하면 Decrypt 수행시 Exception 발생함
String clientSecret = "";
if ("TOKEN".equalsIgnoreCase(encryptBaseKeyType)) {
clientSecret = inboundToken;
} else if ( "SECRETKEY".equalsIgnoreCase(encryptBaseKeyType)) {
OAuth2Manager manager = OAuth2Manager.getInstance();
ClientDetails clientDetail = manager.getClientDeatilsStore().get(xElinkClientId);
if ( clientDetail != null ) {
clientSecret = clientDetail.getClientSecret();
}
}
String encryptedMessage = null;
encryptedMessage = doInboundPostEncrypt(eaiBody, decryptAlgorithm,clientSecret,secretAES256Iv, secretAES256Key );
if (encryptedMessage == null) {
throw new Exception("[ApiAdapterService] Encrypt Error : Invalid PlainText message");
}
return encryptedMessage;
//String resultMessage = new String(encryptedMessage, StandardCharsets.UTF_8 );
//return resultMessage;
}
private byte[] convertObjectToBytes(Object obj) throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(obj);
oos.flush();
return bos.toByteArray();
}
private byte[] convertObjectToBase64Bytes(Object obj) throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(obj);
oos.flush();
oos.close();
// 직렬화된 byte[] → Base64 문자열 → 다시 byte[]로 변환
String base64String = Base64.getEncoder().encodeToString(bos.toByteArray());
return base64String.getBytes(StandardCharsets.UTF_8);
}
public static Object convertBytesToObject(byte[] bytes) throws IOException, ClassNotFoundException {
try (ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bis)) {
return ois.readObject();
}
}
private byte[] doInboundPreDecrypt(String eaiStringBody, String decryptAlgorithm, String clientSecretKey, String secretAES256Iv, String secretAES256Key) throws Exception {
byte[] decryptedMessage = null;
//String eaiStringBody = new String(eaiBody, StandardCharsets.UTF_8);
eaiStringBody = extractBodyMsg(decryptAlgorithm, eaiStringBody);
if (eaiStringBody == null) {
throw new Exception("[ApiAdapterService] Cannot decrypt Error : input is plain text");
}
//test source
logger.debug("Base64 문자열: " + eaiStringBody);
logger.debug("Base64 문자열 길이: " + eaiStringBody.length());
logger.debug("Base64 문자열 끝: " + eaiStringBody.substring(eaiStringBody.length() - 10));
// Base64 디코딩 테스트
try {
byte[] decoded = Base64.getDecoder().decode(eaiStringBody);
logger.debug("디코딩 성공, 길이: " + decoded.length);
} catch (IllegalArgumentException e) {
logger.debug("Base64 디코딩 실패: " + e.getMessage());
}
// end test source
switch (decryptAlgorithm) {
case "AES128":
case "AES128-TOSS":
try {
String key128 = clientSecretKey.substring(22,38); //togetherEncoder 경우는 substring(44,60) 이네??
//String decryptedStrMessage = AESCipher.decrypt(eaiStringBody, key128);
String decryptedStrMessage = AESCipher.decryptExistException(eaiStringBody, key128);
decryptedMessage = decryptedStrMessage.getBytes(StandardCharsets.UTF_8);
} catch (Exception e) {
e.printStackTrace();
logger.error("HttpClientAdapterServiceRest] AES128 Decryption error=" + e.getMessage());
}
break;
case "AES128-TOGETHER":
try {
String key128 = clientSecretKey.substring(44,60); //togetherEncoder 경우는 substring(44,60) 이네??
//String decryptedStrMessage = AESCipher.decrypt(eaiStringBody, key128);
String decryptedStrMessage = AESCipher.decryptExistException(eaiStringBody, key128);
decryptedMessage = decryptedStrMessage.getBytes(StandardCharsets.UTF_8);
} catch (Exception e) {
e.printStackTrace();
logger.error("HttpClientAdapterServiceRest] AES128 Decryption error=" + e.getMessage());
}
break;
case "AES256":
try {
String key = clientSecretKey.substring(10,42);
String decryptedStrMessage = AES256Cipher.decrypt(eaiStringBody, key);
decryptedMessage = decryptedStrMessage.getBytes(StandardCharsets.UTF_8);
} catch (Exception e) {
e.printStackTrace();
logger.error("HttpClientAdapterServiceRest] AES256 Decryption error=" + e.getMessage());
}
break;
case "AES256-KAKAO":
try {
String decryptedStrMessage = AES256Cipher.decrypt(eaiStringBody, secretAES256Iv, secretAES256Key);
decryptedMessage = decryptedStrMessage.getBytes(StandardCharsets.UTF_8);
} catch (Exception e) {
e.printStackTrace();
logger.error("HttpClientAdapterServiceRest] AES256 Decryption error=" + e.getMessage());
}
break;
case "AES256-TOSS":
try {
String decryptedStrMessage = AES256Cipher.decrypt(eaiStringBody, secretAES256Iv, secretAES256Key);
decryptedMessage = decryptedStrMessage.getBytes(StandardCharsets.UTF_8);
} catch (Exception e) {
e.printStackTrace();
logger.error("HttpClientAdapterServiceRest] AES256 Decryption error=" + e.getMessage());
}
break;
case "AES256GCM":
try {
byte[] key = AES256GCMCipher.generateKey();
AES256GCMCipher cipher = new AES256GCMCipher(key);
byte[] aad = "metadata".getBytes();
String decryptedStrMessage = cipher.decrypt(eaiStringBody , aad);
decryptedMessage = decryptedStrMessage.getBytes(StandardCharsets.UTF_8);
} catch (Exception e) {
e.printStackTrace();
logger.error("HttpClientAdapterServiceRest] AES256GCM Encryption error=" + e.getMessage());
}
break;
default:
try {
decryptedMessage = eaiStringBody.getBytes();
} catch (Exception e) {
e.printStackTrace();
logger.error("HttpClientAdapterServiceRest] doOutboundPostFilter Array copy error=" + e.getMessage());
}
break;
}
return decryptedMessage;
}
private String extractBodyMsg(String decryptAlgorithm, String eaiStringBody) {
String decryptedJsonKey = "";
String decryptedBodyMessage = "";
decryptedJsonKey = getJsonKey(decryptAlgorithm);
try {
JSONParser parser = new JSONParser();
JSONObject jsonObject = (JSONObject) parser.parse(eaiStringBody);
decryptedBodyMessage = (String) jsonObject.get(decryptedJsonKey);
} catch (Exception e) {
e.printStackTrace();
logger.error("HttpClient5AdapterServiceRest] extractBodyMsg error : " + e.getMessage());
}
return decryptedBodyMessage;
}
private String getJsonKey(String Algorithm) {
String jsonKey = "";
switch (Algorithm) {
case "AES128":
case "AES128-TOSS":
jsonKey = "preScreeningRequest";
break;
case "AES128-TOGETHER":
jsonKey = "obpTxData";
break;
case "AES256":
jsonKey = "preScreeningRequest";
break;
case "AES256-KAKAO":
jsonKey = "encrypted_data";
break;
case "AES256-TOSS":
jsonKey = "encryptedData";
break;
case "AES256-NICEON":
jsonKey = "enc_data";
break;
case "AES256GCM":
break;
default:
break;
}
return jsonKey;
}
private String doInboundPostEncrypt(String eaiStringBody, String encryptAlgorithm, String clientSecretKey, String secretAES256Iv, String secretAES256Key) {
//byte[] encryptedMessage = null;
String encryptedStrMessage = null;
String encryptedJsonKey = "";
encryptedJsonKey = getJsonKey(encryptAlgorithm);
switch (encryptAlgorithm) {
case "AES128":
case "AES128-TOSS":
try {
String key128 = clientSecretKey.substring(22,38); //togetherEncoder 경우는 substring(44,60) 이네??
encryptedStrMessage = AESCipher.encrypt(eaiStringBody, key128);
//encryptedMessage = encryptedStrMessage.getBytes(StandardCharsets.UTF_8);
} catch (Exception e) {
e.printStackTrace();
logger.error("HttpClientAdapterServiceRest] AES128 Encryption error=" + e.getMessage());
}
break;
case "AES128-TOGETHER":
try {
String key128 = clientSecretKey.substring(44,60); //togetherEncoder 경우는 substring(44,60) 이네??
encryptedStrMessage = AESCipher.encrypt(eaiStringBody, key128);
//encryptedMessage = encryptedStrMessage.getBytes(StandardCharsets.UTF_8);
} catch (Exception e) {
e.printStackTrace();
logger.error("HttpClientAdapterServiceRest] AES128 Encryption error=" + e.getMessage());
}
break;
case "AES256":
try {
String key = clientSecretKey.substring(10,42);
encryptedStrMessage = AES256Cipher.encrypt(eaiStringBody, key);
//encryptedMessage = encryptedStrMessage.getBytes(StandardCharsets.UTF_8);
} catch (Exception e) {
e.printStackTrace();
logger.error("HttpClientAdapterServiceRest] AES256 Encryption error=" + e.getMessage());
}
break;
case "AES256-KAKAO":
try {
encryptedStrMessage = AES256Cipher.encrypt(eaiStringBody, secretAES256Iv, secretAES256Key);
//encryptedMessage = encryptedStrMessage.getBytes(StandardCharsets.UTF_8);
} catch (Exception e) {
e.printStackTrace();
logger.error("HttpClientAdapterServiceRest] AES256 Encryption error=" + e.getMessage());
}
break;
case "AES256-TOSS":
try {
encryptedStrMessage = AES256Cipher.encrypt(eaiStringBody, secretAES256Iv, secretAES256Key);
//encryptedMessage = encryptedStrMessage.getBytes(StandardCharsets.UTF_8);
} catch (Exception e) {
e.printStackTrace();
logger.error("HttpClientAdapterServiceRest] AES256 Encryption error=" + e.getMessage());
}
break;
case "AES256GCM":
try {
byte[] key = AES256GCMCipher.generateKey();
AES256GCMCipher cipher = new AES256GCMCipher(key);
byte[] aad = "metadata".getBytes();
encryptedStrMessage = cipher.encrypt(eaiStringBody , aad);
//encryptedMessage = encryptedStrMessage.getBytes(StandardCharsets.UTF_8);
} catch (Exception e) {
e.printStackTrace();
logger.error("HttpClientAdapterServiceRest] AES256GCM Encryption error=" + e.getMessage());
}
break;
default:
try {
encryptedStrMessage = eaiStringBody;
//encryptedMessage = eaiStringBody.getBytes();
} catch (Exception e) {
e.printStackTrace();
logger.error("HttpClientAdapterServiceRest] doOutboundPostFilter Array copy error=" + e.getMessage());
}
break;
}
Map<String, Object> map = new HashMap<>();
map.put(encryptedJsonKey, encryptedStrMessage);
JSONObject json = new JSONObject();
json.putAll(map);
return json.toJSONString();
//return encryptedMessage;
}
private String JwtTokenExtractor(HttpServletRequest request) {
String Token = "";
try {
Token = JwtAuthFilter.extractJWTToken(request);
} catch (Exception e) {
logger.error(e.getMessage());
}
logger.debug("Token is: " + Token);
return Token;
}
private String JwtClientIdExtractor(String inboundToken) {
String tokenClientId ="";
try {
SignedJWT signedJWT = SignedJWT.parse(inboundToken);
tokenClientId = (String) signedJWT.getJWTClaimsSet().getClaim(PAYLOAD_PARAM_NAME_CLIENT_ID);
} catch (Exception e) {
logger.error(e.getMessage());
tokenClientId = "";
}
logger.debug("Token Client ID: " + tokenClientId);
return tokenClientId;
}
// jwhong until here
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 Properties getHeaders(HttpServletRequest request) {
Properties prop = new Properties();
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String key = headerNames.nextElement();
String value = request.getHeader(key);
prop.setProperty(key, value);
}
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;
}
}
@@ -0,0 +1,777 @@
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 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.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.JacksonUtil;
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.eactive.eai.util.JsonPathUtil;
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 {
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
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
private ObjectMapper mapper = new ObjectMapper();
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();
Object reqObject = assignHeaderGroupRequestHeaders(adptGrpName, adptName, message,
transactionProp, request, response, adptMsgType);
// 위치변경 : 가공되지 않은 Body값을 저장하기 위하여 위쪽으로 이동.
// transactionProp.put(INBOUND_REQUEST_MESSAGE, message);
ApiKeyExtractFilter apiKeyExtractor = new ApiKeyExtractFilter();
apiKeyExtractor.doPreFilter(adptGrpName, adptName, transactionProp.get(INBOUND_REQUEST_MESSAGE), transactionProp, request, response);
Object result = 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)) {
ObjectNode rootNode = (ObjectNode) JacksonUtil.readTree(result, mapper);
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 = rootNode;
}
}
String strResult = null;
if(result instanceof JsonNode)
strResult = OBJECT_MAPPER.writeValueAsString(result);
else
strResult = (String) result;
return strResult;
}
private Object assignHeaderGroupRequestHeaders(String adptGrpName, String adptName, Object message, Properties prop,
HttpServletRequest request, HttpServletResponse response, String adptMsgType) throws DocumentException, IOException, Exception {
// 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 = "{}";
JsonNode jsonMessageNode = JacksonUtil.readTree(message, mapper);
if (jsonMessageNode == null || !jsonMessageNode.isObject()) {
jsonMessageNode = OBJECT_MAPPER.createObjectNode();
}
ObjectNode jsonNode = (ObjectNode) jsonMessageNode;
JsonNode childNode = jsonNode.get(headerGroupName);
ObjectNode headerJson = (childNode != null && childNode.isObject())
? (ObjectNode) childNode
: OBJECT_MAPPER.createObjectNode();
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.isEmpty()) {
jsonNode.set(headerGroupName, headerJson);
message = jsonNode;
}
} 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 Object 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(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)) {
JsonNode jsonNode = JacksonUtil.readTree(resultMessage, OBJECT_MAPPER); // 또는
// objectMapper.readTree(resultMessage)
JsonNode headerGroup = jsonNode.get(headerGroupName);
if (headerGroup != null && !headerGroup.isNull()) {
Iterator<Map.Entry<String, JsonNode>> fieldIterator = headerGroup.fields();
while (fieldIterator.hasNext()) {
Map.Entry<String, JsonNode> fieldEntry = fieldIterator.next();
JsonNode valueNode = fieldEntry.getValue();
logger.info("header group entry : {} , {}", fieldEntry.getKey(), valueNode);
if (valueNode.isTextual()) {
String value = valueNode.asText();
if (StringUtils.equalsIgnoreCase(fieldEntry.getKey(), "content-type")) {
logger.debug("set content-type {}: {}", fieldEntry.getKey(), value);
response.setContentType(value);
} else if (StringUtils.equalsIgnoreCase(fieldEntry.getKey(), HttpHeaders.CONTENT_LENGTH)) {
logger.debug("skip content-length {}: {}", fieldEntry.getKey(), value);
continue;
} else {
logger.debug("add header {}: {}", fieldEntry.getKey(), value);
response.addHeader(fieldEntry.getKey(), value);
}
} else {
logger.info("fieldEntry value is not String. {}", valueNode);
}
}
} else {
logger.info("headerGroup is null");
}
((ObjectNode) jsonNode).remove(headerGroupName);
return jsonNode;
} 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(StringUtils.equalsIgnoreCase(name, "content-type")) {
logger.debug("set content-type {}: {}", name, value);
response.setContentType(value);
} else if(StringUtils.equalsIgnoreCase(name, "content-length")) {
logger.debug("skip content-length {}: {}", name, value);
continue;
} 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.toLowerCase(), 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;
}
}
@@ -1,984 +0,0 @@
package com.eactive.eai.adapter.service;
import com.eactive.eai.adapter.AdapterGroupVO;
import com.eactive.eai.adapter.AdapterPropManager;
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.filter.AbstractCryptoFilter;
import com.eactive.eai.adapter.http.dynamic.filter.JwtAuthFilter;
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;
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.json.simple.parser.JSONParser;
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 javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.net.URLDecoder;
import java.util.*;
//for encrypt/decrypt jwhong
import com.eactive.eai.authserver.service.OAuth2Manager;
import com.kjbank.encrypt.exchange.crypto.AES256Cipher;
import com.kjbank.encrypt.exchange.crypto.AESCipher;
import com.kjbank.encrypt.exchange.crypto.AES256GCMCipher;
import java.nio.charset.StandardCharsets;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import org.springframework.security.oauth2.provider.ClientDetails;
import com.nimbusds.jwt.SignedJWT;
import com.eactive.eai.common.context.ElinkTransactionContext;
@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"; // jwhong
// HEADER_GROUP JSON에 추가할 항목 정의, 없으면 전체 header 추가
public static final String HEADER_KEYS = "HEADER_KEYS";
public static final String PROPERTIES_NAME_HTTP_REQUEST_METHOD = "httpRequestMethod";
private static final String JSON_CONTENT_TYPE = "application/json";
private static final String JSON_FIELD_NAME = "json-body";
private static final String FILE_GROUP_NAME = "image-file";
private static final String UPLOAD_ROOT_PATH = "UPLOAD_ROOT_PATH";
public static final String PAYLOAD_PARAM_NAME_CLIENT_ID = "client_id"; // jwhong
private boolean encryptResponseApply; // inbound에 대한 응답 암호화 여부 flag
public String callApi(HttpServletRequest request, HttpServletResponse response, Properties httpProp, AdapterGroupVO adapterGroupVO, AdapterVO adapterVO, Properties transactionProp) throws Exception {
int traceLevel = 0;
AdapterPropManager manager = null;
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)); // 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 (getHeaders(request).get(HEADER_NAME_CLIENT_ID) != null) { // jwhong
transactionProp.put(HEADER_NAME_CLIENT_ID, getHeaders(request).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 = 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, "")); //jwhong
transactionProp.put(ENCRYPT_AES256_KEY, httpProp.getProperty(ENCRYPT_AES256_KEY, "")); //jwhong
transactionProp.put(AbstractCryptoFilter.PROP_MODULE_NAME, httpProp.getProperty(AbstractCryptoFilter.PROP_MODULE_NAME, ""));
// 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()) ) { // 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;
}
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();
}
}
if (message == null) {
message = "";
}
// 위치변경 : 가공되지 않은 Body값을 저장하기 위하여 위쪽으로 이동.
// transactionProp.put(INBOUND_REQUEST_MESSAGE, message);
// 로컬 서비스 호출 ,encoding 처리 추가
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);
}
}
// 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;
}
/**
* 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);
}
}
// jwhong decrypt
private String doPreDecryption(String eaiBody, Properties transactionProp, HttpServletRequest request) throws Exception {
String decryptAlgorithm = transactionProp.getProperty(ENCRYPT_ALGORITHM, "");
String xElinkClientId = transactionProp.getProperty(HEADER_NAME_CLIENT_ID, ""); // 이 값이 OAuth의 client id 값이다 . http header 에 포함되어 온다. jwhong
String encryptBaseKeyType = transactionProp.getProperty(ENCRYPT_BASE_TYPE);
//String inboundToken2 = transactionProp.getProperty(INBOUND_TOKEN, ""); // 이건 token 값이 아니고 authorization 값이다
String inboundToken = "";
String secretAES256Iv = transactionProp.getProperty("ENCRYPT_AES256_IV");
String secretAES256Key = transactionProp.getProperty("ENCRYPT_AES256_KEY");
if ( decryptAlgorithm == "" ) { // 복호화 대상이 아님
return eaiBody;
}
inboundToken = JwtTokenExtractor(request);
xElinkClientId = JwtClientIdExtractor(inboundToken); // token에서 client id를 추출함
// 암호화 key를 token 내용으로 할지 client secret 내용으로 할지 결정함. adapter property에 정의함
// ENCRYPT_ALGORITHM 을 설정했는데 ENCRYPT_BASE_TYPE을 설정안하면 Decrypt 수행시 Exception 발생함
String clientSecret = "";
if ("TOKEN".equalsIgnoreCase(encryptBaseKeyType)) {
clientSecret = inboundToken;
} else if ( "SECRETKEY".equalsIgnoreCase(encryptBaseKeyType)) {
OAuth2Manager manager = OAuth2Manager.getInstance();
ClientDetails clientDetail = manager.getClientDeatilsStore().get(xElinkClientId);
if ( clientDetail != null ) { // 여기서 not null 이라는 것은 apim oauth server에서 발급된 token 임.
// 즉 KJBank 에서 요청이 들어온 것이다.
clientSecret = clientDetail.getClientSecret();
} // 그럼 업체에서 요청이 들어오면?? 업체도 apim oauth server에서 발급된 token을 사용하는것이다.
}
byte[] decryptedMessage = null;
decryptedMessage = doInboundPreDecrypt(eaiBody, decryptAlgorithm,clientSecret,secretAES256Iv, secretAES256Key );
if (decryptedMessage == null) {
throw new Exception("[ApiAdapterService] Decrypt Error : Invalid encrypted message");
}
String resultMessage = new String(decryptedMessage, StandardCharsets.UTF_8 );
return resultMessage;
//return new String(decryptedMessage, StandardCharsets.UTF_8 );
}
// jwhong encrypt
public String doPostEncryption(String eaiBody, Properties transactionProp, HttpServletRequest request) throws Exception {
String decryptAlgorithm = transactionProp.getProperty(ENCRYPT_ALGORITHM, "");
String xElinkClientId = transactionProp.getProperty(HEADER_NAME_CLIENT_ID, ""); // 이 값이 OAuth의 client id 값이다 . http header 에 포함되어 온다. jwhong
String encryptBaseKeyType = transactionProp.getProperty(ENCRYPT_BASE_TYPE);
String inboundToken = "";
String secretAES256Iv = transactionProp.getProperty("ENCRYPT_AES256_IV");
String secretAES256Key = transactionProp.getProperty("ENCRYPT_AES256_KEY");
if ( decryptAlgorithm == "" ) { // 복호화 대상이 아님
return eaiBody;
}
inboundToken = JwtTokenExtractor(request);
xElinkClientId = JwtClientIdExtractor(inboundToken); // token에서 client id를 추출함
// 암호화 key를 token 내용으로 할지 client secret 내용으로 할지 결정함. adapter property에 정의함
// ENCRYPT_ALGORITHM 을 설정했는데 ENCRYPT_BASE_TYPE을 설정안하면 Decrypt 수행시 Exception 발생함
String clientSecret = "";
if ("TOKEN".equalsIgnoreCase(encryptBaseKeyType)) {
clientSecret = inboundToken;
} else if ( "SECRETKEY".equalsIgnoreCase(encryptBaseKeyType)) {
OAuth2Manager manager = OAuth2Manager.getInstance();
ClientDetails clientDetail = manager.getClientDeatilsStore().get(xElinkClientId);
if ( clientDetail != null ) {
clientSecret = clientDetail.getClientSecret();
}
}
String encryptedMessage = null;
encryptedMessage = doInboundPostEncrypt(eaiBody, decryptAlgorithm,clientSecret,secretAES256Iv, secretAES256Key );
if (encryptedMessage == null) {
throw new Exception("[ApiAdapterService] Encrypt Error : Invalid PlainText message");
}
return encryptedMessage;
//String resultMessage = new String(encryptedMessage, StandardCharsets.UTF_8 );
//return resultMessage;
}
private byte[] convertObjectToBytes(Object obj) throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(obj);
oos.flush();
return bos.toByteArray();
}
private byte[] convertObjectToBase64Bytes(Object obj) throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(obj);
oos.flush();
oos.close();
// 직렬화된 byte[] → Base64 문자열 → 다시 byte[]로 변환
String base64String = Base64.getEncoder().encodeToString(bos.toByteArray());
return base64String.getBytes(StandardCharsets.UTF_8);
}
public static Object convertBytesToObject(byte[] bytes) throws IOException, ClassNotFoundException {
try (ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bis)) {
return ois.readObject();
}
}
private byte[] doInboundPreDecrypt(String eaiStringBody, String decryptAlgorithm, String clientSecretKey, String secretAES256Iv, String secretAES256Key) throws Exception {
byte[] decryptedMessage = null;
//String eaiStringBody = new String(eaiBody, StandardCharsets.UTF_8);
eaiStringBody = extractBodyMsg(decryptAlgorithm, eaiStringBody);
if (eaiStringBody == null) {
throw new Exception("[ApiAdapterService] Cannot decrypt Error : input is plain text");
}
//test source
logger.debug("Base64 문자열: " + eaiStringBody);
logger.debug("Base64 문자열 길이: " + eaiStringBody.length());
logger.debug("Base64 문자열 끝: " + eaiStringBody.substring(eaiStringBody.length() - 10));
// Base64 디코딩 테스트
try {
byte[] decoded = Base64.getDecoder().decode(eaiStringBody);
logger.debug("디코딩 성공, 길이: " + decoded.length);
} catch (IllegalArgumentException e) {
logger.debug("Base64 디코딩 실패: " + e.getMessage());
}
// end test source
switch (decryptAlgorithm) {
case "AES128":
case "AES128-TOSS":
try {
String key128 = clientSecretKey.substring(22,38); //togetherEncoder 경우는 substring(44,60) 이네??
//String decryptedStrMessage = AESCipher.decrypt(eaiStringBody, key128);
String decryptedStrMessage = AESCipher.decryptExistException(eaiStringBody, key128);
decryptedMessage = decryptedStrMessage.getBytes(StandardCharsets.UTF_8);
} catch (Exception e) {
e.printStackTrace();
logger.error("HttpClientAdapterServiceRest] AES128 Decryption error=" + e.getMessage());
}
break;
case "AES128-TOGETHER":
try {
String key128 = clientSecretKey.substring(44,60); //togetherEncoder 경우는 substring(44,60) 이네??
//String decryptedStrMessage = AESCipher.decrypt(eaiStringBody, key128);
String decryptedStrMessage = AESCipher.decryptExistException(eaiStringBody, key128);
decryptedMessage = decryptedStrMessage.getBytes(StandardCharsets.UTF_8);
} catch (Exception e) {
e.printStackTrace();
logger.error("HttpClientAdapterServiceRest] AES128 Decryption error=" + e.getMessage());
}
break;
case "AES256":
try {
String key = clientSecretKey.substring(10,42);
String decryptedStrMessage = AES256Cipher.decrypt(eaiStringBody, key);
decryptedMessage = decryptedStrMessage.getBytes(StandardCharsets.UTF_8);
} catch (Exception e) {
e.printStackTrace();
logger.error("HttpClientAdapterServiceRest] AES256 Decryption error=" + e.getMessage());
}
break;
case "AES256-KAKAO":
try {
String decryptedStrMessage = AES256Cipher.decrypt(eaiStringBody, secretAES256Iv, secretAES256Key);
decryptedMessage = decryptedStrMessage.getBytes(StandardCharsets.UTF_8);
} catch (Exception e) {
e.printStackTrace();
logger.error("HttpClientAdapterServiceRest] AES256 Decryption error=" + e.getMessage());
}
break;
case "AES256-TOSS":
try {
String decryptedStrMessage = AES256Cipher.decrypt(eaiStringBody, secretAES256Iv, secretAES256Key);
decryptedMessage = decryptedStrMessage.getBytes(StandardCharsets.UTF_8);
} catch (Exception e) {
e.printStackTrace();
logger.error("HttpClientAdapterServiceRest] AES256 Decryption error=" + e.getMessage());
}
break;
case "AES256GCM":
try {
byte[] key = AES256GCMCipher.generateKey();
AES256GCMCipher cipher = new AES256GCMCipher(key);
byte[] aad = "metadata".getBytes();
String decryptedStrMessage = cipher.decrypt(eaiStringBody , aad);
decryptedMessage = decryptedStrMessage.getBytes(StandardCharsets.UTF_8);
} catch (Exception e) {
e.printStackTrace();
logger.error("HttpClientAdapterServiceRest] AES256GCM Encryption error=" + e.getMessage());
}
break;
default:
try {
decryptedMessage = eaiStringBody.getBytes();
} catch (Exception e) {
e.printStackTrace();
logger.error("HttpClientAdapterServiceRest] doOutboundPostFilter Array copy error=" + e.getMessage());
}
break;
}
return decryptedMessage;
}
private String extractBodyMsg(String decryptAlgorithm, String eaiStringBody) {
String decryptedJsonKey = "";
String decryptedBodyMessage = "";
decryptedJsonKey = getJsonKey(decryptAlgorithm);
try {
JSONParser parser = new JSONParser();
JSONObject jsonObject = (JSONObject) parser.parse(eaiStringBody);
decryptedBodyMessage = (String) jsonObject.get(decryptedJsonKey);
} catch (Exception e) {
e.printStackTrace();
logger.error("HttpClient5AdapterServiceRest] extractBodyMsg error : " + e.getMessage());
}
return decryptedBodyMessage;
}
private String getJsonKey(String Algorithm) {
String jsonKey = "";
switch (Algorithm) {
case "AES128":
case "AES128-TOSS":
jsonKey = "preScreeningRequest";
break;
case "AES128-TOGETHER":
jsonKey = "obpTxData";
break;
case "AES256":
jsonKey = "preScreeningRequest";
break;
case "AES256-KAKAO":
jsonKey = "encrypted_data";
break;
case "AES256-TOSS":
jsonKey = "encryptedData";
break;
case "AES256-NICEON":
jsonKey = "enc_data";
break;
case "AES256GCM":
break;
default:
break;
}
return jsonKey;
}
private String doInboundPostEncrypt(String eaiStringBody, String encryptAlgorithm, String clientSecretKey, String secretAES256Iv, String secretAES256Key) {
//byte[] encryptedMessage = null;
String encryptedStrMessage = null;
String encryptedJsonKey = "";
encryptedJsonKey = getJsonKey(encryptAlgorithm);
switch (encryptAlgorithm) {
case "AES128":
case "AES128-TOSS":
try {
String key128 = clientSecretKey.substring(22,38); //togetherEncoder 경우는 substring(44,60) 이네??
encryptedStrMessage = AESCipher.encrypt(eaiStringBody, key128);
//encryptedMessage = encryptedStrMessage.getBytes(StandardCharsets.UTF_8);
} catch (Exception e) {
e.printStackTrace();
logger.error("HttpClientAdapterServiceRest] AES128 Encryption error=" + e.getMessage());
}
break;
case "AES128-TOGETHER":
try {
String key128 = clientSecretKey.substring(44,60); //togetherEncoder 경우는 substring(44,60) 이네??
encryptedStrMessage = AESCipher.encrypt(eaiStringBody, key128);
//encryptedMessage = encryptedStrMessage.getBytes(StandardCharsets.UTF_8);
} catch (Exception e) {
e.printStackTrace();
logger.error("HttpClientAdapterServiceRest] AES128 Encryption error=" + e.getMessage());
}
break;
case "AES256":
try {
String key = clientSecretKey.substring(10,42);
encryptedStrMessage = AES256Cipher.encrypt(eaiStringBody, key);
//encryptedMessage = encryptedStrMessage.getBytes(StandardCharsets.UTF_8);
} catch (Exception e) {
e.printStackTrace();
logger.error("HttpClientAdapterServiceRest] AES256 Encryption error=" + e.getMessage());
}
break;
case "AES256-KAKAO":
try {
encryptedStrMessage = AES256Cipher.encrypt(eaiStringBody, secretAES256Iv, secretAES256Key);
//encryptedMessage = encryptedStrMessage.getBytes(StandardCharsets.UTF_8);
} catch (Exception e) {
e.printStackTrace();
logger.error("HttpClientAdapterServiceRest] AES256 Encryption error=" + e.getMessage());
}
break;
case "AES256-TOSS":
try {
encryptedStrMessage = AES256Cipher.encrypt(eaiStringBody, secretAES256Iv, secretAES256Key);
//encryptedMessage = encryptedStrMessage.getBytes(StandardCharsets.UTF_8);
} catch (Exception e) {
e.printStackTrace();
logger.error("HttpClientAdapterServiceRest] AES256 Encryption error=" + e.getMessage());
}
break;
case "AES256GCM":
try {
byte[] key = AES256GCMCipher.generateKey();
AES256GCMCipher cipher = new AES256GCMCipher(key);
byte[] aad = "metadata".getBytes();
encryptedStrMessage = cipher.encrypt(eaiStringBody , aad);
//encryptedMessage = encryptedStrMessage.getBytes(StandardCharsets.UTF_8);
} catch (Exception e) {
e.printStackTrace();
logger.error("HttpClientAdapterServiceRest] AES256GCM Encryption error=" + e.getMessage());
}
break;
default:
try {
encryptedStrMessage = eaiStringBody;
//encryptedMessage = eaiStringBody.getBytes();
} catch (Exception e) {
e.printStackTrace();
logger.error("HttpClientAdapterServiceRest] doOutboundPostFilter Array copy error=" + e.getMessage());
}
break;
}
Map<String, Object> map = new HashMap<>();
map.put(encryptedJsonKey, encryptedStrMessage);
JSONObject json = new JSONObject();
json.putAll(map);
return json.toJSONString();
//return encryptedMessage;
}
private String JwtTokenExtractor(HttpServletRequest request) {
String Token = "";
try {
Token = JwtAuthFilter.extractJWTToken(request);
} catch (Exception e) {
logger.error(e.getMessage());
}
logger.debug("Token is: " + Token);
return Token;
}
private String JwtClientIdExtractor(String inboundToken) {
String tokenClientId ="";
try {
SignedJWT signedJWT = SignedJWT.parse(inboundToken);
tokenClientId = (String) signedJWT.getJWTClaimsSet().getClaim(PAYLOAD_PARAM_NAME_CLIENT_ID);
} catch (Exception e) {
logger.error(e.getMessage());
tokenClientId = "";
}
logger.debug("Token Client ID: " + tokenClientId);
return tokenClientId;
}
// jwhong until here
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,60 @@
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";
}
}
@@ -0,0 +1,50 @@
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,6 +1,8 @@
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;
@@ -34,7 +36,9 @@ import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenCo
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;
import com.eactive.eai.agent.encryption.EncryptionManager;
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;
@@ -128,10 +132,11 @@ public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdap
String keyAlias = "elink-oauth";
String keyPassword = "elink1234";
if (vo != null) {
keystorePath = vo.getProperty(PROP_KEYSTORE_PATH);
keystorePassword = vo.getProperty(PROP_KEYSTORE_PW);
keyAlias = vo.getProperty(PROP_KEY_ALIAS);
keyPassword = vo.getProperty(PROP_KEY_PW);
EncryptionManager encManager = EncryptionManager.getInstance();
keystorePath = encManager.decryptDBData(vo.getProperty(PROP_KEYSTORE_PATH));
keystorePassword = encManager.decryptDBData(vo.getProperty(PROP_KEYSTORE_PW));
keyAlias = encManager.decryptDBData(vo.getProperty(PROP_KEY_ALIAS));
keyPassword = encManager.decryptDBData(vo.getProperty(PROP_KEY_PW));
} else {
logger.warn("The properties has not been set.[" + PROP_GROUP_AUTH_SERVER + "]");
}
@@ -139,9 +144,10 @@ public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdap
Resource keystore = new ClassPathResource(keystorePath);
KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(keystore, keystorePassword.toCharArray());
KeyPair keyPair = keyStoreKeyFactory.getKeyPair(keyAlias, keyPassword.toCharArray());
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setKeyPair(keyPair);
return converter;
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
return new PssJwtAccessTokenConverter(privateKey, publicKey);
}
@@ -1,200 +0,0 @@
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 DJErpOAuth2AccessTokenRequest implements Serializable {
public class DJBOAuth2AccessTokenRequest 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 DJErpOAuth2AccessTokenResponse implements Serializable {
public class DJBOAuth2AccessTokenResponse implements Serializable {
@JsonProperty("access_token")
private String accessToken;
@@ -30,7 +30,7 @@ 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.UnkownMessageLogUtils;
import com.eactive.eai.adapter.http.dynamic.UnknownMessageLogUtils;
import com.eactive.eai.adapter.http.dynamic.filter.JwtAuthException;
import com.eactive.eai.authserver.config.RequestContextData;
import com.eactive.eai.authserver.dao.TokenIssuanceLogDAO;
@@ -46,7 +46,7 @@ import com.eactive.eai.data.entity.onl.authserver.TokenIssuanceLog;
import com.fasterxml.jackson.databind.ObjectMapper;
@Controller
public class DJErpOAuth2Controller {
public class DJBOAuth2Controller {
public static Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT);
private static final String HEADER_TRACEID = "traceId";
@@ -55,20 +55,20 @@ public class DJErpOAuth2Controller {
@Autowired
private TokenIssuanceLogDAO tokenIssuanceLogDAO;
@RequestMapping(value = { "/dj/oauth/token", "/dj/oauth2/token" }, method = RequestMethod.POST,
@RequestMapping(value = { "/*/oauth/token", "/*/oauth2/token" }, method = RequestMethod.POST,
consumes = "application/json", produces = "application/json; charset=UTF-8")
@ResponseBody
public ResponseEntity<?> tokenJson(@RequestBody DJErpOAuth2AccessTokenRequest tokenRequest,
public ResponseEntity<?> tokenJson(@RequestBody DJBOAuth2AccessTokenRequest tokenRequest,
HttpServletRequest request, HttpServletResponse response) {
return issueToken(tokenRequest, request, response);
}
@RequestMapping(value = { "/dj/oauth/token", "/dj/oauth2/token" }, method = RequestMethod.POST,
@RequestMapping(value = { "/*/oauth/token", "/*/oauth2/token" }, method = RequestMethod.POST,
consumes = "application/x-www-form-urlencoded", produces = "application/json; charset=UTF-8")
@ResponseBody
public ResponseEntity<?> tokenForm(@RequestParam Map<String, String> params,
HttpServletRequest request, HttpServletResponse response) {
DJErpOAuth2AccessTokenRequest tokenRequest = new DJErpOAuth2AccessTokenRequest();
DJBOAuth2AccessTokenRequest tokenRequest = new DJBOAuth2AccessTokenRequest();
tokenRequest.setGrantType(params.get("grant_type"));
tokenRequest.setClientId(params.get("client_id"));
tokenRequest.setClientSecret(params.get("client_secret"));
@@ -76,11 +76,11 @@ public class DJErpOAuth2Controller {
return issueToken(tokenRequest, request, response);
}
@GetMapping(value = { "/dj/oauth/token", "/dj/oauth2/token" }, produces = "application/json; charset=UTF-8")
@GetMapping(value = { "/*/oauth/token", "/*/oauth2/token" }, produces = "application/json; charset=UTF-8")
@ResponseBody
public ResponseEntity<?> tokenGet(@RequestParam Map<String, String> params,
HttpServletRequest request, HttpServletResponse response) {
DJErpOAuth2AccessTokenRequest tokenRequest = new DJErpOAuth2AccessTokenRequest();
DJBOAuth2AccessTokenRequest tokenRequest = new DJBOAuth2AccessTokenRequest();
tokenRequest.setGrantType(params.get("grant_type"));
tokenRequest.setClientId(params.get("client_id"));
tokenRequest.setClientSecret(params.get("client_secret"));
@@ -88,7 +88,7 @@ public class DJErpOAuth2Controller {
return issueToken(tokenRequest, request, response);
}
private ResponseEntity<?> issueToken(DJErpOAuth2AccessTokenRequest tokenRequest,
private ResponseEntity<?> issueToken(DJBOAuth2AccessTokenRequest tokenRequest,
HttpServletRequest request, HttpServletResponse response) {
if (logger.isDebug()) {
@@ -143,7 +143,7 @@ public class DJErpOAuth2Controller {
ResponseEntity<OAuth2AccessToken> result = tokenEndpoint().postAccessToken(principal, authorizationParameters);
OAuth2AccessToken token = result.getBody();
DJErpOAuth2AccessTokenResponse responseToken = new DJErpOAuth2AccessTokenResponse();
DJBOAuth2AccessTokenResponse responseToken = new DJBOAuth2AccessTokenResponse();
responseToken.setAccessToken(token.getValue());
responseToken.setTokenType(token.getTokenType());
responseToken.setExpiresIn(Long.valueOf(token.getExpiresIn()));
@@ -164,7 +164,7 @@ public class DJErpOAuth2Controller {
Properties logProp = new Properties();
logProp.put("clientId", clientId);
UnkownMessageLogUtils.logUnkownMessage(this.getClass().getSimpleName(), this.getClass().getSimpleName(),
UnknownMessageLogUtils.logUnkownMessage(this.getClass().getSimpleName(), this.getClass().getSimpleName(),
tokenRequest.toString(), logProp, request, response, "RECEAIIRP301", e);
String errorJson = MessageUtil.makeJsonErrorMessage(MessageUtil.ERROR_CODE_AUTH_FAIL, e.getMessage());
@@ -179,7 +179,7 @@ public class DJErpOAuth2Controller {
Properties logProp = new Properties();
logProp.put("clientId", clientId);
UnkownMessageLogUtils.logUnkownMessage(this.getClass().getSimpleName(), this.getClass().getSimpleName(),
UnknownMessageLogUtils.logUnkownMessage(this.getClass().getSimpleName(), this.getClass().getSimpleName(),
tokenRequest.toString(), logProp, request, response, "RECEAIIRP301", e);
String errorJson = MessageUtil.makeJsonErrorMessage(MessageUtil.ERROR_CODE_AUTH_FAIL, "Unauthorized. [Unknown]");
@@ -1,63 +0,0 @@
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 + "]";
}
}
@@ -1,105 +0,0 @@
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 + "]";
}
}
@@ -1,360 +0,0 @@
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);
}
}
@@ -1,49 +0,0 @@
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 + "]";
}
}
@@ -1,72 +0,0 @@
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 + "]";
}
}
@@ -1,222 +0,0 @@
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);
}
}
@@ -0,0 +1,48 @@
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;
}
}
@@ -0,0 +1,122 @@
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;
}
}
@@ -1,52 +0,0 @@
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);
}
}
@@ -0,0 +1,62 @@
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;
}
}
@@ -1,897 +0,0 @@
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,136 @@
package com.eactive.eai.custom.adapter.http.client.impl;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Properties;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase;
import com.eactive.eai.adapter.http.client.HttpClientAdapterServiceKey;
import com.eactive.eai.adapter.http.client.impl.HttpClient5AdapterServiceRest;
import com.eactive.eai.common.property.PropManager;
import com.eactive.eai.common.util.MaskingUtils;
/**
* 1. 기능 : EAI HTTP OutBound 용 어댑터로 수동 시스템의 HTTP 웹 컴포넌트를 GET/POST 방식으로 호출할 수 있는
* 기능을 제공한다.<br>
* 2. 처리 개요 : <br>
* * - 2020-09-25: Http Header 받아서 처리할 수 있게 기능 추가<br>
* 3. 주의사항 <br>
*
* @author :
* @version : v 1.0.0
* @see : HttpClientAdapterServiceFactory.java,
* HttpClientAdapterServiceSupport.java
* @since :
*
*/
public class HttpClient5AdapterServiceBigTech extends HttpClient5AdapterServiceRest
implements HttpClientAdapterServiceKey {
private static final char[] _HEX_LOWER = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd',
'e', 'f' };
private static String hexEncode(byte[] data) {
if (data == null) {
return null;
}
int length = data.length;
char[] encoded = new char[length << 1];
for (int i = 0, j = 0; i < length; i++) {
encoded[j++] = _HEX_LOWER[(0xF0 & data[i]) >>> 4];
encoded[j++] = _HEX_LOWER[0x0F & data[i]];
}
return new String(encoded);
} // end of hex_encode
private static final String _BTP_CHARSET = "UTF-8";
private static final String _BTP_ALGORITHM = "HmacSHA256";
private static final String _BTP_ACCESS_KEY = "{BTP_ACCESS_KEY}";
private static final String _BTP_SECRET_KEY = "{BTP_SECRET_KEY}";
private static byte[] mac(String charset, String algorithm, String timestamp, String accessKey, String secretKey,
String url) throws UnsupportedEncodingException, NoSuchAlgorithmException, InvalidKeyException {
String message = String.join(" ", Arrays.asList(url, timestamp, accessKey));
SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes(charset), algorithm);
Mac mac = Mac.getInstance(algorithm);
mac.init(secretKeySpec);
return mac.doFinal(message.getBytes(charset));
}// end of mac
private static String macHex(String charset, String algorithm, String timestamp, String accessKey, String secretKey,
String url) throws InvalidKeyException, UnsupportedEncodingException, NoSuchAlgorithmException {
return hexEncode(mac(charset, algorithm, timestamp, accessKey, secretKey, url));
}// end of macHex
protected void assignRequestHeaders(HttpUriRequestBase method, Object httpHeader, Properties prop, Properties tempProp) {
super.assignRequestHeaders(method, httpHeader, prop, tempProp);
try {
String adapterGroupName = tempProp.getProperty(ADAPTER_GROUP_NAME);
Properties customProp = PropManager.getInstance().getProperties(adapterGroupName);
String charset = customProp.getProperty("_BTP_CHARSET", _BTP_CHARSET);
String algorithm = customProp.getProperty("_BTP_ALGORITHM", _BTP_ALGORITHM);
String timestamp = System.currentTimeMillis() + "";
String accessKey = customProp.getProperty("_BTP_ACCESS_KEY", _BTP_ACCESS_KEY);
String secretKey = customProp.getProperty("_BTP_SECRET_KEY", _BTP_SECRET_KEY);
if(logger.isDebug()) {
logger.debug("charset={}", charset);
logger.debug("algorithm={}", algorithm);
logger.debug("timestamp={}", timestamp);
logger.debug("accessKey={}", accessKey);
logger.debug("secretKey={}", MaskingUtils.maskPassword(secretKey));
logger.debug("url={}", method.getRequestUri());
}
String signature = macHex(charset// charset
, algorithm// algorithm
, timestamp// timestamp
, accessKey// accessKey
, secretKey// secretKey
, method.getRequestUri());
method.setHeader("x-btp-access-key", accessKey);
method.setHeader("x-btp-timestamp", timestamp);
method.setHeader("x-btp-signature-v1", signature);
} catch (InvalidKeyException | UnsupportedEncodingException | NoSuchAlgorithmException e) {
throw new RuntimeException("BigTech 헤더 설정 실패", e);
}
}
// public static void main(String[] args) {
// try {
// String url = "/rest/v1/service?param0=&param1=";
// String timestamp = System.currentTimeMillis()+"";
// String accessKey = _BTP_ACCESS_KEY;
// String secretKey = _BTP_SECRET_KEY;
// String signature = macHex(
// _BTP_CHARSET//charset
// , _BTP_ALGORITHM//algorithm
// , timestamp// timestamp
// , _BTP_ACCESS_KEY//accessKey
// , _BTP_SECRET_KEY//secretKey
// , url
// );
// System.out.println(String.format("charset=%s", _BTP_CHARSET));
// System.out.println(String.format("algorithm=%s", _BTP_ALGORITHM));
// System.out.println(String.format("accessKey=%s", accessKey));
// System.out.println(String.format("secretKey=%s", secretKey));
// System.out.println(String.format("timestamp=%s", timestamp));
// System.out.println(String.format("signature=%s", signature));
// } catch (Exception e) {
// e.printStackTrace();
// }
// }// end of main
}
@@ -0,0 +1,147 @@
package com.eactive.eai.custom.adapter.http.client.impl;
import java.util.Properties;
import org.apache.commons.lang3.StringUtils;
import org.dom4j.Node;
import org.json.simple.JSONObject;
import com.eactive.eai.adapter.http.client.HttpClientAdapterServiceKey;
import com.eactive.eai.adapter.http.client.impl.filter.HttpClient5AdapterFilterFactory;
import com.eactive.eai.adapter.http.client.impl.filter.HttpClientAdapterFilter;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
/**
* 1. 기능 : HttpClient5AdapterServiceRest 호출 전후 Filter 적용할 수 있는 기능을 제공한다.<br>
* 2. 처리 개요 : <br>
* 3. 주의사항 <br>
*
* @author :
* @version : v 1.0.0
* @see : HttpClientAdapterServiceFactory.java,
* HttpClientAdapterServiceSupport.java, HttpClient5AdapterServiceRest.java
* @since :
*
*/
public class HttpClient5AdapterServiceBigTechAddFilter extends HttpClient5AdapterServiceBigTech
implements HttpClientAdapterServiceKey {
// 요청전 수행할 필터(쉼표(,)로 구분)
static final String PRE_FILTERS = "PRE_FILTERS";
// 요청후 수행할 필터(쉼표(,)로 구분)
static final String POST_FILTERS = "POST_FILTERS";
// // Adapter에서 Exception이 발생한 경우, 처리할 필터
// static final String EXCEPTION_FILTER = "EXCEPTION_FILTER";
private ObjectMapper mapper = new ObjectMapper();
/**
* 1. 기능 : HttpClient 호출 전후 Filter 적용용 2. 처리 개요 : <br>
* 3. 주의사항 <br>
*
* @param prop Http Adapter 속성 정보
* @return 반환 된 Object
* @exception Exception 수동 시스템 간 통신 중 발생
*/
public Object execute(Properties prop, Object data, Properties tempProp) throws Exception {
String adptGrpName = tempProp.getProperty(ADAPTER_GROUP_NAME);
String adptName = tempProp.getProperty(ADAPTER_NAME);
data = doPreFilters(adptGrpName, adptName, prop, data, tempProp);
Object adapterResponse = super.execute(prop, data, tempProp);
adapterResponse = doPostFilters(adptGrpName, adptName, prop, adapterResponse, tempProp);
if (adapterResponse instanceof JSONObject)
adapterResponse = ((JSONObject) adapterResponse).toJSONString();
else if (adapterResponse instanceof ObjectNode)
adapterResponse = mapper.writeValueAsString((ObjectNode) adapterResponse);
else if (adapterResponse instanceof Node)
adapterResponse = ((Node) adapterResponse).asXML();
return adapterResponse;
}
protected Object doPreFilters(String adptGrpName, String adptName, Properties prop, Object message,
Properties tempProp) throws Exception {
// boolean isSetCommonFilterInAdapterProp = false;
// 1. adapter에 설정된 필터 수행
String preFiltersStr = prop.getProperty(PRE_FILTERS);
if (StringUtils.isNotEmpty(preFiltersStr)) {
String[] preFilters = StringUtils.split(preFiltersStr, ",");
for (String filterName : preFilters) {
if (StringUtils.isBlank(filterName)) {
continue;
}
logger.debug("HttpClient5AdapterServiceRestAddFilter] Processing Start [" + filterName + "]");
HttpClientAdapterFilter adapterFilter = HttpClient5AdapterFilterFactory.createFilter(filterName.trim());
if (adapterFilter != null) {
// if (adapterFilter instanceof IBKOutCommonFilter)
// isSetCommonFilterInAdapterProp = true;
message = adapterFilter.doPreFilter(adptGrpName, adptName, prop, message, tempProp);
} else {
throw new Exception("Failed get Filter Class: " + filterName);
}
}
}
// // 2. IBKOutCommonFilter 필터 수행(adapter 필터에서 수행하지 않을 때만)
// if (!isSetCommonFilterInAdapterProp) {
// HttpClientAdapterFilter ibkFilter = HttpClient5AdapterFilterFactory
// .createFilter(IBKOutCommonFilter.class.getName());
// if (ibkFilter != null) {
// message = ibkFilter.doPreFilter(adptGrpName, adptName, prop, message, tempProp);
// }
// } else {
// logger.debug("IBKOutCommonFilter isSetCommonFilterInAdapterProp 'POST_FILTERS'. already in adapter filters");
// }
return message;
}
protected Object doPostFilters(String adptGrpName, String adptName, Properties prop, Object message,
Properties tempProp) throws Exception {
// boolean isSetCommonFilterInAdapterProp = false;
// 1. adapter에 설정된 필터 수행
String postFiltersStr = prop.getProperty(POST_FILTERS);
if (StringUtils.isNotEmpty(postFiltersStr)) {
String[] postFilters = StringUtils.split(postFiltersStr, ",");
for (String filterName : postFilters) {
if (StringUtils.isBlank(filterName)) {
continue;
}
HttpClientAdapterFilter adapterFilter = HttpClient5AdapterFilterFactory.createFilter(filterName.trim());
if (adapterFilter != null) {
// if (adapterFilter instanceof IBKOutCommonFilter)
// isSetCommonFilterInAdapterProp = true;
message = adapterFilter.doPostFilter(adptGrpName, adptName, prop, message, tempProp);
} else {
throw new Exception("Failed get Filter Class: " + filterName);
}
}
}
// // 2. IBKOutCommonFilter 필터 수행
// if (!isSetCommonFilterInAdapterProp) {
// HttpClientAdapterFilter ibkFilter = HttpClient5AdapterFilterFactory
// .createFilter(IBKOutCommonFilter.class.getName());
// if (ibkFilter != null) {
// message = ibkFilter.doPostFilter(adptGrpName, adptName, prop, message, tempProp);
// }
// } else {
// logger.debug("IBKOutCommonFilter isSetCommonFilterInAdapterProp 'PRE_FILTERS'. already in adapter filters");
// }
return message;
}
}
@@ -1,680 +0,0 @@
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;
}
}
@@ -1,888 +0,0 @@
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;
}
}
@@ -1,867 +0,0 @@
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;
}
}
@@ -0,0 +1,57 @@
package com.eactive.eai.custom.adapter.http.client.impl.filter;
import java.util.Properties;
import com.eactive.eai.adapter.http.client.impl.HttpClient5AdapterServiceRest;
import com.eactive.eai.adapter.http.client.impl.filter.OutCryptoFilter;
import com.eactive.eai.adapter.http.dynamic.filter.InCryptoFilter;
import com.eactive.eai.common.util.JacksonUtil;
import com.eactive.eai.util.JsonPathUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
/**
* 토스 암복호화에 맟추어 커스텀
*/
public class DouzoneEncFieldOutCryptoFilter extends EncFieldOutCryptoFilter {
private final OutCryptoFilter filter = new OutCryptoFilter();
protected Properties setProp(Properties tempProp) {
tempProp.setProperty(OutCryptoFilter.PROP_DEC_FROM_PATH, "/encryptedData");
tempProp.setProperty(OutCryptoFilter.PROP_ENC_TO_PATH, "/encryptedData");
return tempProp;
}
@Override
public Object doPreFilter(String adptGrpName, String adptName, Properties prop, Object message, Properties tempProp)
throws Exception {
String headerGroupName = prop.getProperty(HttpClient5AdapterServiceRest.HEADER_GROUP);
JsonNode root = JacksonUtil.readTree(message);
ObjectNode rootObjectNode = (ObjectNode) root;
JsonNode header = rootObjectNode.get(headerGroupName);
rootObjectNode.remove(headerGroupName);
String xEmpNm = header.get("x-emp-nm").asText();
tempProp.setProperty("x-emp-nm", xEmpNm);
tempProp.setProperty(InCryptoFilter.PROP_AAD_HEADER, xEmpNm);
String xChnlNm = header.get("x-chnl-nm").asText();
tempProp.setProperty("x-chnl-nm", xChnlNm);
Object enc = filter.doPreFilter(adptGrpName, adptName, prop, rootObjectNode, setProp(tempProp));
ObjectNode encJson = (ObjectNode)JacksonUtil.readTree(enc);
encJson.set(headerGroupName, header);
return encJson;
}
@Override
public Object doPostFilter(String adptGrpName, String adptName, Properties prop, Object message,
Properties tempProp) throws Exception {
return filter.doPostFilter(adptGrpName, adptName, setProp(prop), message, setProp(tempProp));
}
}
@@ -0,0 +1,49 @@
package com.eactive.eai.custom.adapter.http.client.impl.filter;
import java.util.Properties;
import com.eactive.eai.adapter.http.client.impl.HttpClient5AdapterServiceRest;
import com.eactive.eai.adapter.http.client.impl.filter.HttpClientAdapterFilter;
import com.eactive.eai.adapter.http.client.impl.filter.OutCryptoFilter;
import com.eactive.eai.common.util.JacksonUtil;
import com.eactive.eai.util.JsonPathUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
/**
* 제주은행 Open API 기본 암복호화 필더
*/
public class EncFieldOutCryptoFilter implements HttpClientAdapterFilter {
private final OutCryptoFilter filter = new OutCryptoFilter();
protected Properties setProp(Properties tempProp) {
tempProp.setProperty(OutCryptoFilter.PROP_DEC_FROM_PATH, "/encryptedData");
tempProp.setProperty(OutCryptoFilter.PROP_ENC_TO_PATH, "/encryptedData");
return tempProp;
}
@Override
public Object doPreFilter(String adptGrpName, String adptName, Properties prop, Object message, Properties tempProp)
throws Exception {
String headerGroupName = prop.getProperty(HttpClient5AdapterServiceRest.HEADER_GROUP);
JsonNode root = JacksonUtil.readTree(message);
ObjectNode rootObjectNode = (ObjectNode) root;
JsonNode header = rootObjectNode.get(headerGroupName);
rootObjectNode.remove(headerGroupName);
Object enc = filter.doPreFilter(adptGrpName, adptName, prop, rootObjectNode, setProp(tempProp));
ObjectNode encJson = (ObjectNode)JacksonUtil.readTree(enc);
encJson.set(headerGroupName, header);
return encJson;
}
@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));
}
}
@@ -0,0 +1,18 @@
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;
}
}
@@ -0,0 +1,17 @@
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;
}
}
@@ -0,0 +1,17 @@
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,82 +0,0 @@
package com.eactive.eai.custom.adapter.http.dynamic.filter;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import com.eactive.eai.adapter.http.dynamic.filter.InCryptoFilter;
import com.eactive.eai.util.JsonPathUtil;
/**
* DJB ERP 연동용 암복호화 필터.
*
* DYNAMIC 키 컨텍스트 추출 규칙:
* HTTP 헤더 {@code httpHeaderGroup}의 값은 JSON 객체이며,
* 그 중 {@code x-emp-nm} 필드 값을 {@code groupSeq}로
* runtimeContext에 담아 키 도출 전략에 전달한다.
*
* 어댑터 프로퍼티 (부모 클래스 공통 설정 외 추가 키):
* crypto.context.key runtimeContext에 넣을 키 이름 (기본: groupSeq)
* crypto.context.header 컨텍스트 JSON이 담긴 HTTP 헤더명 (기본: httpHeaderGroup)
* crypto.context.header.field 헤더 JSON에서 추출할 필드명 (기본: x-emp-nm)
*
* 어댑터 프로퍼티 설정 예:
* crypto.module.name = AES_GCM_NoPadding_DYNAMIC_sample
* crypto.scope = FIELD
* crypto.dec.enabled = Y
* crypto.enc.enabled = N
* crypto.dec.from.path = /encryptedData
* crypto.dec.to.path = /
* crypto.context.key = groupSeq
* crypto.context.header = httpHeaderGroup
* crypto.context.header.field = x-emp-nm
*
* 필터 타입 등록 (FQCN):
* com.eactive.eai.custom.adapter.http.dynamic.filter.DJBErpCryptoFilter
*/
public class DJBErpCryptoFilter extends InCryptoFilter {
public static final String PROP_CONTEXT_KEY = "crypto.context.key";
public static final String PROP_CONTEXT_HEADER = "crypto.context.header";
public static final String PROP_CONTEXT_HEADER_FIELD = "crypto.context.header.field";
private static final String DEFAULT_CONTEXT_KEY = "groupSeq";
private static final String DEFAULT_CONTEXT_HEADER = "httpHeaderGroup";
private static final String DEFAULT_CONTEXT_HEADER_FIELD = "x-emp-nm";
/**
* httpHeaderGroup 헤더(JSON)에서 x-emp-nm 값을 추출하여 groupSeq로 매핑한다.
* 헤더가 없거나 필드를 찾을 수 없으면 빈 Map을 반환한다.
*/
@Override
protected Map<String, String> buildRuntimeContext(Properties prop, HttpServletRequest request) {
Map<String, String> ctx = new HashMap<>();
String contextKey = prop.getProperty(PROP_CONTEXT_KEY, DEFAULT_CONTEXT_KEY);
String contextHeaderName = prop.getProperty(PROP_CONTEXT_HEADER, DEFAULT_CONTEXT_HEADER);
String contextHeaderField = prop.getProperty(PROP_CONTEXT_HEADER_FIELD, DEFAULT_CONTEXT_HEADER_FIELD);
String headerJson = request.getHeader(contextHeaderName);
if (StringUtils.isBlank(headerJson)) {
logger.warn("DJBErpCryptoFilter] 컨텍스트 헤더 없음: " + contextHeaderName);
return ctx;
}
String value = JsonPathUtil.getValueAtPath(headerJson, "/" + contextHeaderField);
if (value == null) {
logger.warn("DJBErpCryptoFilter] 헤더 JSON에서 필드 추출 실패: header="
+ contextHeaderName + ", field=" + contextHeaderField);
return ctx;
}
// x-emp-nm 값은 "groupSeq|empNo" 형태 — '|' 앞부분만 groupSeq로 사용
String groupSeq = value.contains("|") ? value.substring(0, value.indexOf('|')) : value;
ctx.put(contextKey, groupSeq);
return ctx;
}
}
@@ -0,0 +1,88 @@
package com.eactive.eai.custom.adapter.http.dynamic.filter;
import java.util.Base64;
import java.util.Properties;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.json.simple.JSONObject;
import org.springframework.http.HttpStatus;
import com.eactive.eai.adapter.http.dynamic.filter.FilterException;
import com.eactive.eai.adapter.http.dynamic.filter.HttpAdapterFilter;
import com.eactive.eai.common.security.AESCryptoModuleExtension;
import com.eactive.eai.common.security.ARIACryptoModuleExtension;
import com.eactive.eai.common.security.CryptoModuleExtension;
import com.eactive.eai.common.util.Logger;
import com.eactive.eai.util.HexaConverter;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
public class DecrytTestFilter implements HttpAdapterFilter {
private static final Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
@Override
public Object doPreFilter(String adptGrpName, String adptName, Object message, Properties prop,
HttpServletRequest request, HttpServletResponse response) throws Exception {
try {
ObjectNode rootNode = null;
String jsonStr = null;
if (message instanceof String) {
jsonStr = (String) message;
} else if (message instanceof JSONObject) {
jsonStr = ((JSONObject) message).toJSONString();
} else {
return message;
}
rootNode = (ObjectNode) OBJECT_MAPPER.readTree(jsonStr);
JsonNode alg = rootNode.get("alg");
JsonNode mode = rootNode.get("mode");
JsonNode padding = rootNode.get("padding");
JsonNode aadNode = rootNode.get("aad");
JsonNode iv = rootNode.get("iv");
byte[] binIv = null;
if(iv != null) {
String strIv = iv.asText();
binIv = HexaConverter.hexToBin(strIv);
}
JsonNode encKey = rootNode.get("encKey");
String strEncKey = encKey.asText();
byte[] binEncKey = HexaConverter.hexToBin(strEncKey);
CryptoModuleExtension ext = "ARIA".equalsIgnoreCase(alg.asText()) ? new ARIACryptoModuleExtension()
: new AESCryptoModuleExtension();
ext.init(alg.asText(), mode.asText(), padding.asText(), binIv, binEncKey, binEncKey);
JsonNode encryptedData = rootNode.get("encryptedData");
byte[] cipherBytes = Base64.getDecoder().decode(encryptedData.asText().trim());
byte[] decryptedBytes = null;
if(rootNode.has("aad"))
decryptedBytes = ext.decrypt(cipherBytes, HexaConverter.hexToBin(aadNode.asText()));
else
decryptedBytes = ext.decrypt(cipherBytes);
rootNode.put("hsmKeyRaw", new String(decryptedBytes));
String jsonString = OBJECT_MAPPER.writeValueAsString(rootNode);
return jsonString;
} catch (Exception e) {
logger.error("복호화 실패", e);
throw new FilterException("복호화 실패", ERROR_PRE_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 {
return resultMessage;
}
}
@@ -0,0 +1,90 @@
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.HttpAdapterFilter;
import com.eactive.eai.adapter.http.dynamic.filter.InCryptoFilter;
import com.eactive.eai.adapter.service.DJBApiAdapterService;
import com.eactive.eai.common.util.JacksonUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
/**
* 더존 연동용 수신 암복호화 필터.
*
* {@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 {
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 chnlNm = inboundHeaders.getProperty("x-chnl-nm");
tempProp.setProperty("x-chnl-nm", chnlNm);
tempProp.setProperty(InCryptoFilter.PROP_AAD_HEADER, "x-emp-nm");
}
@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("복호화 오류", InCryptoFilter.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);
JsonNode root = JacksonUtil.readTree(resultMessage);
ObjectNode rootObjectNode = (ObjectNode) root;
String headerGroupName = prop.getProperty(DJBApiAdapterService.HEADER_GROUP);
JsonNode header = null;
if(headerGroupName != null) {
header = rootObjectNode.get(headerGroupName);
rootObjectNode.remove(headerGroupName);
}
Object enc = filter.doPostFilter(adptGrpName, adptName, rootObjectNode, setProp(prop), request, response);
ObjectNode encJson = (ObjectNode)JacksonUtil.readTree(enc);
if(header != null)
encJson.set(headerGroupName, header);
return encJson;
} catch (Exception e) {
throw new FilterCryptoException("암호화 오류", InCryptoFilter.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;
/**
* CryptoFilter 프로퍼티를 멤버변수로 관리하는 HttpAdapterFilter 구현체.
*
* 멤버변수에 세팅된 값을 Properties로 변환하여 {@link InCryptoFilter}에 위임한다.
* 값이 blank인 멤버변수는 Properties에 포함하지 않으며, CryptoFilter의 기본값이 적용된다.
*/
public class EncFieldCryptoFilter 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);
}
private 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,66 @@
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.filter.FilterCryptoException;
import com.eactive.eai.adapter.http.dynamic.filter.HttpAdapterFilter;
import com.eactive.eai.adapter.http.dynamic.filter.InCryptoFilter;
import com.eactive.eai.adapter.service.DJBApiAdapterService;
import com.eactive.eai.common.util.JacksonUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
/**
* 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 {
try {
JsonNode root = JacksonUtil.readTree(resultMessage);
ObjectNode rootObjectNode = (ObjectNode) root;
String headerGroupName = prop.getProperty(DJBApiAdapterService.HEADER_GROUP);
JsonNode header = null;
if(headerGroupName != null) {
header = rootObjectNode.get(headerGroupName);
rootObjectNode.remove(headerGroupName);
}
Object enc = filter.doPostFilter(adptGrpName, adptName, rootObjectNode, setProp(prop), request, response);
ObjectNode encJson = (ObjectNode)JacksonUtil.readTree(enc);
if(header != null)
encJson.set(headerGroupName, header);
return encJson;
} catch (Exception e) {
throw new FilterCryptoException("암호화 오류", InCryptoFilter.ERROR_ENC_FAIL, HttpStatus.INTERNAL_SERVER_ERROR.value(), e);
}
}
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,147 @@
package com.eactive.eai.custom.adapter.http.dynamic.filter;
import java.security.KeyStore;
import java.util.Base64;
import java.util.Properties;
import javax.crypto.SecretKey;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.json.simple.JSONObject;
import org.springframework.http.HttpStatus;
import com.eactive.eai.adapter.http.dynamic.filter.FilterException;
import com.eactive.eai.adapter.http.dynamic.filter.HttpAdapterFilter;
import com.eactive.eai.common.hsm.HsmManager;
import com.eactive.eai.common.security.AESCryptoModuleExtension;
import com.eactive.eai.common.security.ARIACryptoModuleExtension;
import com.eactive.eai.common.security.CryptoModuleExtension;
import com.eactive.eai.common.util.Logger;
import com.eactive.eai.util.HexaConverter;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
public class EncrytTestFilter implements HttpAdapterFilter {
private static final Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
@Override
public Object doPreFilter(String adptGrpName, String adptName, Object message, Properties prop,
HttpServletRequest request, HttpServletResponse response) throws Exception {
try {
ObjectNode rootNode = null;
String jsonStr = null;
if (message instanceof String) {
jsonStr = (String) message;
} else if (message instanceof JSONObject) {
jsonStr = ((JSONObject) message).toJSONString();
} else {
return message;
}
rootNode = (ObjectNode) OBJECT_MAPPER.readTree(jsonStr);
// 1. HSM에서 키 가져오기
byte[] hsmKeyBytes = null;
JsonNode hsmKeyAlias = rootNode.get("hsmKeyAlias");
if (hsmKeyAlias != null) {
String hsmKeyAliasValue = hsmKeyAlias.textValue();
KeyStore keyStore = HsmManager.getInstance().getKeyStore();
java.security.Key key = keyStore.getKey(hsmKeyAliasValue, null);
if (key instanceof SecretKey) {
SecretKey secretKey = (SecretKey) key;
hsmKeyBytes = secretKey.getEncoded();
if (hsmKeyBytes != null) {
String hsmKeyBase64 = Base64.getEncoder().encodeToString(hsmKeyBytes);
String hsmKeyHex = HexaConverter.binToHex(hsmKeyBytes);
logger.debug("HsmManager] HSM - {} : [{}]", hsmKeyAliasValue, hsmKeyHex);
rootNode.put("hsmKeyBase64", hsmKeyBase64);
rootNode.put("hsmKeyHex", hsmKeyHex);
rootNode.put("hsmKeyRaw", new String(hsmKeyBytes));
} else {
logger.debug("HsmManager] HSM - secretKey null {} : [{}]", hsmKeyAliasValue, secretKey);
}
}
}
// 2. 암호화 파라미터가 있으면 암호화 후 복호화 검증
JsonNode alg = rootNode.get("alg");
JsonNode mode = rootNode.get("mode");
JsonNode padding = rootNode.get("padding");
JsonNode enc = rootNode.get("enc");
String strEnc = "BASE64";
if(enc != null)
strEnc = enc.asText();
JsonNode ivNode = rootNode.get("iv");
JsonNode aadNode = rootNode.get("aad");
JsonNode encKeyNode = rootNode.get("encKey");
JsonNode dataNode = rootNode.get("data");
if (alg != null && mode != null && padding != null && encKeyNode != null
&& dataNode != null) {
byte[] binIv = null;
if(ivNode != null) {
String strIvValue = ivNode.asText();
if(strIvValue.length() == 32||strEnc.equalsIgnoreCase("HEX"))
binIv = HexaConverter.hexToBin(strIvValue);
else
binIv = Base64.getDecoder().decode(strIvValue.trim());
}
String strEncKeyValue = encKeyNode.asText();
byte[] binEncKey = null;
if(strEnc.equalsIgnoreCase("BASE64"))
binEncKey = Base64.getDecoder().decode(strEncKeyValue.trim());
else
binEncKey = HexaConverter.hexToBin(strEncKeyValue);
CryptoModuleExtension ext = "ARIA".equalsIgnoreCase(alg.asText()) ? new ARIACryptoModuleExtension()
: new AESCryptoModuleExtension();
ext.init(alg.asText(), mode.asText(), padding.asText(), binIv, binEncKey, binEncKey);
byte[] plainBytes = dataNode.asText().getBytes();
// 암호화
byte[] encryptedBytes = null;
if(rootNode.has("aad"))
encryptedBytes = ext.encrypt(plainBytes, HexaConverter.hexToBin(aadNode.asText()));
else
encryptedBytes = ext.encrypt(plainBytes);
String encryptedBase64 = Base64.getEncoder().encodeToString(encryptedBytes);
rootNode.put("encryptedData", encryptedBase64);
logger.debug("암호화 결과: [{}]", encryptedBase64);
// 복호화 검증
byte[] decryptedBytes = null;
if(rootNode.has("aad"))
decryptedBytes = ext.decrypt(encryptedBytes, HexaConverter.hexToBin(aadNode.asText()));
else
decryptedBytes = ext.decrypt(encryptedBytes);
rootNode.put("decryptedVerify", new String(decryptedBytes));
logger.debug("복호화 검증: [{}]", new String(decryptedBytes));
}
String jsonString = OBJECT_MAPPER.writeValueAsString(rootNode);
return jsonString;
} catch (Exception e) {
throw new FilterException("HSM key 암호화/복호화 검증 실패", ERROR_PRE_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 {
return resultMessage;
}
}
@@ -0,0 +1,117 @@
package com.eactive.eai.custom.adapter.http.dynamic.filter;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.json.simple.JSONObject;
import org.springframework.http.HttpStatus;
import com.eactive.eai.adapter.http.dynamic.filter.FilterException;
import com.eactive.eai.adapter.http.dynamic.filter.HttpAdapterFilter;
import com.eactive.eai.common.security.keyderiv.DerivedKey;
import com.eactive.eai.common.util.Logger;
import com.eactive.eai.custom.common.security.keyderiv.HsmContextSha256KeyDerivationStrategy;
import com.eactive.eai.custom.common.security.keyderiv.HsmKeyAndIvSliceDerivationStrategy;
import com.eactive.eai.util.HexaConverter;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
public class GenKeyFilter implements HttpAdapterFilter {
private static final Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
@Override
public Object doPreFilter(String adptGrpName, String adptName, Object message, Properties prop,
HttpServletRequest request, HttpServletResponse response) throws Exception {
try {
ObjectNode rootNode = null;
String jsonStr = null;
if (message instanceof String) {
jsonStr = (String) message;
} else if (message instanceof JSONObject) {
jsonStr = ((JSONObject) message).toJSONString();
} else {
return message;
}
rootNode = (ObjectNode) OBJECT_MAPPER.readTree(jsonStr);
JsonNode hsmKey = rootNode.get("hsmKey");
JsonNode hsmIv = rootNode.get("hsmIv");
JsonNode contextKey = rootNode.get("contextKey");
JsonNode type = rootNode.get("type");
JsonNode enc = rootNode.get("enc");
String strEnc = "BASE64";
if(enc != null)
strEnc = enc.asText();
byte[] binIv = null;
if(hsmIv != null) {
String strIvValue = hsmIv.asText();
if(strIvValue.length() == 32||strEnc.equalsIgnoreCase("HEX"))
binIv = HexaConverter.hexToBin(strIvValue);
else
binIv = Base64.getDecoder().decode(strIvValue.trim());
}
String strEncKeyValue = hsmKey.asText();
byte[] binEncKey = null;
if(strEnc.equalsIgnoreCase("BASE64"))
binEncKey = Base64.getDecoder().decode(strEncKeyValue.trim());
else
binEncKey = HexaConverter.hexToBin(strEncKeyValue);
DerivedKey derivedKey = null;
if(type.asText().equals("douzone")) {
Map<String, String> params = new HashMap<>();
Map<String, String> runtimeContext = new HashMap<>();
params.put("contextKey", "contextKey");
runtimeContext.put("contextKey", contextKey.asText());
HsmContextSha256KeyDerivationStrategy douzoneStrategy = new HsmContextSha256KeyDerivationStrategy();
derivedKey = douzoneStrategy.deriveKey(params, runtimeContext, binEncKey);
} else if(type.asText().equals("kakaobank")) {
HsmKeyAndIvSliceDerivationStrategy kakaobank = new HsmKeyAndIvSliceDerivationStrategy();
derivedKey = kakaobank.deriveKey(binEncKey, binIv);
}
JsonNode aad = rootNode.get("aad");
if (aad != null) {
rootNode.put("generatedAdd", HexaConverter.binToHex(aad.asText().getBytes()));
}
String encBase64 = Base64.getEncoder().encodeToString(derivedKey.getDecKey());
logger.debug("generated KEY {} : [{}]", type.asText(), encBase64);
rootNode.put("generatedKeyBase64", encBase64);
rootNode.put("generatedKeyHex", HexaConverter.binToHex(derivedKey.getDecKey()));
rootNode.put("generatedKeyRaw", new String(derivedKey.getDecKey()));
if(derivedKey.getIv() != null) {
String encBase64Iv = Base64.getEncoder().encodeToString(derivedKey.getIv());
logger.debug("generated IV {} : [{}]", type.asText(), encBase64Iv);
rootNode.put("generatedIvBase64", encBase64Iv);
rootNode.put("generatedIvHex", HexaConverter.binToHex(derivedKey.getIv()));
rootNode.put("generatedIvRaw", new String(derivedKey.getIv()));
}
String jsonString = OBJECT_MAPPER.writeValueAsString(rootNode);
return jsonString;
} catch (Exception e) {
throw new FilterException("HSM key 암호화/복호화 검증 실패", ERROR_PRE_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 {
return resultMessage;
}
}
@@ -0,0 +1,80 @@
package com.eactive.eai.custom.adapter.http.dynamic.filter;
import java.security.KeyStore;
import java.util.Base64;
import java.util.Properties;
import javax.crypto.SecretKey;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.json.simple.JSONObject;
import org.springframework.http.HttpStatus;
import com.eactive.eai.adapter.http.dynamic.filter.FilterException;
import com.eactive.eai.adapter.http.dynamic.filter.HttpAdapterFilter;
import com.eactive.eai.common.hsm.HsmManager;
import com.eactive.eai.common.util.Logger;
import com.eactive.eai.util.HexaConverter;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
public class HSMKeyCryptoFilter implements HttpAdapterFilter {
private static final Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
@Override
public Object doPreFilter(String adptGrpName, String adptName, Object message, Properties prop,
HttpServletRequest request, HttpServletResponse response) throws Exception {
try {
ObjectNode rootNode = null;
String jsonStr = null;
if (message instanceof String) {
jsonStr = (String) message;
} else if (message instanceof JSONObject) {
jsonStr = ((JSONObject) message).toJSONString();
} else if (message instanceof ObjectNode) {
rootNode = (ObjectNode) message;
} else {
return message;
}
if (rootNode == null)
rootNode = (ObjectNode) OBJECT_MAPPER.readTree(jsonStr);
JsonNode hsmKeyAlias = rootNode.get("hsmKeyAlias");
if (hsmKeyAlias != null) {
String hsmKeyAliasValue = hsmKeyAlias.textValue();
KeyStore keyStore = HsmManager.getInstance().getKeyStore();
java.security.Key key = keyStore.getKey(hsmKeyAliasValue, null);
if (key instanceof SecretKey) {
SecretKey secretKey = (SecretKey) key;
byte[] encoded = secretKey.getEncoded();
if (encoded != null) {
String encBase64 = Base64.getEncoder().encodeToString(encoded);
logger.debug("HsmManager] HSM - {} : [{}]", hsmKeyAliasValue, encBase64);
rootNode.put("hsmKeyBase64", encBase64);
rootNode.put("hsmKeyRaw", HexaConverter.binToHex(encoded));
} else {
logger.debug("HsmManager] HSM - secretKey null {} : [{}]", hsmKeyAliasValue, secretKey);
}
}
}
return rootNode;
} catch (Exception e) {
throw new FilterException("HSM key 가져오기 실패", ERROR_PRE_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 {
return resultMessage;
}
}
@@ -1,71 +0,0 @@
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);
}
}
@@ -1,110 +0,0 @@
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);
}
}
@@ -0,0 +1,18 @@
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,45 @@
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.FilterException;
import com.eactive.eai.adapter.http.dynamic.filter.HttpAdapterFilter;
import com.eactive.eai.common.util.JacksonUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* 카카오뱅크 암복호화에 맟추어 커스텀 필요
*/
public class KakaopayFilter implements HttpAdapterFilter {
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
@Override
public Object doPreFilter(String adptGrpName, String adptName, Object message, Properties prop,
HttpServletRequest request, HttpServletResponse response) throws Exception {
JsonNode rootNode = JacksonUtil.readTree(message, OBJECT_MAPPER);
// term_agreements[0].is_agreed 값이 true 인지 확인
boolean firstAgreed = JacksonUtil.getBoolean(rootNode, "term_agreements[0].is_agreed", false);
if (!firstAgreed) {
throw new FilterException("EB000003", "금리한도조회실패 - 고객 요청으로 인한 CB 조회 실패", 200);
}
return message;
}
@Override
public Object doPostFilter(String adptGrpName, String adptName, Object resultMessage, Properties prop,
HttpServletRequest request, HttpServletResponse response) throws Exception {
return resultMessage;
}
}
@@ -1,68 +0,0 @@
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);
}
}
@@ -1,215 +0,0 @@
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;
}
}
@@ -1,205 +0,0 @@
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);
}
}
@@ -1,237 +0,0 @@
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();
}
}
@@ -1,205 +0,0 @@
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;
}
}
@@ -0,0 +1,18 @@
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;
}
}
@@ -0,0 +1,18 @@
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;
}
}
@@ -1,120 +0,0 @@
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;
}
}
}
@@ -1,53 +0,0 @@
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;
}
}
}
@@ -1,66 +0,0 @@
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);
}
}
}
@@ -1,29 +0,0 @@
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);
}
}
@@ -1,30 +0,0 @@
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);
}
}
@@ -1,81 +0,0 @@
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();
}
}
@@ -1,71 +0,0 @@
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));
}
}
@@ -0,0 +1,308 @@
package com.eactive.eai.custom.authoutbound.client.impl;
import java.io.IOException;
import java.nio.charset.Charset;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.Date;
import java.util.Properties;
import javax.net.ssl.SSLContext;
import org.apache.commons.lang3.StringUtils;
import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
import org.apache.hc.client5.http.ssl.NoopHostnameVerifier;
import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
import org.apache.hc.client5.http.ssl.TrustAllStrategy;
import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.ssl.SSLContextBuilder;
import org.apache.hc.core5.util.Timeout;
import org.springframework.security.web.util.UrlUtils;
import com.eactive.eai.adapter.AdapterGroupVO;
import com.eactive.eai.adapter.AdapterManager;
import com.eactive.eai.adapter.http.client.HttpClientAdapterServiceKey;
import com.eactive.eai.adapter.http.secure.HttpClient5SSLContextFactory;
import com.eactive.eai.authoutbound.OutboundOAuthCredentialVo;
import com.eactive.eai.authoutbound.client.HttpClientAccessTokenServiceByDB;
import com.eactive.eai.common.util.Logger;
import com.eactive.eai.httpouttlsinfo.HttpOutTlsInfoManager;
import com.eactive.eai.httpouttlsinfo.HttpOutTlsInfoVO;
import com.eactive.eai.util.TestModeChecker;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.openbanking.eai.common.token.AccessTokenVO;
import com.openbanking.eai.common.token.OAuth2AccessTokenVO;
/**
* 1. 기능 : HTTP 웹 컴포넌트를 POST 방식으로 호출할 수 있는 기능을 제공한다. 2. 처리 개요 : * - 2009.11.24
* retry 로직 제거 : 요청 3. 주의사항
*
* @author :
* @version : v 1.0.0
* @see : WLIAdapterFactory.java, WLIAdapter.java, WLIDefaultAdapter.java
* @since :
*
*/
public class DJBErpNsmapiAccessTokenService implements HttpClientAccessTokenServiceByDB {
public static Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT);
private static boolean testMode = TestModeChecker.isTestMode();
/**
* 1. 기능 : 법인검증 토큰 발급에 사용 2. 처리 개요 : - 속성 정보를 설정 하고 토큰 발급 URL 호출 한다. 3. 주의사항
* - JSON 방식
* - ( Header Authorization Basic <base64_Encode(client_id:client_secret)> ) 으로 구성
* @param adapterProp Http Adapter 속성 정보
* @return 반환 된 AccessTokenVO
* @exception Exception 수동 시스템 간 통신 중 발생
**/
public AccessTokenVO execute(String name, Properties adapterProp, OutboundOAuthCredentialVo oAuthCredentialVo)
throws Exception {
AdapterGroupVO gvo = AdapterManager.getInstance().getAdapterGroupVO(name);
String adapterUrl = adapterProp.getProperty("URL");
String encode = gvo.getMessageEncode();
String timeoutTemp = adapterProp.getProperty("HTTP_TIME_OUT");
if (StringUtils.isBlank(timeoutTemp)) {
timeoutTemp = "30000";
}
String connectionTimeoutTemp = adapterProp.getProperty("CONNECTION_TIMEOUT");
if (StringUtils.isBlank(connectionTimeoutTemp)) {
connectionTimeoutTemp = "3000";
}
int timeout = Integer.parseInt(timeoutTemp);
int connectionTimeout = Integer.parseInt(connectionTimeoutTemp);
long currentTime = System.currentTimeMillis();
String uri = oAuthCredentialVo.getUrl();
if (!UrlUtils.isAbsoluteUrl(uri)) {
uri = appendPath(adapterUrl, uri);
}
String contentType = "application/json;";
Charset charset;
if (StringUtils.isNotBlank(encode)) {
charset = Charset.forName(encode);
} else {
charset = Charset.defaultCharset();
encode = charset.toString();
}
boolean useForwardProxy = StringUtils.equalsIgnoreCase(adapterProp.getProperty("FORWARD_PROXY_USE_YN"), "Y");
String forwardProxyUrl = adapterProp.getProperty("FORWARD_PROXY_URL");
logger.debug(
"uri:{}, charset:{}, transactionTimeout:{}, connectionTimeout:{}, useForwardProxy:{}, forwardProxyUrl:{}",
uri, encode, timeout, connectionTimeout, useForwardProxy, forwardProxyUrl);
// mTLS config with default connection parameters
boolean useMtls = StringUtils.equalsIgnoreCase(adapterProp.getProperty(HttpClientAdapterServiceKey.USE_MTLS),
"Y");
AdapterGroupVO adapterGroup = AdapterManager.getInstance().getAdapterGroup(name);
String clientId = adapterGroup.getClientId();
if (logger.isInfo()) {
logger.info("adapterGroupName. : {}, useMtls. : {}, clientId : {}", name, useMtls, clientId);
}
int maxTotalConnections = HttpClientAdapterServiceKey.DEFAULT_MAX_TOTAL_CONNECTIONS;
int maxHostConnections = HttpClientAdapterServiceKey.DEFAULT_MAX_CONNECTION_PER_HOST;
HttpOutTlsInfoVO mtlsInfo = null;
SSLContext sslContext = null;
PoolingHttpClientConnectionManagerBuilder cmBuilder = PoolingHttpClientConnectionManagerBuilder.create();
PoolingHttpClientConnectionManager connectionManager = null;
try {
if (useMtls) {
HttpOutTlsInfoManager tlsManager = HttpOutTlsInfoManager.getInstance();
if (StringUtils.isNotEmpty(clientId)) {
mtlsInfo = tlsManager.getHttpOutTlsInfo(clientId);
}
}
if (useMtls && mtlsInfo != null) {
String storeType = mtlsInfo.getStoreType();
String keyStoreInfo = mtlsInfo.getKeystoreInfo();
String keyStorePassword = mtlsInfo.getKeystorePassword();
String trustStoreInfo = mtlsInfo.getTruststoreInfo();
String trustStorePassword = mtlsInfo.getTruststorePassword();
String[] tlsVersions = null;
String[] cipherSuites = null;
if (StringUtils.isAnyEmpty(keyStoreInfo, keyStorePassword)) {
throw new Exception("mTLS keyStore config error");
}
boolean skipTrust = false;
if (StringUtils.isAnyEmpty(trustStoreInfo, trustStorePassword)) {
if (logger.isWarn())
logger.warn("Skip trustStore validation adapterGroupName : " + name);
skipTrust = true;
}
sslContext = HttpClient5SSLContextFactory.createMTLSContextFromContent(storeType, keyStoreInfo,
keyStorePassword, trustStoreInfo, trustStorePassword, skipTrust, tlsVersions, cipherSuites);
SSLConnectionSocketFactory sslSocketFactory = null;
if (testMode) {
sslSocketFactory = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
} else {
sslSocketFactory = new SSLConnectionSocketFactory(sslContext);
}
cmBuilder.setSSLSocketFactory(sslSocketFactory);
} else {
sslContext = SSLContextBuilder.create().loadTrustMaterial(new TrustAllStrategy()).build();
SSLConnectionSocketFactory sslSocketFactory = null;
if(testMode) {
// Hostname verifier 비활성화 (테스트용)
sslSocketFactory = new SSLConnectionSocketFactory(
sslContext
, NoopHostnameVerifier.INSTANCE
);
}
else {
sslSocketFactory = new SSLConnectionSocketFactory(
sslContext
);
}
cmBuilder.setSSLSocketFactory(sslSocketFactory);
}
} catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException | CertificateException
| IOException | UnrecoverableKeyException e) {
throw new RuntimeException(e);
} catch (Exception e) {
throw new RuntimeException(e);
}
connectionManager = cmBuilder.build();
connectionManager.setMaxTotal(maxTotalConnections);
connectionManager.setDefaultMaxPerRoute(maxHostConnections);
//json body setting
// Map<String,Object> jsonMap = new HashMap<>();
// jsonMap.put("scope",oAuthCredentialVo.getScope());
// jsonMap.put("grant_type",oAuthCredentialVo.getGrantType());
// ObjectMapper objMapper = new ObjectMapper();
// String authJsonBody = objMapper.writeValueAsString(jsonMap);
try (CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(connectionManager).build();) {
HttpPost httpPost = new HttpPost(uri);
// httpPost.setEntity(new StringEntity(authJsonBody));
httpPost.setHeader("Content-Type", contentType+" charset=" + encode);
//HTTP HEADER에 client id, client secret를 base64Encode 한 후 추가
// String cId = oAuthCredentialVo.getClientId();
// String cSecret = oAuthCredentialVo.getClientSecret();
// String authValue = cId + ":" + cSecret;
// String encodedAuth = Base64.getEncoder().encodeToString(authValue.getBytes(StandardCharsets.UTF_8));
String encodedAuth = oAuthCredentialVo.getClientId();
httpPost.setHeader("Authorization","Basic "+ encodedAuth);
RequestConfig.Builder requestConfigBuilder = RequestConfig.custom();
if (useForwardProxy) {
java.net.URL url = new java.net.URL(forwardProxyUrl);
// 프로토콜, 호스트, 포트 추출
String protocol = url.getProtocol();
String host = url.getHost();
int port = url.getPort();
HttpHost proxy = new HttpHost(protocol, host, port);
requestConfigBuilder.setProxy(proxy);
}
RequestConfig requestConfig = requestConfigBuilder
.setConnectTimeout(Timeout.ofMilliseconds(connectionTimeout))
.setResponseTimeout(Timeout.ofMilliseconds(timeout)).build();
httpPost.setConfig(requestConfig);
logger.debug("uri = [" + uri + "]");
logger.debug("oauthClientId = [" + oAuthCredentialVo.getClientId() + "]");
logger.debug("oauthClientSecret = [" + oAuthCredentialVo.getClientSecret() + "]");
logger.debug("oauthScope = [" + oAuthCredentialVo.getScope() + "]");
logger.debug("oauthGrantType = [" + oAuthCredentialVo.getGrantType() + "]");
logger.debug("oauthauthValue = [" + encodedAuth + "]");
logger.debug("contentType = [" + contentType + "]");
logger.debug("encode = [" + encode + "]");
OAuth2AccessTokenVO accessToken = null;
try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
if (response.getCode() != 200) {
throw new Exception("OAuth token receive status fail value= " + response.getCode());
}
logger.info("DJBErpNsmapiAccessTokenService==>" + response.getCode());
HttpEntity entity = response.getEntity();
String responseString = EntityUtils.toString(entity, encode);
logger.debug("Base64Header oauthToken RECV = [" + responseString + "]");
if (StringUtils.isNotBlank(responseString)) {
ObjectMapper objectMapper = new ObjectMapper();
JsonNode responseJSON = objectMapper.readTree(responseString);
if (responseJSON.has("data")) {
JsonNode data = responseJSON.path("data");
if(data.has("token")) {
String token = data.path("token").asText();
accessToken = new OAuth2AccessTokenVO();
accessToken.setAccessToken(token);
accessToken.setExpiration(new Date(currentTime + 30_000L));
logger.debug("oauthToken =" + accessToken.toString());
return accessToken;
} else {
throw new Exception("oauth token return null");
}
} else {
throw new Exception("oauth token return null");
}
// accessToken.setExpiration(new Date(currentTime + oAuthCredentialVo.getIntervalSec() * 1000L));
} else {
throw new Exception("oauth token return null");
}
} catch (Exception e) {
logger.error("[DJBErpNsmapiAccessTokenService] retrieve accessToken exception :" + e.getMessage(), e);
throw e;
}
}
}
/**
* URL에 경로를 추가합니다. 중복되는 슬래시를 방지합니다.
*
* @param baseUrl 기본 URL
* @param pathToAdd 추가할 경로
* @return 완성된 URL 문자열
*/
public static String appendPath(String baseUrl, String pathToAdd) {
if (!baseUrl.endsWith("/") && !pathToAdd.startsWith("/")) {
return baseUrl + "/" + pathToAdd;
} else if (baseUrl.endsWith("/") && pathToAdd.startsWith("/")) {
return baseUrl + pathToAdd.substring(1);
} else {
return baseUrl + pathToAdd;
}
}
}
@@ -0,0 +1,58 @@
package com.eactive.eai.custom.common.security.keyderiv;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Map;
import com.eactive.eai.common.security.keyderiv.DerivedKey;
import com.eactive.eai.common.security.keyderiv.strategy.BaseHsmKeyDerivationStrategy;
/**
* 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 extends BaseHsmKeyDerivationStrategy {
@Override
public DerivedKey deriveKey(Map<String, String> params, Map<String, String> runtimeContext) throws Exception {
byte[] masterKeyBytes = super.getHsmKey(params);
return deriveKey(params, runtimeContext, masterKeyBytes);
}
public DerivedKey deriveKey(Map<String, String> params, Map<String, String> runtimeContext, byte[] masterKeyBytes)
throws NoSuchAlgorithmException {
String contextKey = required(params, PARAM_CONTEXT_KEY);
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;
}
}
@@ -0,0 +1,45 @@
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);
return deriveKey(keyBytes, rawIv);
}
public DerivedKey deriveKey(byte[] keyBytes, byte[] rawIv) {
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,13 +1,25 @@
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.ClientDualBucket;
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)에서
@@ -21,9 +33,85 @@ import com.eactive.eai.inbound.processor.RequestProcessor;
* <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 checkClientInflow(Bucket bucket, String clientId) {
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);
@@ -32,9 +120,9 @@ public class DJBRequestProcessor extends RequestProcessor {
}
@Override
protected InflowTargetVO resolveClientInflowThreshold(Bucket bucket, String clientId) {
protected InflowTargetVO resolveCustomInflowThreshold(Bucket bucket, EAIMessage eaiMsg, Properties prop) {
if (bucket instanceof ClientDualBucket) {
return ((ClientDualBucket) bucket).getClientInflowThreshold(clientId);
return ((ClientDualBucket) bucket).getClientInflowThreshold(eaiMsg.getClientId());
}
return null;
}
@@ -0,0 +1,23 @@
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);
}
@@ -0,0 +1,114 @@
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;
}
}
@@ -9,7 +9,6 @@ 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.ClientDualInflowControlManager;
import com.eactive.eai.common.inflow.dual.DualCustomBucket;
import com.eactive.eai.manage.inflow.GroupBucketStatusDTO;
import com.eactive.eai.manage.inflow.TargetBucketStatusDTO;
@@ -8,7 +8,6 @@ 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.ClientDualInflowControlManager;
import com.eactive.eai.common.inflow.dual.DualInflowControlManager;
import com.eactive.eai.manage.inflow.TargetMetricDTO;
@@ -1,152 +0,0 @@
package com.eactive.eai.custom.kjb.adapter.http.dynamic.filter;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Properties;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.net.util.Base64;
import org.springframework.http.HttpStatus;
import org.apache.commons.lang3.StringUtils;
import com.eactive.eai.adapter.http.dynamic.HttpAdapterServiceKey;
import com.eactive.eai.adapter.http.dynamic.HttpAdapterServiceSupport;
import com.eactive.eai.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;
public class KjbHmacSha256VerifyFilter implements HttpAdapterFilter, HttpAdapterServiceKey {
static Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);
private static final String SERVICE_CODE_UNKNOWN = "00";
public KjbHmacSha256VerifyFilter() {
super();
}
@Override
public Object doPreFilter(String adptGrpName, String adptName, Object message, Properties prop,
HttpServletRequest request, HttpServletResponse response) throws Exception {
String inboundMethod = prop.getProperty(INBOUND_METHOD);
String inboundUri = prop.getProperty(INBOUND_URI);
String inboundBody = prop.getProperty(INBOUND_REQUEST_MESSAGE);
try {
String xObpPartnercode = request.getHeader("x-obp-partnercode");
String xObpSignatureUrl = request.getHeader("x-obp-signature-url");
String xObpSignatureBody = request.getHeader("x-obp-signature-body");
if (StringUtils.isBlank(xObpPartnercode)
|| StringUtils.isBlank(xObpSignatureUrl)
|| StringUtils.isBlank(xObpSignatureBody)) {
throw new JwtAuthException(
String.valueOf(HttpStatus.UNAUTHORIZED.value()),
"Can not find mandatory Http Header");
}
if (StringUtils.isNotBlank(prop.getProperty(INBOUND_QUERY_STRING))) {
inboundUri = inboundUri + "?" + prop.getProperty(INBOUND_QUERY_STRING);
inboundBody = "";
}
String chkUrl = inboundMethod + "&" + inboundUri;
String chkBody = inboundBody;
String clientSecret = assignClientSecret(prop, request);
String macUrl = calculateHMAC(chkUrl, clientSecret);
String macBody = calculateHMAC(chkBody, clientSecret);
if (!StringUtils.equals(xObpSignatureUrl, macUrl)) {
throw new JwtAuthException(
String.valueOf(HttpStatus.UNAUTHORIZED.value()),
"Signature verifying failed : x-obp-signature-url");
}
if (!StringUtils.equals(xObpSignatureBody, macBody)) {
throw new JwtAuthException(
String.valueOf(HttpStatus.UNAUTHORIZED.value()),
"Signature verifying failed : x-obp-signature-body");
}
} catch (JwtAuthException e) {
logger.debug(e.getMessage());
throw e;
} catch (Exception e) {
logger.error(e.getMessage());
throw new JwtAuthException(
String.valueOf(HttpStatus.UNAUTHORIZED.value()),
"Signature verifying failed(unkown)");
}
return message;
}
public static String calculateHMAC(String data, String key)
throws NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException {
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(key.getBytes(), "HmacSHA256"));
return Base64.encodeBase64String(mac.doFinal(data.getBytes("UTF-8")));
}
private String assignClientSecret(Properties prop, HttpServletRequest request) throws Exception {
String clientId = assignClientId(prop, request);
if (StringUtils.isBlank(clientId)) {
throw new JwtAuthException(
String.valueOf(HttpStatus.UNAUTHORIZED.value()),
"Can not find clientId info");
}
ClientVO client = (ClientVO) OAuth2Manager.getInstance().getClientDeatilsStore().get(clientId);
if (client == null) {
throw new JwtAuthException(
String.valueOf(HttpStatus.UNAUTHORIZED.value()),
"Can not find client info");
}
return client.getClientSecret();
}
private String assignClientId(Properties prop, HttpServletRequest request) {
String clientId = null;
try {
// 이전 filter에서 셋팅한 clientId 확보
clientId = prop.getProperty(HttpAdapterServiceSupport.PROPERTIES_NAME_CLIENT_ID);
// 이전 filter에서 셋팅한 값이 없다면 header 정보에서 확보
if (StringUtils.isBlank(clientId)) {
clientId = request.getHeader(HttpAdapterServiceSupport.HEADER_NAME_CLIENT_ID);
}
} catch (Exception e) {
logger.error(e.getMessage());
}
return clientId;
}
@Override
public Object doPostFilter(String adptGrpName, String adptName, Object resultMessage, Properties prop,
HttpServletRequest request, HttpServletResponse response) throws Exception {
String outboundBody = (String) resultMessage;
try {
String clientSecret = assignClientSecret(prop, request);
String macBody = calculateHMAC(outboundBody, clientSecret);
response.addHeader("x-obp-signature-body", macBody);
} catch (Exception e) {
logger.error(e.getMessage());
}
return resultMessage;
}
}
@@ -1,82 +0,0 @@
package com.eactive.eai.custom.message;
import org.apache.commons.lang3.StringUtils;
import com.eactive.eai.message.StandardItem;
import com.eactive.eai.message.StandardMessage;
import com.eactive.eai.message.filter.MessageFilter;
import com.eactive.eai.message.manager.StandardMessageManager;
import com.eactive.eai.message.service.InterfaceMapper;
import com.ext.eai.common.stdmessage.STDMessageKeys;
@SuppressWarnings("serial")
public class FlatMessageFilterKAKAOCARD implements MessageFilter {
@Override
public StandardMessage doFilter(StandardMessage message) {
// 카카오은행 기준
// 전체길이(8) : 전체길이포함 전체
// 헤더 : 150
// 공통부: 850
// DATA헤더 : 10
// DATA : ?
// 종료 : @@ : 2
// 예) DATA가 320byte이면 전체길이는 1332 임
// 요청시 MESSAGE 부가 없음
// 응답시 MESSAGE 부가 있음
boolean isMessageHidden = true;
// 아래로직은 표준전문에서 요청일때 MESSAGE부가 안보이고 응답일때 MESSAGE부 보이게 설정
StandardItem mesgDmanDvcd = message.findItem("Header.MESG_DMAN_DVCD");
if (mesgDmanDvcd != null && StringUtils.equals(mesgDmanDvcd.getValue(), "R")) {
isMessageHidden = false;
}
// message부 보이기 여부
StandardItem msg = message.findItem("MSG");
// 아래로직은 표준전문에서 요청일때 MESSAGE부가 안보이고 응답일때 MESSAGE부 보이게 설정
msg.setHidden(isMessageHidden);
int totalSize = 0; // 전체 길이 ( 메시지여부에 따라 변경됨)
int ioSize = 0; // 데이터부 길이
int messageSize = 0; // 메시지부 길이
try {
ioSize = message.getBizDataBytes().length;
// 데이터부 길이 설정
StandardItem dataHedrLen = message.findItem("DataPart.DataHeader.DATA_HEDR_LEN");
dataHedrLen.setValue(String.valueOf(ioSize));
} catch (Exception e) {
}
try {
messageSize = message.getBytesDataLengthForPath("MSG");
if (messageSize > 10) {
StandardItem msgLen = message.findItem("MSG.MSG_HEDR_LEN");
msgLen.setValue(String.valueOf(messageSize - 10));
}
StandardItem msgCnt = message.findItem("MSG.OUTPUT_MSG_NCNT");
} catch (Exception e) {
}
if (isMessageHidden) {
// 전체길이 = 150 + 850 + 메시지부길이(X) + DATA부(10) + bizData(?) +종료부(2)
totalSize = 150 + 850 + 0 + 10 + ioSize + 2;
} else {
// 전체길이 = 150 + 850 + 메시지부길이(배열존재(출력메시지개수)) + DATA부(10) + bizData +종료부(2)
totalSize = 150 + 850 + messageSize + 10 + ioSize + 2;
}
// 전체 길이 셋팅
try {
message.setLlData(String.valueOf(totalSize));
} catch (Exception e) {
}
return message;
}
}
@@ -1,57 +0,0 @@
package com.eactive.eai.custom.message;
import org.apache.commons.lang3.StringUtils;
import com.eactive.eai.message.StandardItem;
import com.eactive.eai.message.StandardMessage;
import com.eactive.eai.message.filter.MessageFilter;
import com.eactive.eai.message.manager.StandardMessageManager;
import com.eactive.eai.message.service.InterfaceMapper;
import com.ext.eai.common.stdmessage.STDMessageKeys;
@SuppressWarnings("serial")
public class FlatMessageFilterSBI implements MessageFilter {
@Override
public StandardMessage doFilter(StandardMessage message) {
// total 메시지 길이 설정
int totalSize = 0;
try {
totalSize = message.getBytesDataLength();
message.setLlData(String.valueOf(totalSize));
} catch (Exception e) {
}
// whlMesgLen=전체길이 - 채널헤더길이(24) - ZZ(2)
try {
StandardItem whlMesgLenItem = message.findItem("STD_HEADER.whlMesgLen");
if (whlMesgLenItem != null) {
int whlMesgLen = totalSize - 24 - 2;
whlMesgLenItem.setValue(String.valueOf(whlMesgLen));
}
// stndHdrLen=전체길이 - 채널헤더길이(24) - bizData길이 - ZZ(2)
StandardItem stndHdrLenItem = message.findItem("STD_HEADER.stndHdrLen");
if (stndHdrLenItem != null) {
int stndHdrLen = totalSize - 24 - message.getBizDataBytes().length - 2;
stndHdrLenItem.setValue(String.valueOf(stndHdrLen));
}
} catch (Exception e) {
}
// STD_HEADER.rsltCd 설정
StandardMessageManager standardManager = StandardMessageManager.getInstance();
InterfaceMapper mapper = standardManager.getMapper();
if (StringUtils.equals(mapper.getSendRecvDivision(message), "R")) {
StandardItem rsltCdItem = message.findItem("STD_HEADER.rsltCd");
if (StringUtils.equals(mapper.getResponseType(message), STDMessageKeys.RESPONSE_TYPE_CODE_N)) {
rsltCdItem.setValue("00"); // STD_MSG_RSLT_CD_SUCCESS
} else {
rsltCdItem.setValue("99"); // STD_MSG_RSLT_CD_ERROR_UNKNOWN
}
}
return message;
}
}
@@ -1,81 +0,0 @@
package com.eactive.eai.custom.message;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import org.apache.commons.lang3.StringUtils;
import com.eactive.eai.common.server.Keys;
import com.eactive.eai.common.util.StringUtil;
import com.eactive.eai.common.util.UUID;
/**
* 1. 기능 : UUID Generation Class 2. 처리 개요 : * - 3. 주의사항
*
* @author :
* @version : v 1.0.0
* @see : 관련 기능을 참조
* @since : :
*/
public final class GUIDGeneratorKAKAOCARD {
private static final DateTimeFormatter SDF_YYYYMMDDHHMMSSMS = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS");
/*
* 날짜(17) + "-"(1) + 시스템코드(3) + hostname(6) + 일련번호(5) + "-"(1) + 001(3)
*
*/
public static final int GUID_LENGTH = 33; // seq값 제외부분
/**
* 1. 기능 : Private 생성자 2. 처리 개요 : - 3. 주의사항 - Instance 생성하지 못함
**/
private GUIDGeneratorKAKAOCARD() {
}
public synchronized static String getGUID(String systemCode) {
StringBuilder sb = new StringBuilder();
sb.append(SDF_YYYYMMDDHHMMSSMS.format(ZonedDateTime.now()));
sb.append("-");
sb.append(systemCode);
sb.append(UNIQUE_NUM);
char[] formatSeq = { '0', '0', '0', '0', '0' };
char[] seqCharArray = String.valueOf(seq).toCharArray();
int destPos = formatSeq.length - seqCharArray.length;
System.arraycopy(seqCharArray, 0, formatSeq, destPos, seqCharArray.length);
sb.append(formatSeq);
seq++;
if (seq > 99999) {
seq = 1;
}
return sb.toString() + "-" + "001";
}
private static String UNIQUE_NUM;
private static int seq = 1;
private static int hostlength = 6;
static {
String uniqueValue = System.getProperty(Keys.SERVER_KEY);
if (StringUtils.isBlank(uniqueValue)) {
try {
uniqueValue = java.net.InetAddress.getLocalHost().getHostName();
} catch (Exception e) {
uniqueValue = StringUtil.stringFormat("", false, 'X', hostlength);
}
}
if (uniqueValue.length() > hostlength) {
uniqueValue = uniqueValue.substring(uniqueValue.length() - hostlength, uniqueValue.length());
} else if (uniqueValue.length() < hostlength) {
uniqueValue = StringUtil.stringFormat(uniqueValue, false, 'X', hostlength);
}
UNIQUE_NUM = uniqueValue.toUpperCase();
}
public static void main(String[] args) {
String guid = getGUID("FEP");
// System.out.println("[" + guid.length() + "]" + guid);
}
}
@@ -1,40 +0,0 @@
package com.eactive.eai.custom.message;
import com.eactive.eai.common.server.Keys;
import com.eactive.eai.common.util.DatetimeUtil;
import com.eactive.eai.common.util.StringUtil;
public class GUIDGeneratorKBANK {
private static String UNIQUE_NUM;
private static int seq = 1;
static {
String uniqueValue = System.getProperty(Keys.SERVER_KEY);
if(uniqueValue.length() < 8) {
uniqueValue = StringUtil.stringFormat(uniqueValue, false, '0', 8);
}else if(uniqueValue.length() > 8){
uniqueValue = uniqueValue.substring(uniqueValue.length() - 8);
}
UNIQUE_NUM = uniqueValue.toUpperCase();
}
public synchronized static String makeGUID() {
StringBuilder sb = new StringBuilder();
sb.append(DatetimeUtil.getCurrentDateTime());
sb.append(UNIQUE_NUM);
char[] formatSeq = {'0','0','0','0','0','0','0','0'};
char[] seqCharArray = String.valueOf(seq).toCharArray();
int destPos = formatSeq.length - seqCharArray.length;
System.arraycopy(seqCharArray, 0, formatSeq, destPos, seqCharArray.length);
sb.append(formatSeq);
seq ++;
if(seq > 99999999) {
seq = 1;
}
return sb.toString() ;
}
}
@@ -1,98 +0,0 @@
package com.eactive.eai.custom.message;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import com.eactive.eai.common.util.DatetimeUtil;
import org.apache.commons.lang3.StringUtils;
import com.eactive.eai.common.server.Keys;
import com.eactive.eai.common.util.StringUtil;
import com.eactive.eai.common.util.UUID;
/**
* 1. 기능 : UUID Generation Class 2. 처리 개요 : * - 3. 주의사항
*
* @author :
* @version : v 1.0.0
* @see : 관련 기능을 참조
* @since : :
*/
public final class GUIDGeneratorKJB {
/*
* 그룹사코드(3) + 호스트명(8) + 서버구분번호(2) + 일자(8) + 전문생성시간(6) + 일련번호(6) + 시스템경로번호(3, '001')
*/
public static final int GUID_LENGTH = 33; // 시스템경로번호 제외부분
/**
* 1. 기능 : Private 생성자 2. 처리 개요 : - 3. 주의사항 - Instance 생성하지 못함
**/
private GUIDGeneratorKJB() {
}
public synchronized static String getGUID(String groupCmpCd) {
String guid = groupCmpCd + UNIQUE_NUM;
guid = guid + DatetimeUtil.getCurrentDateTime();
if(seq < 10) {
guid = guid + "00000" + seq;
}
else if(seq < 100) {
guid = guid + "0000" + seq;
}
else if(seq < 1000) {
guid = guid + "000" + seq;
}
else if(seq < 10000) {
guid = guid + "00" + seq;
}
else if(seq < 100000) {
guid = guid + "0" + seq;
}
else {
guid = guid + seq;
}
seq ++;
if(seq > 999999) {
seq = 1;
}
return guid + "001";
}
private static String UNIQUE_NUM;
private static int seq = 1;
static {
String hostname = "";
try {
hostname = java.net.InetAddress.getLocalHost().getHostName();
}
catch(Exception e) {
hostname = "________";
}
if(hostname.getBytes().length != 8) {
if(hostname.getBytes().length < 8) {
hostname = StringUtils.leftPad(hostname, 8, '_');
} else if(hostname.getBytes().length > 8) {
hostname = StringUtils.substring(hostname, 0, 8);
}
}
String serverName = System.getProperty(Keys.SERVER_KEY);
String instid = "00";
if(serverName != null && serverName.length() > 2) {
instid = serverName.substring(serverName.length()-2, serverName.length());
}
UNIQUE_NUM = hostname.toUpperCase() + instid;
}
public static void main(String[] args) {
String guid = getGUID("051");
// System.out.println("[" + guid.length() + "]" + guid);
}
}
@@ -1,60 +0,0 @@
package com.eactive.eai.custom.message;
import java.text.SimpleDateFormat;
import com.eactive.eai.common.server.Keys;
import com.eactive.eai.common.util.StringUtil;
public final class GUIDGeneratorSBI {
private static final SimpleDateFormat SDF_YYYYMMDDHHMMSSMI = new SimpleDateFormat("yyyyMMddHHmmssSSS");
private GUIDGeneratorSBI() {
}
/**
* 시스템일시(17)+시스템환경코드(1)+시스템코드(3)+시스템고유번호(9)+일련번호(2)
*
* @param envDvcd
* @return
*/
public synchronized static String getGUID(String envDvcd, String systemType) {
String guid = (SDF_YYYYMMDDHHMMSSMI).format(new java.util.Date()) + envDvcd + systemType + UNIQUE_NUM;
if (seq < 10) {
guid = guid + "0" + seq;
} else {
guid = guid + seq;
}
seq++;
if (seq > 99) {
seq = 1;
}
return guid;
}
private static String UNIQUE_NUM;
private static int seq = 1;
static {
String hostname = "";
try {
hostname = java.net.InetAddress.getLocalHost().getHostName();
} catch (Exception e) {
hostname = "_______";
}
if (hostname.getBytes().length != 7) {
hostname = StringUtil.stringFormat(hostname, false, '_', 7);
}
String serverName = System.getProperty(Keys.SERVER_KEY); // inst.Name
String instid = "11";
if (serverName != null && serverName.length() > 2) {
instid = serverName.substring(serverName.length() - 2, serverName.length());
}
UNIQUE_NUM = hostname.toUpperCase() + instid;
}
}
@@ -2,6 +2,7 @@ package com.eactive.eai.custom.message;
import org.apache.commons.lang3.StringUtils;
import com.eactive.eai.common.property.PropManager;
import com.eactive.eai.common.server.EAIServerManager;
import com.eactive.eai.message.StandardMessage;
import com.eactive.eai.message.service.DefaultInterfaceMapper;
@@ -28,17 +29,24 @@ public class InterfaceMapperDJB extends DefaultInterfaceMapper {
// 처리결과구분코드 DJBank 값
private static final String PROCS_RSLT_SUCCESS = "S";
private static final String PROCS_RSLT_FAIL = "F";
private static String API_ID_PREFIX = StringUtils
.defaultString(PropManager.getInstance().getProperty("API_ID_PREFIX"));
@Override
public String getEaiSvcCode(StandardMessage standardMessage) {
String interfaceId = getInterfaceId(standardMessage);
if(StringUtils.isNotBlank(standardMessage.getEaiSvcCd()))
return standardMessage.getEaiSvcCd();
String eaiInterfaceId = getInterfaceId(standardMessage);
// String txId = getServiceId(standardMessage);
String sendRecvDvcd = getSendRecvDivision(standardMessage); // S | R
String inExDivision = getInExDivision(standardMessage); // 1 | 2
if (interfaceId == null) interfaceId = "";
interfaceId = interfaceId.trim();
return interfaceId + sendRecvDvcd + StringUtils.defaultString(inExDivision);
if(StringUtils.isEmpty(eaiInterfaceId))
throw new RuntimeException("not found if_id");
return API_ID_PREFIX + StringUtils.substring(eaiInterfaceId, 2) + sendRecvDvcd + StringUtils.defaultString(inExDivision);
}
// ----------------------------------------------------------------
@@ -1,202 +0,0 @@
package com.eactive.eai.custom.message;
import org.apache.commons.lang3.StringUtils;
import com.eactive.eai.common.server.EAIServerManager;
import com.eactive.eai.common.util.StringUtil;
import com.eactive.eai.message.StandardMessage;
import com.eactive.eai.message.service.DefaultInterfaceMapper;
import com.ext.eai.common.stdmessage.STDMessageKeys;
@SuppressWarnings("serial")
public class InterfaceMapperKAKAOCARD extends DefaultInterfaceMapper {
private static String SESSION_ID = "INTERFACE_COMMON.MCI_SESN_ID";
private static String INSTANCE_ID = "INTERFACE_COMMON.MCI_INSTNC_ID";
@Override
public String getEaiSvcCode(StandardMessage standardMessage) {
String eaiSvcCode = null;
String interfaceId = getInterfaceId(standardMessage);
String returnType = getSendRecvDivision(standardMessage); // S | R
String inExDivision = getInExDivision(standardMessage); // 1 | 2
if (interfaceId == null)
interfaceId = "";
interfaceId = interfaceId.trim();
// TODO : site에 맞게 조합해야 함.
eaiSvcCode = interfaceId + returnType + StringUtils.defaultString(inExDivision);
return eaiSvcCode;
}
@Override
public void setGuid(StandardMessage standardMessage, String guid) {
if (guid == null)
throw new RuntimeException("guid is null");
if (guid.length() != 36)
throw new RuntimeException("The length of guid is not 36");
setItemValue(standardMessage, getPath(GUID), guid);
}
@Override
public String getGuid(StandardMessage standardMessage) {
String guid = getItemValue(standardMessage, getPath(GUID));
if (guid.length() == 36) {
guid = guid.substring(0, 33);
}
return guid;
}
@Override
public String getGuidSeq(StandardMessage standardMessage) {
String guidseq = null;
String guid = getItemValue(standardMessage, getPath(GUID));
if (guid.length() == 36) {
guidseq = guid.substring(33);
} else {
guidseq = "000";
}
return guidseq;
}
@Override
public void setGuidSeq(StandardMessage standardMessage, String guidSeq) {
setItemValue(standardMessage, getPath(GUID), getGuid(standardMessage) + guidSeq);
}
@Override
public String getOrgGuid(StandardMessage standardMessage) {
String orgGuid = getItemValue(standardMessage, getPath(GUID_ORG));
if (orgGuid.length() == 36) {
orgGuid = orgGuid.substring(0, 33);
}
return orgGuid;
}
@Override
public void setOrgGuid(StandardMessage standardMessage, String orgGuid) {
if (orgGuid.length() == 36) {
orgGuid = orgGuid.substring(0, 33);
}
setItemValue(standardMessage, getPath(GUID_ORG), orgGuid);
}
@Override
public String nextGuidSeq(StandardMessage standardMessage) {
String guidSeq = getGuidSeq(standardMessage);
if (guidSeq == null)
return "";
int seq = 0;
try {
seq = Integer.parseInt(guidSeq) + 1;
} catch (NumberFormatException e) {
seq = 1;
}
guidSeq = StringUtil.stringFormat(Integer.toString(seq), true, '0', 3);
setGuidSeq(standardMessage, guidSeq);
return guidSeq;
}
/**
* 표준전문의 복원은 COMMON.ORTR_RESTR_YN 의 value는 (Y/N) 엔진에서 복원여부 의 value는 (1/0)
*/
@Override
public String getRecoverYn(StandardMessage standardMessage) {
String data = getItemValue(standardMessage, getPath(RECOVER_YN));
if ("Y".equals(data)) {
return "1";
} else {
return "0";
}
}
@Override
public void setRecoverYn(StandardMessage standardMessage, String recoverYn) {
if ("1".equals(recoverYn)) {
setItemValue(standardMessage, getPath(RECOVER_YN), "Y");
} else {
setItemValue(standardMessage, getPath(RECOVER_YN), "N");
}
}
@Override
public String getInstCode(StandardMessage standardMessage) {
EAIServerManager server = EAIServerManager.getInstance();
if (server != null && server.isMCI()) {
return getItemValue(standardMessage, INSTANCE_ID);
} else {
return "";
}
}
@Override
public void setInstCode(StandardMessage standardMessage, String instCode) {
EAIServerManager server = EAIServerManager.getInstance();
if (server != null && server.isMCI()) {
setItemValue(standardMessage, INSTANCE_ID, instCode);
}
}
@Override
public String getSessionId(StandardMessage standardMessage) {
EAIServerManager server = EAIServerManager.getInstance();
if (server != null && server.isMCI()) {
return getItemValue(standardMessage, SESSION_ID);
} else {
return "";
}
}
@Override
public void setSessionId(StandardMessage standardMessage, String sessionId) {
EAIServerManager server = EAIServerManager.getInstance();
if (server != null && server.isMCI()) {
setItemValue(standardMessage, SESSION_ID, sessionId);
}
}
/*
* 엔진 에서는 public static final String RESPONSE_TYPE_CODE_N = "0";//정상(normal)
* public static final String RESPONSE_TYPE_CODE_E = "1";//오류(error)
*/
@Override
public void setResponseType(StandardMessage standardMessage, String responseType) {
if (responseType != null && (STDMessageKeys.RESPONSE_TYPE_CODE_N.equals(responseType)
|| STDMessageKeys.RESPONSE_TYPE_CODE_E.equals(responseType))) {
if (STDMessageKeys.RESPONSE_TYPE_CODE_N.equals(responseType)) {
setItemValue(standardMessage, getPath(RESPONSE_TYPE), "NR");
} else {
setItemValue(standardMessage, getPath(RESPONSE_TYPE), "ER");
}
} else {
setItemValue(standardMessage, getPath(RESPONSE_TYPE), responseType);
}
}
@Override
public String getInterfaceId(StandardMessage standardMessage) {
String interfaceId = getItemValue(standardMessage, getPath(INTERFACE_ID));
if (interfaceId == null)
return "";
interfaceId = interfaceId.trim();
return interfaceId;
}
@Override
public String getReqSysCode(StandardMessage standardMessage) {
// apiPath의 값에서 추출할 예정 : serviceId = api_path
String apiPath = getItemValue(standardMessage, getPath(SERVICE_ID));
if (apiPath != null) {
String[] split = apiPath.split("/");
if (split.length > 2) {
String route = split[1].toUpperCase();
if (route.length() > 3) {
route = route.substring(0, 3);
}
return route;
}
}
return null;
}
}
@@ -1,50 +0,0 @@
package com.eactive.eai.custom.message;
import org.apache.commons.lang3.StringUtils;
import com.eactive.eai.message.StandardMessage;
import com.eactive.eai.message.service.DefaultInterfaceMapper;
import com.ext.eai.common.stdmessage.STDMessageKeys;
public class InterfaceMapperKBANK extends DefaultInterfaceMapper {
private static final long serialVersionUID = 1L;
//전문작성일시
String ITEM_TLGR_WRTN_DT = "header_part.tlgrWrtnDt";
//전문생성시스템
String ITEM_TLGR_CRTN_SYS_NM = "header_part.tlgrCrtnSysNm";
//전문일련번호
String ITEM_TLGR_SRL_NO = "header_part.tlgrSrlNo";
@Override
public String getInExDivision(StandardMessage standardMessage) {
return "2";
}
@Override
public void setGuid(StandardMessage standardMessage, String guid) {
String tlgrWrtnDt = StringUtils.substring(guid, 0, 14);
String tlgrCrtnSysNm = StringUtils.substring(guid, 14, 22);
String tlgrSrlNo = StringUtils.substring(guid, 22, 30);
setItemValue(standardMessage, ITEM_TLGR_WRTN_DT, tlgrWrtnDt);
setItemValue(standardMessage, ITEM_TLGR_CRTN_SYS_NM, tlgrCrtnSysNm);
setItemValue(standardMessage, ITEM_TLGR_SRL_NO, tlgrSrlNo);
}
@Override
public void setGuidSeq(StandardMessage standardMessage, String guidSeq) {
if(guidSeq.length() == 1) {
guidSeq = "0"+guidSeq;
}
super.setGuidSeq(standardMessage, guidSeq);
}
@Override
public String getGuid(StandardMessage standardMessage) {
String tlgrWrtnDt = getItemValue(standardMessage, ITEM_TLGR_WRTN_DT);
String tlgrCrtnSysNm = getItemValue(standardMessage, ITEM_TLGR_CRTN_SYS_NM);
String tlgrSrlNo = getItemValue(standardMessage, ITEM_TLGR_SRL_NO);
return tlgrWrtnDt+tlgrCrtnSysNm+tlgrSrlNo;
}
}
@@ -1,202 +0,0 @@
package com.eactive.eai.custom.message;
import org.apache.commons.lang3.StringUtils;
import com.eactive.eai.common.server.EAIServerManager;
import com.eactive.eai.common.util.StringUtil;
import com.eactive.eai.message.StandardMessage;
import com.eactive.eai.message.service.DefaultInterfaceMapper;
import com.ext.eai.common.stdmessage.STDMessageKeys;
@SuppressWarnings("serial")
public class InterfaceMapperKJB extends DefaultInterfaceMapper {
private static String SESSION_ID = "INTERFACE_COMMON.MCI_SESN_ID";
private static String INSTANCE_ID = "INTERFACE_COMMON.MCI_INSTNC_ID";
@Override
public String getEaiSvcCode(StandardMessage standardMessage) {
String eaiSvcCode = null;
String interfaceId = getInterfaceId(standardMessage);
String returnType = getSendRecvDivision(standardMessage); // S | R
String inExDivision = getInExDivision(standardMessage); // 1 | 2
if (interfaceId == null)
interfaceId = "";
interfaceId = interfaceId.trim();
// TODO : site에 맞게 조합해야 함.
eaiSvcCode = interfaceId + returnType + StringUtils.defaultString(inExDivision);
return eaiSvcCode;
}
@Override
public void setGuid(StandardMessage standardMessage, String guid) {
if (guid == null)
throw new RuntimeException("guid is null");
if (guid.length() != 36)
throw new RuntimeException("The length of guid is not 36");
setItemValue(standardMessage, getPath(GUID), guid);
}
@Override
public String getGuid(StandardMessage standardMessage) {
String guid = getItemValue(standardMessage, getPath(GUID));
if (guid.length() == 36) {
guid = guid.substring(0, 33);
}
return guid;
}
@Override
public String getGuidSeq(StandardMessage standardMessage) {
String guidseq = null;
String guid = getItemValue(standardMessage, getPath(GUID));
if (guid.length() == 36) {
guidseq = guid.substring(33);
} else {
guidseq = "000";
}
return guidseq;
}
@Override
public void setGuidSeq(StandardMessage standardMessage, String guidSeq) {
setItemValue(standardMessage, getPath(GUID), getGuid(standardMessage) + guidSeq);
}
@Override
public String getOrgGuid(StandardMessage standardMessage) {
String orgGuid = getItemValue(standardMessage, getPath(GUID));
if (orgGuid.length() == 36) {
orgGuid = orgGuid.substring(0, 33);
}
return orgGuid;
}
@Override
public void setOrgGuid(StandardMessage standardMessage, String orgGuid) {
if (orgGuid.length() == 36) {
orgGuid = orgGuid.substring(0, 33);
}
setItemValue(standardMessage, getPath(GUID_ORG), orgGuid);
}
@Override
public String nextGuidSeq(StandardMessage standardMessage) {
String guidSeq = getGuidSeq(standardMessage);
if (guidSeq == null)
return "";
int seq = 0;
try {
seq = Integer.parseInt(guidSeq) + 1;
} catch (NumberFormatException e) {
seq = 1;
}
guidSeq = StringUtil.stringFormat(Integer.toString(seq), true, '0', 3);
setGuidSeq(standardMessage, guidSeq);
return guidSeq;
}
/**
* 표준전문의 복원은 COMMON.ORTR_RESTR_YN 의 value는 (Y/N) 엔진에서 복원여부 의 value는 (1/0)
*/
@Override
public String getRecoverYn(StandardMessage standardMessage) {
String data = getItemValue(standardMessage, getPath(RECOVER_YN));
if ("Y".equals(data)) {
return "1";
} else {
return "0";
}
}
@Override
public void setRecoverYn(StandardMessage standardMessage, String recoverYn) {
if ("1".equals(recoverYn)) {
setItemValue(standardMessage, getPath(RECOVER_YN), "Y");
} else {
setItemValue(standardMessage, getPath(RECOVER_YN), "N");
}
}
@Override
public String getInstCode(StandardMessage standardMessage) {
EAIServerManager server = EAIServerManager.getInstance();
if (server != null && server.isMCI()) {
return getItemValue(standardMessage, INSTANCE_ID);
} else {
return "";
}
}
@Override
public void setInstCode(StandardMessage standardMessage, String instCode) {
EAIServerManager server = EAIServerManager.getInstance();
if (server != null && server.isMCI()) {
setItemValue(standardMessage, INSTANCE_ID, instCode);
}
}
@Override
public String getSessionId(StandardMessage standardMessage) {
EAIServerManager server = EAIServerManager.getInstance();
if (server != null && server.isMCI()) {
return getItemValue(standardMessage, SESSION_ID);
} else {
return "";
}
}
@Override
public void setSessionId(StandardMessage standardMessage, String sessionId) {
EAIServerManager server = EAIServerManager.getInstance();
if (server != null && server.isMCI()) {
setItemValue(standardMessage, SESSION_ID, sessionId);
}
}
/*
* 엔진 에서는 public static final String RESPONSE_TYPE_CODE_N = "0";//정상(normal)
* public static final String RESPONSE_TYPE_CODE_E = "1";//오류(error)
*/
@Override
public void setResponseType(StandardMessage standardMessage, String responseType) {
if (responseType != null && (STDMessageKeys.RESPONSE_TYPE_CODE_N.equals(responseType)
|| STDMessageKeys.RESPONSE_TYPE_CODE_E.equals(responseType))) {
if (STDMessageKeys.RESPONSE_TYPE_CODE_N.equals(responseType)) {
setItemValue(standardMessage, getPath(RESPONSE_TYPE), "NR");
} else {
setItemValue(standardMessage, getPath(RESPONSE_TYPE), "ER");
}
} else {
setItemValue(standardMessage, getPath(RESPONSE_TYPE), responseType);
}
}
@Override
public String getInterfaceId(StandardMessage standardMessage) {
String interfaceId = getItemValue(standardMessage, getPath(INTERFACE_ID));
if (interfaceId == null)
return "";
interfaceId = interfaceId.trim();
return interfaceId;
}
@Override
public String getReqSysCode(StandardMessage standardMessage) {
// apiPath의 값에서 추출할 예정 : serviceId = api_path
String apiPath = getItemValue(standardMessage, getPath(SERVICE_ID));
if (apiPath != null) {
String[] split = apiPath.split("/");
if (split.length > 2) {
String route = split[1].toUpperCase();
if (route.length() > 3) {
route = route.substring(0, 3);
}
return route;
}
}
return null;
}
}

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