95 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
curry772 a86d1ba30f chore: elink-online-common 서브모듈 업데이트 (JSON 파싱 횟수 최적화)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 14:53:55 +09:00
curry772 17a343c4ac chore: elink-online-common 서브모듈 업데이트 (decryptField mergeAtRoot 수정)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 13:35:10 +09:00
curry772 dd352fef42 chore: elink-online-common 서브모듈 업데이트 (BaseCryptoFilter 잔여 참조 수정)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 11:28:08 +09:00
curry772 656cd39d30 refactor: CryptoFilter → InCryptoFilter 참조 교체 및 서브모듈 업데이트
- DJBErpCryptoFilter: CryptoFilter → InCryptoFilter 상속 변경
- EncFieldCryptoFilter: BaseCryptoFilter → InCryptoFilter 사용으로 변경
- DJErpApiAdapterService: CryptoFilter.PROP_MODULE_NAME → AbstractCryptoFilter.PROP_MODULE_NAME
- elink-online-common 서브모듈 feature/cryptofilter 브랜치 반영

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 11:26:50 +09:00
curry772 7be74095c5 refactor: HttpAdapterFilterFactoryKjb 제거 - elink-online-common 서브모듈 업데이트
- DJErpApiAdapterController 임포트를 HttpAdapterFilterFactory로 교체
- elink-online-common 서브모듈 포인터 업데이트 (c4a250d)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 16:18:22 +09:00
curry772 28b233aeff refactor: HttpClient5 어댑터 필터 패키지를 elink-online-common으로 이동
- custom/kjb/adapter/http/client/filter/ 패키지 삭제 (elink-online-common으로 이동)
- custom/kjb/adapter/http/client/impl/HttpClient5AdapterServiceRestAddFilter.java 삭제
- elink-online-common 서브모듈 업데이트

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 15:58:32 +09:00
curry772 7a5958481f chore: elink-online-common 서브모듈 업데이트 (테스트 로그 추가)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 13:54:00 +09:00
curry772 ac15633b21 Merge branch 'feature/crypto-module'
CryptoFilter 암복호화 필터, HSM 연동, EncFieldCryptoFilter 실거래 검증 완료
2026-05-13 13:30:21 +09:00
curry772 bf9f865508 feat: elink-online-transformer 서브모듈 업데이트 (ConcatN 함수 추가)
- ConcatN: 파라미터 수 제한 없는 가변 문자열 연결 함수 (numberOfParameters=-1)
- ConcatNFunctionTest, ConcatNParserTest 단위테스트 추가
- .gitignore에 작업내역 문서 패턴 추가

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 20:07:47 +09:00
curry772 9c9ad8c19c Merge branch 'feature/crypto-module'
- HSM 키 파생 전략 elink-online-common으로 이동
- HsmContextSha256KeyDerivationStrategyTest elink-online-common으로 이동
- elink-online-common 서브모듈 업데이트 (CryptoFilter AAD 지원 + 단위테스트)
2026-05-12 20:07:10 +09:00
164 changed files with 4932 additions and 14026 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)) {
filterChain.doFilter(request, response);
return;
}
String encoding = getInboundGroupAdapterEncoding(request);
//원본 바디를 byte[]로 읽기 (개행 포함 그대로) 왜? 뒤에 필터에서 HMAC값 계산할수도 있어서 변조되면 안됨. 개행문자도 그대로 가야함.
byte[] rawBody = readBodyAsBytes(request);
@@ -54,7 +51,8 @@ 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);
@@ -62,6 +60,7 @@ public class ApiRequestBodyFilter extends OncePerRequestFilter {
bodyStr = DJB_ROOTLESS_ARRAY + bodyStr + "}";
finalBody = bodyStr.getBytes(encoding);
}
}
} catch (Exception e) {
// JSON 파싱 실패 시 원본 유지
@@ -75,6 +74,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();
// token 이 포함되면 무조건 제외
@@ -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.HttpAdapterFilterFactoryKjb;
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 = 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() < 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 {
@@ -56,6 +59,11 @@ public class HttpResponseLoggingInterceptor implements HandlerInterceptor {
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.dynamic.filter.CryptoFilter;
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(CryptoFilter.PROP_MODULE_NAME, httpProp.getProperty(CryptoFilter.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
}
@@ -1,24 +1,16 @@
package com.eactive.eai.custom.kjb.adapter.http.client.impl;
package com.eactive.eai.custom.adapter.http.client.impl;
import java.util.Properties;
//import java.util.concurrent.Callable;
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.HttpClient5AdapterServiceRest;
import com.eactive.eai.custom.kjb.adapter.http.client.filter.HttpClient5AdapterFilterFactory;
//import com.eactive.eai.custom.kjb.adapter.http.client.filter.HttpClientAdapterExceptionFilter;
import com.eactive.eai.custom.kjb.adapter.http.client.filter.HttpClientAdapterFilter;
//import com.eactive.eai.custom.kjb.adapter.http.client.filter.IBKOutCommonFilter;
//import com.eactive.eai.custom.kjb.adapter.http.client.filter.IBKOutExceptionFilter;
//import com.fasterxml.jackson.databind.ObjectMapper;
//import com.fasterxml.jackson.databind.node.ObjectNode;
//import com.ibk.eai.common.circuitbreaker.url.UrlCircuitBreakerManager;
//import io.github.resilience4j.circuitbreaker.CircuitBreaker;
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>
@@ -32,7 +24,7 @@ import com.eactive.eai.custom.kjb.adapter.http.client.filter.HttpClientAdapterFi
* @since :
*
*/
public class HttpClient5AdapterServiceRestAddFilter extends HttpClient5AdapterServiceRest
public class HttpClient5AdapterServiceBigTechAddFilter extends HttpClient5AdapterServiceBigTech
implements HttpClientAdapterServiceKey {
// 요청전 수행할 필터(쉼표(,) 구분)
@@ -44,6 +36,8 @@ public class HttpClient5AdapterServiceRestAddFilter extends HttpClient5AdapterSe
// // Adapter에서 Exception이 발생한 경우, 처리할 필터
// static final String EXCEPTION_FILTER = "EXCEPTION_FILTER";
private ObjectMapper mapper = new ObjectMapper();
/**
* 1. 기능 : HttpClient 호출 전후 Filter 적용용 2. 처리 개요 : <br>
* 3. 주의사항 <br>
@@ -61,75 +55,17 @@ public class HttpClient5AdapterServiceRestAddFilter extends HttpClient5AdapterSe
Object adapterResponse = super.execute(prop, data, tempProp);
// //원본 url를 찾아서 있으면, 그것을 사용한다.
// String url = tempProp.getProperty("URL_ORG");
// if(StringUtils.isEmpty(url))
// url = tempProp.getProperty(HttpClientAdapterServiceKey.URL);
//
// UrlCircuitBreakerManager cManager = UrlCircuitBreakerManager.getInstance();
// CircuitBreaker circuitBreaker = cManager.getCircuitBreaker(url);
// try {
// if (circuitBreaker != null) {
// if (logger.isDebugEnabled()) {
// // ObjectMapper 인스턴스 생성
// ObjectMapper objectMapper = new ObjectMapper();
// // 서킷 브레이커의 메트릭 정보와 상태 정보를 직접 Node로 변환
// ObjectNode combinedNode = objectMapper.createObjectNode();
// combinedNode.put("state", circuitBreaker.getState().toString());
// combinedNode.set("metrics", objectMapper.valueToTree(circuitBreaker.getMetrics()));
// logger.debug("CircuitBreaker state [" + circuitBreaker.getName() + "] - " + combinedNode.toString());
// }
// adapterResponse = circuitBreaker.executeCallable(new HttpClientAdapterCallable(prop, data, tempProp, this));
// } else {
// adapterResponse = super.execute(prop, data, tempProp);
// }
// } catch (Exception e) {
// adapterResponse = doExceptionFilters(adptGrpName, adptName, prop, adapterResponse, tempProp, e);
//
// if (adapterResponse instanceof JSONObject)
// adapterResponse = ((JSONObject) adapterResponse).toJSONString();
//
// if (adapterResponse instanceof Node)
// adapterResponse = ((Node) adapterResponse).asXML();
//
// return adapterResponse;
// }
adapterResponse = doPostFilters(adptGrpName, adptName, prop, adapterResponse, tempProp);
if (adapterResponse instanceof JSONObject)
adapterResponse = ((JSONObject) adapterResponse).toJSONString();
if (adapterResponse instanceof Node)
else if (adapterResponse instanceof ObjectNode)
adapterResponse = mapper.writeValueAsString((ObjectNode) adapterResponse);
else if (adapterResponse instanceof Node)
adapterResponse = ((Node) adapterResponse).asXML();
return adapterResponse;
}
// protected Object callSuper(Properties prop, Object data, Properties tempProp) throws Exception {
// return super.execute(prop, data, tempProp);
// }
// private class HttpClientAdapterCallable implements Callable<Object> {
// Properties prop;
// Object data;
// Properties tempProp;
// HttpClient5AdapterServiceRestAddFilter clientService;
//
// public HttpClientAdapterCallable(Properties prop, Object data, Properties tempProp,
// HttpClient5AdapterServiceRestAddFilter clientService) {
// this.prop = prop;
// this.data = data;
// this.tempProp = tempProp;
// this.clientService = clientService;
// }
//
// @Override
// public Object call() throws Exception {
// return clientService.callSuper(prop, data, tempProp);
// }
//
// }
protected Object doPreFilters(String adptGrpName, String adptName, Properties prop, Object message,
Properties tempProp) throws Exception {
@@ -208,15 +144,4 @@ public class HttpClient5AdapterServiceRestAddFilter extends HttpClient5AdapterSe
return message;
}
// protected Object doExceptionFilters(String adptGrpName, String adptName, Properties prop, Object message,
// Properties tempProp, Exception e) throws Exception {
// String exceptionFilterName = prop.getProperty(EXCEPTION_FILTER);
// if (StringUtils.isEmpty(exceptionFilterName))
// exceptionFilterName = IBKOutExceptionFilter.class.getSimpleName();
//
// HttpClientAdapterExceptionFilter exceptionFilter = HttpClient5AdapterFilterFactory.createExceptionFilter(exceptionFilterName.trim());
// return exceptionFilter.doExceptionFilter(adptGrpName, adptName, prop, message, tempProp, e);
//
// }
}
@@ -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.CryptoFilter;
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 CryptoFilter {
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.BaseCryptoFilter;
import com.eactive.eai.adapter.http.dynamic.filter.HttpAdapterFilter;
/**
* CryptoFilter 프로퍼티를 멤버변수로 관리하는 HttpAdapterFilter 구현체.
*
* 멤버변수에 세팅된 값을 Properties로 변환하여 {@link BaseCryptoFilter}에 위임한다.
* 값이 blank인 멤버변수는 Properties에 포함하지 않으며, CryptoFilter의 기본값이 적용된다.
*/
public class EncFieldCryptoFilter implements HttpAdapterFilter {
private final BaseCryptoFilter filter = new BaseCryptoFilter();
@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(BaseCryptoFilter.PROP_DEC_FROM_PATH, "/encryptedData");
p.setProperty(BaseCryptoFilter.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)에서
@@ -22,8 +34,84 @@ import com.eactive.eai.inbound.processor.RequestProcessor;
*/
public class DJBRequestProcessor extends RequestProcessor {
static Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT);
@Override
protected String checkClientInflow(Bucket bucket, String clientId) {
protected void afterFoundEaiMessage(EAIMessage eaiMsg, ProcessVO vo, Properties prop) {
logger.debug("DJBRequestProcessor interface found.");
try {
this.resolveStandardHeaderMapping(eaiMsg, prop);
} catch (Exception e) {
logger.warn("DJBRequestProcessor header set failed.", e);
}
}
private void resolveStandardHeaderMapping(EAIMessage eaiMsg, Properties prop) {
StandardMessage standardMessage = eaiMsg.getStandardMessage();
HashMap<String, String> headerMapping = getPropertyMap("DJB_IN_HTTHEAD_STDHEAD_MAPPING", eaiMsg.getSngSysItfTp());
for (Entry<String, String> entry : headerMapping.entrySet()) {
String stdHeaderPath = entry.getKey();
int dotIdx = stdHeaderPath.indexOf('.');
if(dotIdx > 0) {
String firstPath = stdHeaderPath.substring(0, dotIdx);
StandardItem topLevelItem = standardMessage.findItem(firstPath);
topLevelItem.setHidden(false);
}
StandardItem item = standardMessage.findItem(entry.getKey());
String value = resolveNestedValue(prop, entry.getValue());
if (item != null && value != null)
item.setValue(value);
else
logger.debug("header mapping item not found : {} ", entry);
}
}
private HashMap<String, String> getPropertyMap(String propertyGroupName, String prefix) {
Properties prop = PropManager.getInstance().getProperties(propertyGroupName);
Set<String> keys = prop.stringPropertyNames();
HashMap<String, String> tmpToJsonArrayInterface = new HashMap<>();
for (String key : keys) {
if (StringUtils.startsWith(key, prefix + ".")) {
String propValue = prop.getProperty(key);
String replacedKey = StringUtils.replace(key, prefix + ".", "");
tmpToJsonArrayInterface.put(replacedKey, propValue);
}
}
return tmpToJsonArrayInterface;
}
private String resolveNestedValue(Map<?, ?> map, String keyPath) {
int dotIdx = keyPath.indexOf('.');
if (dotIdx < 0) {
Object val = map.get(keyPath);
if (val == null)
return null;
if (val instanceof String)
return (String) val;
return val.toString();
}
String first = keyPath.substring(0, dotIdx);
String rest = keyPath.substring(dotIdx + 1);
Object val = map.get(first);
if (val instanceof Map) {
return resolveNestedValue((Map<?, ?>) val, rest);
}
// 폴백: 전체 keyPath를 단일 키로 조회
Object direct = map.get(keyPath);
if (direct == null)
return null;
if (direct instanceof String)
return (String) direct;
return direct.toString();
}
@Override
protected String checkCustomInflow(Bucket bucket, EAIMessage eaiMsg, Properties prop) {
String clientId = eaiMsg.getClientId();
if (StringUtils.isBlank(clientId)) return null;
if (bucket instanceof ClientDualBucket) {
return ((ClientDualBucket) bucket).isClientPassDetail(clientId);
@@ -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,123 +0,0 @@
package com.eactive.eai.custom.kjb.adapter.http.client.filter;
import java.util.Arrays;
import java.util.Map;
import java.util.Properties;
import java.util.TreeMap;
import java.util.stream.Stream;
import org.apache.commons.lang3.StringUtils;
import com.eactive.eai.adapter.http.dynamic.HttpAdapterServiceKey;
import com.eactive.eai.adapter.http.dynamic.HttpAdapterServiceSupport;
import com.eactive.eai.common.TransactionContextKeys;
import com.eactive.eai.common.property.PropManager;
import com.eactive.eai.common.util.Logger;
public class AllHeaderFilter implements HttpClientAdapterFilter, HttpAdapterServiceKey {
protected static Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);
public static final String PROPERTIES_GROUP_NAME = "HttpHeaderFilter";
public static final String HEADER_KEY_NAMES = "AllHeaderFilter.blackList";
String[] HttpHeaderRelayBlackList = {
"Content-Length",
"Transfer-Encoding",
"Host",
"Authorization",
"Accept",
"Host",
"Cookie",
"Connection",
"Keep-Alive",
"Proxy-Authenticate",
"Proxy-Authorization",
"TE",
"Trailer",
"Transfer-Encoding",
"Upgrade",
"Content-Type",
"Content-Encoding",
"Content-Language",
"Content-Location",
"Content-MD5",
"Expect",
};
@Override
public Object doPreFilter(String adptGrpName, String adptName, Properties prop, Object message, Properties tempProp)
throws Exception {
logger.debug("doPreFilter Processing Start.");
Properties filterHeaders = (Properties) tempProp.get("FILTERHEADER");
if (filterHeaders == null) {
filterHeaders = new Properties();
tempProp.put("FILTERHEADER", filterHeaders);
}
Map<String, String> inboundHeaderMap = getInboundHeaderProp(tempProp);
String propValue = PropManager.getInstance().getProperty(PROPERTIES_GROUP_NAME, HEADER_KEY_NAMES, "").trim();
String[] userSettingBlackList = propValue.split(",");
if( userSettingBlackList.length > 0 ) {
HttpHeaderRelayBlackList = Stream
.concat(Arrays.stream(HttpHeaderRelayBlackList), Arrays.stream(userSettingBlackList))
.toArray(String[]::new);
}
for(String keyName : inboundHeaderMap.keySet() ) {
if( StringUtils.equalsAnyIgnoreCase( keyName, HttpHeaderRelayBlackList ) ) {
logger.debug("Skip Processing Key ["+keyName+"], value ["+inboundHeaderMap.get(keyName)+"] in HttpHeaderRelayBlackList");
continue;
}
filterHeaders.put(keyName, inboundHeaderMap.get(keyName));
logger.debug("Processing Key ["+keyName+"], value ["+inboundHeaderMap.get(keyName)+"]");
}
logger.debug("doPreFilter Processing End.");
return message;
}
public Map<String, String> getInboundHeaderProp(Properties prop) {
Properties header = new Properties();
Map<String, String> inboundHeaderMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
try {
Object headerObj = prop.get(HttpAdapterServiceKey.INBOUND_HEADER);
if (headerObj instanceof Properties) { //tomcat
header = (Properties) headerObj;
for (String name : header.stringPropertyNames()) {
inboundHeaderMap.put(name, header.getProperty(name));
}
} else if (headerObj instanceof String) { //weblogic
String str = (String) headerObj;
str = str.substring(1, str.length() - 1);
// key=value 쌍 분리
String[] entries = str.split(",");
for (String entry : entries) {
String[] kv = entry.split("=", 2);
if (kv.length == 2) {
inboundHeaderMap.put(kv[0].trim(), kv[1].trim());
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return inboundHeaderMap;
}
@Override
public Object doPostFilter(String adptGrpName, String adptName, Properties prop, Object message,
Properties tempProp) throws Exception {
return message;
}
}
@@ -1,118 +0,0 @@
package com.eactive.eai.custom.kjb.adapter.http.client.filter;
import java.util.Arrays;
import java.util.Map;
import java.util.Properties;
import java.util.TreeMap;
import org.apache.commons.lang3.StringUtils;
import com.eactive.eai.adapter.http.dynamic.HttpAdapterServiceKey;
import com.eactive.eai.common.TransactionContextKeys;
import com.eactive.eai.common.property.PropManager;
import com.eactive.eai.common.util.Logger;
public class AsyncReponseFilter implements HttpClientAdapterFilter, HttpAdapterServiceKey {
protected static Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);
public static final String X_TRACE_ID = "partnerTraceId";
public static final String TRACE_ID = "traceId";
public static final String PROPERTIES_GROUP_NAME = "HttpHeaderFilter";
public static final String HEADER_KEY_NAMES = "AsyncReponseFilter";
@Override
public Object doPreFilter(String adptGrpName, String adptName, Properties prop, Object message, Properties tempProp)
throws Exception {
logger.debug("AsyncReponseFilter] PreFilter Processing Start!!");
String traceId = tempProp.getProperty(TransactionContextKeys.TRANSACTION_UUID, "");
Properties filterHeaders = (Properties) tempProp.get("FILTERHEADER");
if (filterHeaders == null) {
filterHeaders = new Properties();
tempProp.put("FILTERHEADER", filterHeaders);
}
String propValue = PropManager.getInstance().getProperty(PROPERTIES_GROUP_NAME, HEADER_KEY_NAMES, "").trim();
String[] headerKeyNames = propValue.split(",");
headerKeyNames = Arrays.stream(headerKeyNames)
.map(String::trim)
.toArray(String[]::new);
try {
Map<String, String> inboundHeaderMap = getInboundHeaderProp(tempProp);
if (!inboundHeaderMap.isEmpty()) {
if (inboundHeaderMap.containsKey(X_TRACE_ID)) {
filterHeaders.put(X_TRACE_ID, inboundHeaderMap.get(X_TRACE_ID));
logger.debug("AsyncReponseFilter] Processing Key ["+X_TRACE_ID+"], value ["+inboundHeaderMap.get(X_TRACE_ID)+"]");
}
if (inboundHeaderMap.containsKey(TransactionContextKeys.X_LOAN_TOKEN)) {
filterHeaders.put(TransactionContextKeys.X_LOAN_TOKEN, inboundHeaderMap.get(TransactionContextKeys.X_LOAN_TOKEN));
logger.debug("AsyncReponseFilter] Processing Key ["+TransactionContextKeys.X_LOAN_TOKEN+"], value ["+inboundHeaderMap.get(TransactionContextKeys.X_LOAN_TOKEN)+"]");
}
}
filterHeaders.setProperty(TRACE_ID, traceId);
logger.debug("AsyncReponseFilter] Processing Key ["+TRACE_ID+"], value ["+traceId+"]");
for (String keyName : inboundHeaderMap.keySet()) {
if (StringUtils.equalsAnyIgnoreCase(keyName, headerKeyNames)) {
String value = inboundHeaderMap.get(keyName);
filterHeaders.put(keyName, value);
logger.debug("AsyncReponseFilter] Processing Key [" + keyName + "], value [" + value + "]");
}
}
} catch (Exception e) {
logger.error(e.getMessage());
}
return message;
}
public Map<String, String> getInboundHeaderProp(Properties prop) {
Properties header = new Properties();
Map<String, String> inboundHeaderMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
try {
Object headerObj = prop.get(HttpAdapterServiceKey.INBOUND_HEADER);
if (headerObj instanceof Properties) { //tomcat
header = (Properties) headerObj;
for (String name : header.stringPropertyNames()) {
inboundHeaderMap.put(name, header.getProperty(name));
}
} else if (headerObj instanceof String) { //weblogic
String str = (String) headerObj;
str = str.substring(1, str.length() - 1);
// key=value 쌍 분리
String[] entries = str.split(",");
for (String entry : entries) {
String[] kv = entry.split("=", 2);
if (kv.length == 2) {
inboundHeaderMap.put(kv[0].trim(), kv[1].trim());
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return inboundHeaderMap;
}
@Override
public Object doPostFilter(String adptGrpName, String adptName, Properties prop, Object message,
Properties tempProp) throws Exception {
return message;
}
}
@@ -1,115 +0,0 @@
package com.eactive.eai.custom.kjb.adapter.http.client.filter;
import java.util.concurrent.ConcurrentHashMap;
import com.eactive.eai.common.util.Logger;
public class HttpClient5AdapterFilterFactory {
static Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);
static String basePackage = "com.eactive.eai.custom.kjb.adapter.http.client.filter";
private static ConcurrentHashMap<String, HttpClientAdapterFilter> h = new ConcurrentHashMap<>();
private static ConcurrentHashMap<String, HttpClientAdapterExceptionFilter> eh = new ConcurrentHashMap<>();
private HttpClient5AdapterFilterFactory() {
throw new IllegalStateException("Utility class");
}
public static HttpClientAdapterFilter createFilter(String type) {
if (h.containsKey(type)) {
return h.get(type);
}
HttpClientAdapterFilter filter = null;
switch (HttpClient5AdapterFilterType.getValue(type)) {
case SIMPLEFRAMEWORK:
filter = new SimpleFrameworkFilter();
break;
case SIMPLEFRAMEWORKBODY:
filter = new SimpleframeworkBodyFilter();
break;
case JBOBP:
filter = new JBOBPFilter();
break;
case KFTCFACE:
filter = new KFTCFaceFilter();
break;
case KFTCP2P:
filter = new KFTCP2PFilter();
break;
case KAKAOBANK:
filter = new KAKAOBankFilter();
break;
case NAVERFIN:
filter = new NAVERFinFilter();
break;
case NICECREDIT:
filter = new NICECreditFilter();
break;
case TRACEBODY:
filter = new TraceBodyFilter();
break;
case TOSSBANK:
filter = new TOSSBankFilter();
break;
case ASYNCRESPONSE:
filter = new AsyncReponseFilter();
break;
case ALLHEADER:
filter = new AllHeaderFilter();
break;
default:
filter = classForName(type);
if (filter == null) {
filter = classForName(basePackage + "." + type);
}
break;
}
if (filter != null) {
h.put(type, filter);
return filter;
} else {
return null;
}
}
private static HttpClientAdapterFilter classForName(String type) {
try {
Class<?> cl = Class.forName(type);
return (HttpClientAdapterFilter) cl.newInstance();
} catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {
logger.error("Cannot create a HttpClientAdapterFilter. - {}", type);
return null;
}
}
public static HttpClientAdapterExceptionFilter createExceptionFilter(String type) {
if (eh.containsKey(type)) {
return eh.get(type);
}
HttpClientAdapterExceptionFilter filter = classForExceptionFilterName(type);
if (filter == null) {
filter = classForExceptionFilterName(basePackage + "." + type);
}
if (filter != null) {
eh.put(type, filter);
return filter;
} else {
return null;
}
}
private static HttpClientAdapterExceptionFilter classForExceptionFilterName(String type) {
try {
Class<?> cl = Class.forName(type);
return (HttpClientAdapterExceptionFilter) cl.newInstance();
} catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {
logger.error("Cannot create a HttpClientAdapterExceptionFilter. - {}", type);
return null;
}
}
}
@@ -1,27 +0,0 @@
package com.eactive.eai.custom.kjb.adapter.http.client.filter;
public enum HttpClient5AdapterFilterType {
SIMPLEFRAMEWORK,
SIMPLEFRAMEWORKBODY,
JBOBP,
KFTCFACE,
KFTCP2P,
KAKAOBANK,
NAVERFIN,
NICECREDIT,
TOSSBANK,
TRACEBODY,
ASYNCRESPONSE,
ALLHEADER,
UNKNOWN;
public static HttpClient5AdapterFilterType getValue(String type) {
try {
return valueOf(type.toUpperCase());
} catch (NullPointerException e) {
return UNKNOWN;
} catch (Exception e) {
return UNKNOWN;
}
}
}
@@ -1,62 +0,0 @@
package com.eactive.eai.custom.kjb.adapter.http.client.filter;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import com.eactive.eai.common.util.Logger;
public abstract class HttpClientAdapterBaseFilter implements HttpClientAdapterFilter {
protected static Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);
protected String filterName = "";
protected List<HttpClientAdapterFilter> filters;
protected HttpClientAdapterBaseFilter(String filterName) {
this.filterName = filterName;
filters = new ArrayList<>();
logger.info("{}] filter created.", filterName);
}
protected void addFilter(HttpClientAdapterFilter filter) {
filters.add(filter);
logger.info("{}] sub filter({}) added. filters= {}", filterName, filter, filters);
}
@Override
public Object doPreFilter(String adptGrpName, String adptName, Properties prop, Object message, Properties tempProp)
throws Exception {
try {
Object filteredMessage = message;
for(HttpClientAdapterFilter subFilter : filters) {
logger.debug("{}] sub filter({}) doPreFilter start.", filterName, subFilter);
filteredMessage = subFilter.doPreFilter(adptGrpName, adptName, prop, filteredMessage, tempProp);
logger.debug("{}] sub filter({}) doPreFilter end.", filterName, subFilter);
}
return filteredMessage;
} catch (Exception e) {
logger.error(this.filterName + "] doPreFilter error.", e);
throw e;
}
}
@Override
public Object doPostFilter(String adptGrpName, String adptName, Properties prop, Object message,
Properties tempProp) throws Exception {
try {
Object filteredMessage = message;
for(HttpClientAdapterFilter subFilter : filters) {
logger.debug("{}] sub filter({}) doPostFilter start.", filterName, subFilter);
filteredMessage = subFilter.doPostFilter(adptGrpName, adptName, prop, filteredMessage, tempProp);
logger.debug("{}] sub filter({}) doPostFilter end.", filterName, subFilter);
}
return filteredMessage;
} catch (Exception e) {
logger.error(this.filterName + "] doPostFilter error.", e);
throw e;
}
}
}
@@ -1,10 +0,0 @@
package com.eactive.eai.custom.kjb.adapter.http.client.filter;
import java.util.Properties;
public interface HttpClientAdapterExceptionFilter {
public Object doExceptionFilter(String adptGrpName, String adptName, Properties prop, Object message,
Properties tempProp, Exception e) throws Exception;
}
@@ -1,12 +0,0 @@
package com.eactive.eai.custom.kjb.adapter.http.client.filter;
import java.util.Properties;
public interface HttpClientAdapterFilter {
public Object doPreFilter(String adptGrpName, String adptName, Properties prop, Object message, Properties tempProp)
throws Exception;
public Object doPostFilter(String adptGrpName, String adptName, Properties prop, Object message,
Properties tempProp) throws Exception;
}
@@ -1,35 +0,0 @@
package com.eactive.eai.custom.kjb.adapter.http.client.filter;
import java.util.Properties;
import lombok.Getter;
public class HttpClientAdapterFilterException extends RuntimeException {
private static final long serialVersionUID = -1208983360831093751L;
@Getter
private final String code;
@Getter
private final Properties prop;
@Getter
private final Properties tempProp;
public HttpClientAdapterFilterException(String code, String msg, Properties prop, Properties tmepProp) {
super(msg);
this.code = code;
this.prop = prop;
this.tempProp = tmepProp;
}
public HttpClientAdapterFilterException(String code, String msg, Properties prop, Properties tempProp, Throwable cause) {
super(msg, cause);
this.code = code;
this.prop = prop;
this.tempProp = tempProp;
}
@Override
public String toString() {
return "HttpClientAdapterFilterException [code=" + code + ", prop=" + prop + ", tempProp=" + tempProp + ", parent=" + super.toString() + "]";
}
}
@@ -1,106 +0,0 @@
package com.eactive.eai.custom.kjb.adapter.http.client.filter;
import java.util.Map;
import java.util.Properties;
import java.util.TreeMap;
import org.apache.commons.lang3.StringUtils;
import com.eactive.eai.adapter.http.dynamic.HttpAdapterServiceKey;
import com.eactive.eai.common.TransactionContextKeys;
import com.eactive.eai.common.property.PropGroupVO;
import com.eactive.eai.common.property.PropManager;
import com.eactive.eai.common.util.Logger;
public class JBOBPFilter implements HttpClientAdapterFilter, HttpAdapterServiceKey {
protected static Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);
public static final String KJB_HEADER = "KJB_Header";
public static final String X_OBP_PARTNERCODE = "x-obp-partnercode";
public static final String X_OBP_TXID = "x-obp-txid";
public static final String X_OBP_TRUST_SYSTEM = "x-obp-trust-system";
String systemTrustKey = "APIMGW-0001-QVBJTUdXLTAwMDE=";
@Override
public Object doPreFilter(String adptGrpName, String adptName, Properties prop, Object message, Properties tempProp)
throws Exception {
logger.debug("JBOBPFilter] PreFilter Processing Start!!");
PropGroupVO propGroupVo = PropManager.getInstance().getPropGroupVO(KJB_HEADER);
if (propGroupVo != null) systemTrustKey = propGroupVo.getProperty(X_OBP_TRUST_SYSTEM, systemTrustKey);
Properties filterHeaders = (Properties) tempProp.get("FILTERHEADER");
if (filterHeaders == null) {
filterHeaders = new Properties();
tempProp.put("FILTERHEADER", filterHeaders);
}
try {
String obpPartnerCode = "000000-00";
Map<String, String> inboundHeaderMap = getInboundHeaderProp(tempProp);
if (!inboundHeaderMap.isEmpty()) {
if (inboundHeaderMap.containsKey(X_OBP_PARTNERCODE)) {
filterHeaders.put(X_OBP_PARTNERCODE, inboundHeaderMap.get(X_OBP_PARTNERCODE));
logger.debug("JBOBPFilter] Processing Key ["+X_OBP_PARTNERCODE+"], value ["+inboundHeaderMap.get(X_OBP_PARTNERCODE)+"]");
} else {
filterHeaders.put(X_OBP_PARTNERCODE, obpPartnerCode);
logger.debug("JBOBPFilter] Processing Key ["+X_OBP_PARTNERCODE+"], value ["+obpPartnerCode+"]");
}
} else {
filterHeaders.put(X_OBP_PARTNERCODE, obpPartnerCode);
logger.debug("JBOBPFilter] Processing Key ["+X_OBP_PARTNERCODE+"], value ["+obpPartnerCode+"]");
}
String uuid = tempProp.getProperty(TransactionContextKeys.TRANSACTION_UUID, "");
if (StringUtils.isNotBlank(uuid)) {
filterHeaders.setProperty(X_OBP_TXID, uuid); // JB OBP Framework용
logger.debug("JBOBPFilter] Processing Key ["+X_OBP_TXID+"], value ["+uuid+"]");
}
if (StringUtils.isNotBlank(systemTrustKey)) {
filterHeaders.setProperty(X_OBP_TRUST_SYSTEM, systemTrustKey); // JB OBP Framework용
logger.debug("JBOBPFilter] Processing Key ["+X_OBP_TRUST_SYSTEM+"], value ["+systemTrustKey+"]");
}
} catch (Exception e) {
logger.error(e.getMessage());
}
return message;
}
public Map<String, String> getInboundHeaderProp(Properties prop) {
Properties header = new Properties();
Map<String, String> inboundHeaderMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
try {
Object headerObj = prop.get(HttpAdapterServiceKey.INBOUND_HEADER);
if (headerObj instanceof Properties) { //tomcat
header = (Properties) headerObj;
for (String name : header.stringPropertyNames()) {
inboundHeaderMap.put(name, header.getProperty(name));
}
} else if (headerObj instanceof String) { //weblogic
String str = (String) headerObj;
str = str.substring(1, str.length() - 1);
// key=value 쌍 분리
String[] entries = str.split(",");
for (String entry : entries) {
String[] kv = entry.split("=", 2);
if (kv.length == 2) {
inboundHeaderMap.put(kv[0].trim(), kv[1].trim());
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return inboundHeaderMap;
}
@Override
public Object doPostFilter(String adptGrpName, String adptName, Properties prop, Object message,
Properties tempProp) throws Exception {
return message;
}
}
@@ -1,135 +0,0 @@
package com.eactive.eai.custom.kjb.adapter.http.client.filter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.Properties;
import java.util.TreeMap;
import com.eactive.eai.adapter.http.dynamic.HttpAdapterServiceKey;
import com.eactive.eai.common.property.PropGroupVO;
import com.eactive.eai.common.property.PropManager;
import com.eactive.eai.common.util.Logger;
public class KAKAOBankFilter implements HttpClientAdapterFilter, HttpAdapterServiceKey {
protected static Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);
public static final String KJB_HEADER = "KJB_Header";
public static final String PROP_PREFIX = "kakaobank";
public static final String X_KKB_PARTNER_CODE = "X-KKB-PARTNER-CODE";
public static final String X_KKB_API_NAME = "X-KKB-API-NAME";
public static final String X_KKB_API_TX_ID = "X-KKB-API-TX-ID";
public static final String X_KKB_TX_TIME = "X-KKB-TX-TIME";
public static final String X_KKB_CMPR_MGMT_NO = "X-KKB-CMPR-MGMT-NO";
@Override
public Object doPreFilter(String adptGrpName, String adptName, Properties prop, Object message, Properties tempProp)
throws Exception {
logger.debug("KAKAOBankFilter] PreFilter Processing Start!!");
Object returnMessage = message;
String partnerCode = "KJB";
String apiName = "status_change";
String apiTxId = "0";
String txTime = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
String cmprMgmtNo = "0";
PropGroupVO propGroupVo = PropManager.getInstance().getPropGroupVO(KJB_HEADER);
if (propGroupVo != null) {
partnerCode = propGroupVo.getProperty(PROP_PREFIX + "." + X_KKB_PARTNER_CODE);
apiName = propGroupVo.getProperty(PROP_PREFIX + "." + X_KKB_API_NAME);
apiTxId = propGroupVo.getProperty(PROP_PREFIX + "." + X_KKB_API_TX_ID);
cmprMgmtNo = propGroupVo.getProperty(PROP_PREFIX + "." + X_KKB_CMPR_MGMT_NO);
}
Properties filterHeaders = (Properties) tempProp.get("FILTERHEADER");
if (filterHeaders == null) {
filterHeaders = new Properties();
tempProp.put("FILTERHEADER", filterHeaders);
}
try {
Map<String, String> inboundHeaderMap = getInboundHeaderProp(tempProp);
if (!inboundHeaderMap.isEmpty()) {
if (inboundHeaderMap.containsKey(X_KKB_PARTNER_CODE)) {
filterHeaders.put(X_KKB_PARTNER_CODE, inboundHeaderMap.get(X_KKB_PARTNER_CODE));
} else {
filterHeaders.put(X_KKB_PARTNER_CODE, partnerCode);
}
if (inboundHeaderMap.containsKey(X_KKB_API_NAME)) {
filterHeaders.put(X_KKB_API_NAME, inboundHeaderMap.get(X_KKB_API_NAME));
} else {
filterHeaders.put(X_KKB_API_NAME, apiName);
}
if (inboundHeaderMap.containsKey(X_KKB_API_TX_ID)) {
filterHeaders.put(X_KKB_API_TX_ID, inboundHeaderMap.get(X_KKB_API_TX_ID));
} else {
filterHeaders.put(X_KKB_API_TX_ID, apiTxId);
}
if (inboundHeaderMap.containsKey(X_KKB_TX_TIME)) {
filterHeaders.put(X_KKB_TX_TIME, inboundHeaderMap.get(X_KKB_TX_TIME));
} else {
filterHeaders.put(X_KKB_TX_TIME, txTime);
}
if (inboundHeaderMap.containsKey(X_KKB_CMPR_MGMT_NO)) {
filterHeaders.put(X_KKB_CMPR_MGMT_NO, inboundHeaderMap.get(X_KKB_CMPR_MGMT_NO));
} else {
filterHeaders.put(X_KKB_CMPR_MGMT_NO, cmprMgmtNo);
}
} else {
filterHeaders.put(X_KKB_PARTNER_CODE, partnerCode);
filterHeaders.put(X_KKB_API_NAME, apiName);
filterHeaders.put(X_KKB_API_TX_ID, apiTxId);
filterHeaders.put(X_KKB_TX_TIME, txTime);
filterHeaders.put(X_KKB_CMPR_MGMT_NO, cmprMgmtNo);
}
logger.debug("KAKAOBankFilter] Processing Key ["+X_KKB_PARTNER_CODE+"], value ["+filterHeaders.getProperty(X_KKB_PARTNER_CODE)+"]");
logger.debug("KAKAOBankFilter] Processing Key ["+X_KKB_API_NAME+"], value ["+filterHeaders.getProperty(X_KKB_API_NAME)+"]");
logger.debug("KAKAOBankFilter] Processing Key ["+X_KKB_API_TX_ID+"], value ["+filterHeaders.getProperty(X_KKB_API_TX_ID)+"]");
logger.debug("KAKAOBankFilter] Processing Key ["+X_KKB_TX_TIME+"], value ["+filterHeaders.getProperty(X_KKB_TX_TIME)+"]");
logger.debug("KAKAOBankFilter] Processing Key ["+X_KKB_CMPR_MGMT_NO+"], value ["+filterHeaders.getProperty(X_KKB_CMPR_MGMT_NO)+"]");
} catch (Exception e) {
logger.error(e.getMessage());
}
return returnMessage;
}
public Map<String, String> getInboundHeaderProp(Properties prop) {
Properties header = new Properties();
Map<String, String> inboundHeaderMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
try {
Object headerObj = prop.get(HttpAdapterServiceKey.INBOUND_HEADER);
if (headerObj instanceof Properties) { //tomcat
header = (Properties) headerObj;
for (String name : header.stringPropertyNames()) {
inboundHeaderMap.put(name, header.getProperty(name));
}
} else if (headerObj instanceof String) { //weblogic
String str = (String) headerObj;
str = str.substring(1, str.length() - 1);
// key=value 쌍 분리
String[] entries = str.split(",");
for (String entry : entries) {
String[] kv = entry.split("=", 2);
if (kv.length == 2) {
inboundHeaderMap.put(kv[0].trim(), kv[1].trim());
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return inboundHeaderMap;
}
@Override
public Object doPostFilter(String adptGrpName, String adptName, Properties prop, Object message,
Properties tempProp) throws Exception {
return message;
}
}
@@ -1,154 +0,0 @@
package com.eactive.eai.custom.kjb.adapter.http.client.filter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.Properties;
import java.util.TreeMap;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
import com.eactive.eai.adapter.AdapterGroupVO;
import com.eactive.eai.adapter.AdapterManager;
import com.eactive.eai.adapter.http.dynamic.HttpAdapterServiceKey;
import com.eactive.eai.common.property.PropGroupVO;
import com.eactive.eai.common.property.PropManager;
import com.eactive.eai.common.server.EAIServerManager;
import com.eactive.eai.common.util.Logger;
public class KFTCFaceFilter implements HttpClientAdapterFilter, HttpAdapterServiceKey {
protected static Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);
public static final String KJB_HEADER = "KJB_Header";
public static final String PROP_PREFIX = "kftcface";
public static final String H_CLIENT_ID = "Client-Id";
public static final String B_ORG_CODE = "org_code";
public static final String B_TRANSACTION_ID = "transaction_id";
public static final String B_REQUEST_DATETIME = "request_datetime";
public static String instId = null;
static {
EAIServerManager eaiServerManager = EAIServerManager.getInstance();
instId = eaiServerManager.getInstId();
}
@SuppressWarnings({ "unchecked" })
@Override
public Object doPreFilter(String adptGrpName, String adptName, Properties prop, Object message, Properties tempProp)
throws Exception {
logger.debug("KFTCFaceFilter] PreFilter Processing Start!!");
JSONObject returnMessage = null;
String clientId = "";
String orgCode = "034"; // 광주은행 행코드
PropGroupVO propGroupVo = PropManager.getInstance().getPropGroupVO(KJB_HEADER);
if (propGroupVo != null) {
if (StringUtils.isEmpty(clientId))
clientId = propGroupVo.getProperty(PROP_PREFIX + "." + H_CLIENT_ID);
orgCode = propGroupVo.getProperty(PROP_PREFIX + "." + B_ORG_CODE);
}
String request_datetime = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
String reqDate = new SimpleDateFormat("yyMMdd").format(new Date());
String reqTime = new SimpleDateFormat("HHmmss").format(new Date());
// 기관코드(3) + 요청일자(6) -- 금결원 표준화 항목, 고정 9자리.
// 요청시간(6) + 인스턴스ID(2) + RandomString(3) -- 기관별 생성 거래고유번호(11자리)
// 기관코드(3) + 요청일자(6) + 요청시간(6) + 인스턴스ID(2) + RandomString(3) -- 20자리.
String transaction_id = orgCode + reqDate + reqTime + instId + RandomStringUtils.random(3, true, true).toUpperCase();
Properties filterHeaders = (Properties) tempProp.get("FILTERHEADER");
if (filterHeaders == null) {
filterHeaders = new Properties();
tempProp.put("FILTERHEADER", filterHeaders);
}
try {
Map<String, String> inboundHeaderMap = getInboundHeaderProp(tempProp);
if (!inboundHeaderMap.isEmpty()) {
if (inboundHeaderMap.containsKey(H_CLIENT_ID)) {
filterHeaders.put(H_CLIENT_ID, inboundHeaderMap.get(H_CLIENT_ID));
logger.debug("KFTCFaceFilter] Processing Key ["+H_CLIENT_ID+"], value ["+inboundHeaderMap.get(H_CLIENT_ID)+"]");
} else {
filterHeaders.put(H_CLIENT_ID, clientId);
logger.debug("KFTCFaceFilter] Processing Key ["+H_CLIENT_ID+"], value ["+clientId+"]");
}
} else {
filterHeaders.setProperty(H_CLIENT_ID, clientId);
logger.debug("KFTCFaceFilter] Processing Key ["+H_CLIENT_ID+"], value ["+clientId+"]");
}
} catch (Exception e) {
logger.error(e.getMessage());
}
AdapterManager adapterManager = AdapterManager.getInstance();
AdapterGroupVO adptGrpVO = adapterManager.getAdapterGroupVO(adptGrpName);
String encode = adptGrpVO.getMessageEncode();
if (message == null) {
returnMessage = new JSONObject();
} else {
if (message instanceof JSONObject) {
returnMessage = (JSONObject) message;
} else if (message instanceof String) {
returnMessage = parseJson((String) message);
} else if (message instanceof byte[]) {
returnMessage = parseJson(new String((byte[]) message, encode));
}
}
returnMessage.put(B_ORG_CODE, orgCode);
returnMessage.put(B_TRANSACTION_ID, transaction_id);
returnMessage.put(B_REQUEST_DATETIME, request_datetime);
return returnMessage.toJSONString();
}
private JSONObject parseJson(String message) throws Exception {
if (message == null) {
return null;
}
return (JSONObject) JSONValue.parse(message);
}
public Map<String, String> getInboundHeaderProp(Properties prop) {
Properties header = new Properties();
Map<String, String> inboundHeaderMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
try {
Object headerObj = prop.get(HttpAdapterServiceKey.INBOUND_HEADER);
if (headerObj instanceof Properties) { //tomcat
header = (Properties) headerObj;
for (String name : header.stringPropertyNames()) {
inboundHeaderMap.put(name, header.getProperty(name));
}
} else if (headerObj instanceof String) { //weblogic
String str = (String) headerObj;
str = str.substring(1, str.length() - 1);
// key=value 쌍 분리
String[] entries = str.split(",");
for (String entry : entries) {
String[] kv = entry.split("=", 2);
if (kv.length == 2) {
inboundHeaderMap.put(kv[0].trim(), kv[1].trim());
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return inboundHeaderMap;
}
@Override
public Object doPostFilter(String adptGrpName, String adptName, Properties prop, Object message,
Properties tempProp) throws Exception {
return message;
}
}

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