diff --git a/src/main/java/com/eactive/eai/custom/adapter/http/client/impl/filter/DJBErpEncFieldOutCryptoFilter.java b/src/main/java/com/eactive/eai/custom/adapter/http/client/impl/filter/DJBErpEncFieldOutCryptoFilter.java new file mode 100644 index 0000000..c38328c --- /dev/null +++ b/src/main/java/com/eactive/eai/custom/adapter/http/client/impl/filter/DJBErpEncFieldOutCryptoFilter.java @@ -0,0 +1,53 @@ +package com.eactive.eai.custom.adapter.http.client.impl.filter; + +import java.util.Properties; + +import com.eactive.eai.adapter.http.client.impl.filter.HttpClientAdapterFilter; +import com.eactive.eai.adapter.http.client.impl.filter.OutCryptoFilter; +import com.eactive.eai.common.security.keyderiv.KeyDerivationStrategy; + +/** + * DJBank 더존 연동 암복호화 필터 + * 전문에서 groupSeq, empSeq를 추출한다. + * 암복호화 키 생성에 HSM에서 master key를 가져오고, groupSeq를 조합 + * empSeq는 AES GCM의 AAD로 사용(PROP_AAD_HEADER prop 세팅) + * + */ +public class DJBErpEncFieldOutCryptoFilter implements HttpClientAdapterFilter { + + private final OutCryptoFilter filter = new OutCryptoFilter(); + + @Override + public Object doPreFilter(String adptGrpName, String adptName, Properties prop, Object message, Properties tempProp) + throws Exception { + setContext(tempProp); + return filter.doPreFilter(adptGrpName, adptName, prop, message, setProp(tempProp)); + } + + @Override + public Object doPostFilter(String adptGrpName, String adptName, Properties prop, Object message, + Properties tempProp) throws Exception { + setContext(tempProp); + return filter.doPostFilter(adptGrpName, adptName, prop, message, setProp(tempProp)); + } + + private void setContext(Properties tempProp) { + String empSeq = ""; + //TODO : empSeq 추출 + tempProp.setProperty(OutCryptoFilter.PROP_AAD_HEADER, empSeq); + + String groupSeq = ""; + //TODO : groupSeq 추출 + tempProp.setProperty(KeyDerivationStrategy.PARAM_CONTEXT_KEY, groupSeq); + } + + private Properties setProp(Properties p) { + p.setProperty(OutCryptoFilter.PROP_DEC_FROM_PATH, "/encryptedData"); + p.setProperty(OutCryptoFilter.PROP_ENC_TO_PATH, "/encryptedData"); + + return p; + } + + + +} diff --git a/src/main/java/com/eactive/eai/custom/adapter/http/client/impl/filter/EncFieldOutCryptoFilter.java b/src/main/java/com/eactive/eai/custom/adapter/http/client/impl/filter/EncFieldOutCryptoFilter.java new file mode 100644 index 0000000..c33e77a --- /dev/null +++ b/src/main/java/com/eactive/eai/custom/adapter/http/client/impl/filter/EncFieldOutCryptoFilter.java @@ -0,0 +1,31 @@ +package com.eactive.eai.custom.adapter.http.client.impl.filter; + +import java.util.Properties; + +import com.eactive.eai.adapter.http.client.impl.filter.HttpClientAdapterFilter; +import com.eactive.eai.adapter.http.client.impl.filter.OutCryptoFilter; +/** + * 제주은행 Open API 기본 암복호화 필더 + */ +public class EncFieldOutCryptoFilter implements HttpClientAdapterFilter { + + private final OutCryptoFilter filter = new OutCryptoFilter(); + + protected Properties setProp(Properties p) { + p.setProperty(OutCryptoFilter.PROP_DEC_FROM_PATH, "/encryptedData"); + p.setProperty(OutCryptoFilter.PROP_ENC_TO_PATH, "/encryptedData"); + return p; + } + + @Override + public Object doPreFilter(String adptGrpName, String adptName, Properties prop, Object message, Properties tempProp) + throws Exception { + return filter.doPreFilter(adptGrpName, adptName, prop, message, setProp(tempProp)); + } + + @Override + public Object doPostFilter(String adptGrpName, String adptName, Properties prop, Object message, + Properties tempProp) throws Exception { + return filter.doPostFilter(adptGrpName, adptName, prop, message, setProp(tempProp)); + } +} diff --git a/src/main/java/com/eactive/eai/custom/adapter/http/client/impl/filter/JejubankEncFieldOutCryptoFilter.java b/src/main/java/com/eactive/eai/custom/adapter/http/client/impl/filter/JejubankEncFieldOutCryptoFilter.java new file mode 100644 index 0000000..469abe1 --- /dev/null +++ b/src/main/java/com/eactive/eai/custom/adapter/http/client/impl/filter/JejubankEncFieldOutCryptoFilter.java @@ -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; + +/** + * TODO : 업체 암복호화에 맟추어 커스텀 필요 + */ +public class JejubankEncFieldOutCryptoFilter 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; + } +} diff --git a/src/main/java/com/eactive/eai/custom/adapter/http/client/impl/filter/KakaobankEncFieldOutCryptoFilter.java b/src/main/java/com/eactive/eai/custom/adapter/http/client/impl/filter/KakaobankEncFieldOutCryptoFilter.java new file mode 100644 index 0000000..a168a55 --- /dev/null +++ b/src/main/java/com/eactive/eai/custom/adapter/http/client/impl/filter/KakaobankEncFieldOutCryptoFilter.java @@ -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; + +/** + * TODO : 업체 암복호화에 맟추어 커스텀 필요 + */ +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; + } + +} diff --git a/src/main/java/com/eactive/eai/custom/adapter/http/client/impl/filter/KakaopayEncFieldOutCryptoFilter.java b/src/main/java/com/eactive/eai/custom/adapter/http/client/impl/filter/KakaopayEncFieldOutCryptoFilter.java new file mode 100644 index 0000000..69331fe --- /dev/null +++ b/src/main/java/com/eactive/eai/custom/adapter/http/client/impl/filter/KakaopayEncFieldOutCryptoFilter.java @@ -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; + +/** + * TODO : 업체 암복호화에 맟추어 커스텀 필요 + */ +public class KakaopayEncFieldOutCryptoFilter 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; + } +} diff --git a/src/main/java/com/eactive/eai/custom/adapter/http/client/impl/filter/NaverfincorpEncFieldOutCryptoFilter.java b/src/main/java/com/eactive/eai/custom/adapter/http/client/impl/filter/NaverfincorpEncFieldOutCryptoFilter.java new file mode 100644 index 0000000..d8e2450 --- /dev/null +++ b/src/main/java/com/eactive/eai/custom/adapter/http/client/impl/filter/NaverfincorpEncFieldOutCryptoFilter.java @@ -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; + +/** + * TODO : 업체 암복호화에 맟추어 커스텀 필요 + */ +public class NaverfincorpEncFieldOutCryptoFilter 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; + } + +} diff --git a/src/main/java/com/eactive/eai/custom/adapter/http/client/impl/filter/TossbankEncFieldOutCryptoFilter.java b/src/main/java/com/eactive/eai/custom/adapter/http/client/impl/filter/TossbankEncFieldOutCryptoFilter.java new file mode 100644 index 0000000..eefadaf --- /dev/null +++ b/src/main/java/com/eactive/eai/custom/adapter/http/client/impl/filter/TossbankEncFieldOutCryptoFilter.java @@ -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; + +/** + * TODO : 업체 암복호화에 맟추어 커스텀 필요 + */ +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; + } +} diff --git a/src/main/java/com/eactive/eai/custom/adapter/http/dynamic/filter/DJBErpCryptoFilter.java b/src/main/java/com/eactive/eai/custom/adapter/http/dynamic/filter/DJBErpCryptoFilter.java deleted file mode 100644 index d65531c..0000000 --- a/src/main/java/com/eactive/eai/custom/adapter/http/dynamic/filter/DJBErpCryptoFilter.java +++ /dev/null @@ -1,82 +0,0 @@ -package com.eactive.eai.custom.adapter.http.dynamic.filter; - -import java.util.HashMap; -import java.util.Map; -import java.util.Properties; - -import javax.servlet.http.HttpServletRequest; - -import org.apache.commons.lang3.StringUtils; - -import com.eactive.eai.adapter.http.dynamic.filter.InCryptoFilter; -import com.eactive.eai.util.JsonPathUtil; - -/** - * DJB ERP 연동용 암복호화 필터. - * - * DYNAMIC 키 컨텍스트 추출 규칙: - * HTTP 헤더 {@code httpHeaderGroup}의 값은 JSON 객체이며, - * 그 중 {@code x-emp-nm} 필드 값을 {@code groupSeq}로 - * runtimeContext에 담아 키 도출 전략에 전달한다. - * - * 어댑터 프로퍼티 (부모 클래스 공통 설정 외 추가 키): - * crypto.context.key runtimeContext에 넣을 키 이름 (기본: groupSeq) - * crypto.context.header 컨텍스트 JSON이 담긴 HTTP 헤더명 (기본: httpHeaderGroup) - * crypto.context.header.field 헤더 JSON에서 추출할 필드명 (기본: x-emp-nm) - * - * 어댑터 프로퍼티 설정 예: - * crypto.module.name = AES_GCM_NoPadding_DYNAMIC_sample - * crypto.scope = FIELD - * crypto.dec.enabled = Y - * crypto.enc.enabled = N - * crypto.dec.from.path = /encryptedData - * crypto.dec.to.path = / - * crypto.context.key = groupSeq - * crypto.context.header = httpHeaderGroup - * crypto.context.header.field = x-emp-nm - * - * 필터 타입 등록 (FQCN): - * com.eactive.eai.custom.adapter.http.dynamic.filter.DJBErpCryptoFilter - */ -public class DJBErpCryptoFilter extends InCryptoFilter { - - public static final String PROP_CONTEXT_KEY = "crypto.context.key"; - public static final String PROP_CONTEXT_HEADER = "crypto.context.header"; - public static final String PROP_CONTEXT_HEADER_FIELD = "crypto.context.header.field"; - - private static final String DEFAULT_CONTEXT_KEY = "groupSeq"; - private static final String DEFAULT_CONTEXT_HEADER = "httpHeaderGroup"; - private static final String DEFAULT_CONTEXT_HEADER_FIELD = "x-emp-nm"; - - /** - * httpHeaderGroup 헤더(JSON)에서 x-emp-nm 값을 추출하여 groupSeq로 매핑한다. - * 헤더가 없거나 필드를 찾을 수 없으면 빈 Map을 반환한다. - */ - @Override - protected Map buildRuntimeContext(Properties prop, HttpServletRequest request) { - Map 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; - } -} diff --git a/src/main/java/com/eactive/eai/custom/adapter/http/dynamic/filter/DJBErpEncFieldInCryptoFilter.java b/src/main/java/com/eactive/eai/custom/adapter/http/dynamic/filter/DJBErpEncFieldInCryptoFilter.java new file mode 100644 index 0000000..ef37850 --- /dev/null +++ b/src/main/java/com/eactive/eai/custom/adapter/http/dynamic/filter/DJBErpEncFieldInCryptoFilter.java @@ -0,0 +1,74 @@ +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.client.impl.filter.OutCryptoFilter; +import com.eactive.eai.adapter.http.dynamic.filter.HttpAdapterFilter; +import com.eactive.eai.adapter.http.dynamic.filter.InCryptoFilter; +import com.eactive.eai.common.security.keyderiv.KeyDerivationStrategy; + +/** + * 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 DJBErpEncFieldInCryptoFilter 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) { + String empSeq = ""; + // TODO : empSeq 추출 + tempProp.setProperty(InCryptoFilter.PROP_AAD_HEADER, empSeq); + + String groupSeq = ""; + // TODO : groupSeq 추출 + tempProp.setProperty(KeyDerivationStrategy.PARAM_CONTEXT_KEY, groupSeq); + } + + @Override + public Object doPreFilter(String adptGrpName, String adptName, Object message, Properties prop, + HttpServletRequest request, HttpServletResponse response) throws Exception { + setContext(prop); + 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 { + setContext(prop); + return filter.doPostFilter(adptGrpName, adptName, resultMessage, setProp(prop), request, response); + } + +} diff --git a/src/main/java/com/eactive/eai/custom/adapter/http/dynamic/filter/EncFieldCryptoFilter.java b/src/main/java/com/eactive/eai/custom/adapter/http/dynamic/filter/EncFieldInCryptoFilter.java similarity index 84% rename from src/main/java/com/eactive/eai/custom/adapter/http/dynamic/filter/EncFieldCryptoFilter.java rename to src/main/java/com/eactive/eai/custom/adapter/http/dynamic/filter/EncFieldInCryptoFilter.java index 01af811..ceb1e47 100644 --- a/src/main/java/com/eactive/eai/custom/adapter/http/dynamic/filter/EncFieldCryptoFilter.java +++ b/src/main/java/com/eactive/eai/custom/adapter/http/dynamic/filter/EncFieldInCryptoFilter.java @@ -9,12 +9,12 @@ import com.eactive.eai.adapter.http.dynamic.filter.InCryptoFilter; import com.eactive.eai.adapter.http.dynamic.filter.HttpAdapterFilter; /** - * CryptoFilter 프로퍼티를 멤버변수로 관리하는 HttpAdapterFilter 구현체. + * InCryptoFilter를 멤버변수로 관리하는 HttpAdapterFilter 구현체. * * 멤버변수에 세팅된 값을 Properties로 변환하여 {@link InCryptoFilter}에 위임한다. - * 값이 blank인 멤버변수는 Properties에 포함하지 않으며, CryptoFilter의 기본값이 적용된다. + * 값이 blank인 멤버변수는 Properties에 포함하지 않으며, InCryptoFilter의 기본값이 적용된다. */ -public class EncFieldCryptoFilter implements HttpAdapterFilter { +public class EncFieldInCryptoFilter implements HttpAdapterFilter { private final InCryptoFilter filter = new InCryptoFilter(); @@ -30,7 +30,7 @@ public class EncFieldCryptoFilter implements HttpAdapterFilter { return filter.doPostFilter(adptGrpName, adptName, resultMessage, setProp(prop), request, response); } - private Properties setProp(Properties p) { + protected Properties setProp(Properties p) { p.setProperty(InCryptoFilter.PROP_DEC_FROM_PATH, "/encryptedData"); p.setProperty(InCryptoFilter.PROP_ENC_TO_PATH, "/encryptedData"); return p; diff --git a/src/main/java/com/eactive/eai/custom/adapter/http/dynamic/filter/KakaobankEncFieldInCryptoFilter.java b/src/main/java/com/eactive/eai/custom/adapter/http/dynamic/filter/KakaobankEncFieldInCryptoFilter.java new file mode 100644 index 0000000..c260ee3 --- /dev/null +++ b/src/main/java/com/eactive/eai/custom/adapter/http/dynamic/filter/KakaobankEncFieldInCryptoFilter.java @@ -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; + +/** + * TODO : 업체 암복호화에 맟추어 커스텀 필요 + */ +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; + } + +} diff --git a/src/main/java/com/eactive/eai/custom/adapter/http/dynamic/filter/KakaopayEncFieldInCryptoFilter.java b/src/main/java/com/eactive/eai/custom/adapter/http/dynamic/filter/KakaopayEncFieldInCryptoFilter.java new file mode 100644 index 0000000..c759406 --- /dev/null +++ b/src/main/java/com/eactive/eai/custom/adapter/http/dynamic/filter/KakaopayEncFieldInCryptoFilter.java @@ -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; + +/** + * TODO : 업체 암복호화에 맟추어 커스텀 필요 + */ +public class KakaopayEncFieldInCryptoFilter 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; + } + +} diff --git a/src/main/java/com/eactive/eai/custom/adapter/http/dynamic/filter/NaverfincorpEncFieldInCryptoFilter.java b/src/main/java/com/eactive/eai/custom/adapter/http/dynamic/filter/NaverfincorpEncFieldInCryptoFilter.java new file mode 100644 index 0000000..71ba3e1 --- /dev/null +++ b/src/main/java/com/eactive/eai/custom/adapter/http/dynamic/filter/NaverfincorpEncFieldInCryptoFilter.java @@ -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; + +/** + * TODO : 업체 암복호화에 맟추어 커스텀 필요 + */ +public class NaverfincorpEncFieldInCryptoFilter 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; + } + +} diff --git a/src/main/java/com/eactive/eai/custom/adapter/http/dynamic/filter/TossbankEncFieldInCryptoFilter.java b/src/main/java/com/eactive/eai/custom/adapter/http/dynamic/filter/TossbankEncFieldInCryptoFilter.java new file mode 100644 index 0000000..8ca0c5b --- /dev/null +++ b/src/main/java/com/eactive/eai/custom/adapter/http/dynamic/filter/TossbankEncFieldInCryptoFilter.java @@ -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; + +/** + * TODO : 업체 암복호화에 맟추어 커스텀 필요 + */ +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; + } + +} diff --git a/src/test/java/com/eactive/eai/custom/adapter/http/dynamic/filter/DJBErpCryptoFilterTest.java b/src/test/java/com/eactive/eai/custom/adapter/http/dynamic/filter/DJBErpCryptoFilterTest.java deleted file mode 100644 index a423e0b..0000000 --- a/src/test/java/com/eactive/eai/custom/adapter/http/dynamic/filter/DJBErpCryptoFilterTest.java +++ /dev/null @@ -1,317 +0,0 @@ -package com.eactive.eai.custom.adapter.http.dynamic.filter; - -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.*; - -import java.nio.charset.StandardCharsets; -import java.util.Base64; -import java.util.Map; -import java.util.Properties; - -import org.mockito.ArgumentCaptor; - -import javax.servlet.http.HttpServletRequest; - -import org.json.simple.JSONObject; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.MethodOrderer; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestMethodOrder; -import org.springframework.beans.factory.support.BeanDefinitionBuilder; -import org.springframework.context.support.GenericApplicationContext; - -import com.eactive.eai.common.security.CryptoModuleService; -import com.eactive.eai.common.util.ApplicationContextProvider; - -@TestMethodOrder(MethodOrderer.DisplayName.class) -class DJBErpCryptoFilterTest { - - private static final String MODULE_NAME = "TEST_MODULE"; - // 복호화 후 평문 (JSON 문자열) - private static final String PLAIN_TEXT = "{\"userId\":\"U001\",\"name\":\"test\"}"; - private static final byte[] PLAIN_BYTES = PLAIN_TEXT.getBytes(StandardCharsets.UTF_8); - // 더미 암호문 bytes + Base64 표현 - private static final byte[] CIPHER_BYTES = new byte[]{0x10, 0x20, 0x30, 0x40}; - private static final String ENC_BASE64 = Base64.getEncoder().encodeToString(CIPHER_BYTES); - - // 기본 httpHeaderGroup 헤더 JSON (groupSeq|empNo 형태) - private static final String HEADER_JSON_WITH_PIPE = "{\"x-emp-nm\":\"G001|E001\"}"; - private static final String HEADER_JSON_WITHOUT_PIPE = "{\"x-emp-nm\":\"G001\"}"; - - private static GenericApplicationContext ctx; - private static CryptoModuleService mockCryptoService; - - private DJBErpCryptoFilter filter; - private HttpServletRequest mockRequest; - private Properties prop; - - // ========================================================================= - // 클래스 초기화 — Spring ApplicationContext + Mock CryptoModuleService - // ========================================================================= - - @BeforeAll - static void setUpClass() throws Exception { - mockCryptoService = mock(CryptoModuleService.class); - - ctx = new GenericApplicationContext(); - ctx.getBeanFactory().registerSingleton("cryptoModuleService", mockCryptoService); - ctx.registerBeanDefinition("applicationContextProvider", - BeanDefinitionBuilder.genericBeanDefinition(ApplicationContextProvider.class) - .getBeanDefinition()); - ctx.refresh(); - - when(mockCryptoService.decrypt(anyString(), anyMap(), any(byte[].class))) - .thenReturn(PLAIN_BYTES); - when(mockCryptoService.encrypt(anyString(), anyMap(), any(byte[].class))) - .thenReturn(CIPHER_BYTES); - } - - @AfterAll - static void tearDownClass() { - if (ctx != null) ctx.close(); - } - - @BeforeEach - void setUp() { - clearInvocations(mockCryptoService); // 이전 테스트의 호출 이력 초기화 (stubbing은 유지) - filter = new DJBErpCryptoFilter(); - mockRequest = mock(HttpServletRequest.class); - prop = new Properties(); - prop.setProperty(DJBErpCryptoFilter.PROP_MODULE_NAME, MODULE_NAME); - } - - // ========================================================================= - // 1. buildRuntimeContext - // ========================================================================= - - @Test - @DisplayName("1-1. x-emp-nm이 'groupSeq|empNo' 형태이면 '|' 앞부분만 groupSeq로 추출") - void buildRuntimeContext_pipeFormat_extractsGroupSeqOnly() { - when(mockRequest.getHeader("httpHeaderGroup")).thenReturn(HEADER_JSON_WITH_PIPE); - - Map runtimeCtx = filter.buildRuntimeContext(prop, mockRequest); - - assertEquals("G001", runtimeCtx.get("groupSeq")); - assertEquals(1, runtimeCtx.size()); - } - - @Test - @DisplayName("1-2. x-emp-nm에 '|'가 없으면 전체 값을 groupSeq로 사용") - void buildRuntimeContext_noPipe_usesFullValue() { - when(mockRequest.getHeader("httpHeaderGroup")).thenReturn(HEADER_JSON_WITHOUT_PIPE); - - Map runtimeCtx = filter.buildRuntimeContext(prop, mockRequest); - - assertEquals("G001", runtimeCtx.get("groupSeq")); - } - - @Test - @DisplayName("1-3. httpHeaderGroup 헤더가 없으면 빈 Map 반환") - void buildRuntimeContext_headerMissing_returnsEmptyMap() { - when(mockRequest.getHeader("httpHeaderGroup")).thenReturn(null); - - Map runtimeCtx = filter.buildRuntimeContext(prop, mockRequest); - - assertTrue(runtimeCtx.isEmpty()); - } - - @Test - @DisplayName("1-4. 헤더 JSON에 x-emp-nm 필드가 없으면 빈 Map 반환") - void buildRuntimeContext_fieldMissingInJson_returnsEmptyMap() { - when(mockRequest.getHeader("httpHeaderGroup")).thenReturn("{\"other-field\":\"value\"}"); - - Map runtimeCtx = filter.buildRuntimeContext(prop, mockRequest); - - assertTrue(runtimeCtx.isEmpty()); - } - - @Test - @DisplayName("1-5. 프로퍼티로 헤더명/필드명/컨텍스트키를 커스터마이징할 수 있다") - void buildRuntimeContext_customProps_overrideDefaults() { - prop.setProperty(DJBErpCryptoFilter.PROP_CONTEXT_HEADER, "X-Custom-Header"); - prop.setProperty(DJBErpCryptoFilter.PROP_CONTEXT_HEADER_FIELD, "emp-id"); - prop.setProperty(DJBErpCryptoFilter.PROP_CONTEXT_KEY, "empGroup"); - - when(mockRequest.getHeader("X-Custom-Header")).thenReturn("{\"emp-id\":\"GRP99|EMP99\"}"); - - Map runtimeCtx = filter.buildRuntimeContext(prop, mockRequest); - - assertEquals("GRP99", runtimeCtx.get("empGroup")); - } - - // ========================================================================= - // 2. doPreFilter — 복호화 - // ========================================================================= - - @Test - @DisplayName("2-1. crypto.dec.enabled=N 이면 message를 그대로 반환하고 복호화하지 않는다") - void doPreFilter_decDisabled_returnsOriginalWithoutDecrypt() throws Exception { - prop.setProperty("crypto.dec.enabled", "N"); - String original = "{\"encryptedData\":\"" + ENC_BASE64 + "\"}"; - - Object result = filter.doPreFilter(null, null, original, prop, mockRequest, null); - - assertSame(original, result); - verifyNoInteractions(mockCryptoService); - } - - @Test - @DisplayName("2-2. FIELD scope: /encryptedData 복호화 후 dec.to.path=/ 이면 body 전체를 평문으로 교체") - void doPreFilter_fieldScope_toRoot_replacesEntireBody() throws Exception { - setFieldDecryptProps("/encryptedData", "/"); - when(mockRequest.getHeader("httpHeaderGroup")).thenReturn(HEADER_JSON_WITH_PIPE); - - String body = "{\"encryptedData\":\"" + ENC_BASE64 + "\"}"; - Object result = filter.doPreFilter(null, null, body, prop, mockRequest, null); - - assertEquals(PLAIN_TEXT, result); - } - - @Test - @DisplayName("2-3. FIELD scope: dec.to.path가 특정 경로이면 해당 필드에 복호화 결과를 설정") - void doPreFilter_fieldScope_toSpecificPath_setsFieldInBody() throws Exception { - setFieldDecryptProps("/encryptedData", "/plainData"); - when(mockRequest.getHeader("httpHeaderGroup")).thenReturn(HEADER_JSON_WITH_PIPE); - - String body = "{\"encryptedData\":\"" + ENC_BASE64 + "\"}"; - String result = (String) filter.doPreFilter(null, null, body, prop, mockRequest, null); - - assertTrue(result.contains("\"encryptedData\""), "원본 필드가 유지되어야 한다"); - assertTrue(result.contains("\"plainData\""), "복호화 결과 필드가 추가되어야 한다"); - } - - @Test - @DisplayName("2-4. FIELD scope: 복호화 대상 필드가 JSON에 없으면 원본 body를 그대로 반환") - void doPreFilter_fieldScope_missingEncryptedField_returnsOriginalBody() throws Exception { - setFieldDecryptProps("/encryptedData", "/"); - when(mockRequest.getHeader("httpHeaderGroup")).thenReturn(HEADER_JSON_WITH_PIPE); - - String body = "{\"otherField\":\"value\"}"; - Object result = filter.doPreFilter(null, null, body, prop, mockRequest, null); - - assertEquals(body, result); - } - - @Test - @DisplayName("2-5. BODY scope: message가 Base64 String이면 전체 복호화") - void doPreFilter_bodyScope_stringMessage_decryptsWholeBody() throws Exception { - prop.setProperty("crypto.scope", "BODY"); - - Object result = filter.doPreFilter(null, null, ENC_BASE64, prop, mockRequest, null); - - assertEquals(PLAIN_TEXT, result); - } - - @Test - @DisplayName("2-6. message가 byte[]이면 UTF-8 변환 후 FIELD 복호화 처리") - void doPreFilter_fieldScope_byteArrayMessage_convertsAndDecrypts() throws Exception { - setFieldDecryptProps("/encryptedData", "/"); - when(mockRequest.getHeader("httpHeaderGroup")).thenReturn(HEADER_JSON_WITH_PIPE); - - byte[] bodyBytes = ("{\"encryptedData\":\"" + ENC_BASE64 + "\"}").getBytes(StandardCharsets.UTF_8); - - Object result = filter.doPreFilter(null, null, bodyBytes, prop, mockRequest, null); - - assertEquals(PLAIN_TEXT, result); - } - - @SuppressWarnings("unchecked") - @Test - @DisplayName("2-7. message가 JSONObject이면 toString() 변환 후 FIELD 복호화 처리") - void doPreFilter_fieldScope_jsonObjectMessage_convertsAndDecrypts() throws Exception { - setFieldDecryptProps("/encryptedData", "/"); - when(mockRequest.getHeader("httpHeaderGroup")).thenReturn(HEADER_JSON_WITH_PIPE); - - JSONObject jsonMsg = new JSONObject(); - jsonMsg.put("encryptedData", ENC_BASE64); - - Object result = filter.doPreFilter(null, null, jsonMsg, prop, mockRequest, null); - - assertEquals(PLAIN_TEXT, result); - } - - @SuppressWarnings("unchecked") - @Test - @DisplayName("2-8. FIELD scope: runtimeContext에 groupSeq가 올바르게 전달된다") - void doPreFilter_fieldScope_runtimeContextPassedToDecrypt() throws Exception { - setFieldDecryptProps("/encryptedData", "/"); - when(mockRequest.getHeader("httpHeaderGroup")).thenReturn(HEADER_JSON_WITH_PIPE); - - String body = "{\"encryptedData\":\"" + ENC_BASE64 + "\"}"; - filter.doPreFilter(null, null, body, prop, mockRequest, null); - - ArgumentCaptor ctxCaptor = ArgumentCaptor.forClass(Map.class); - verify(mockCryptoService).decrypt(eq(MODULE_NAME), ctxCaptor.capture(), any(byte[].class)); - assertEquals("G001", ctxCaptor.getValue().get("groupSeq")); - } - - // ========================================================================= - // 3. doPostFilter — 암호화 - // ========================================================================= - - @Test - @DisplayName("3-1. crypto.enc.enabled 기본값(N) — resultMessage를 그대로 반환하고 암호화하지 않는다") - void doPostFilter_encDisabledByDefault_returnsOriginalWithoutEncrypt() throws Exception { - String original = "{\"result\":\"ok\"}"; - - Object result = filter.doPostFilter(null, null, original, prop, mockRequest, null); - - assertSame(original, result); - verifyNoInteractions(mockCryptoService); - } - - @Test - @DisplayName("3-2. BODY scope, enc.enabled=Y — body 전체를 암호화하여 Base64 반환") - void doPostFilter_bodyScope_encEnabled_returnsBase64() throws Exception { - prop.setProperty("crypto.scope", "BODY"); - prop.setProperty("crypto.enc.enabled", "Y"); - - Object result = filter.doPostFilter(null, null, PLAIN_TEXT, prop, mockRequest, null); - - assertEquals(ENC_BASE64, result); - } - - @Test - @DisplayName("3-3. FIELD scope, enc.from.path=/ — body 전체 암호화 후 enc.to.path 필드로 래핑") - void doPostFilter_fieldScope_fromRoot_wrapsEncryptedInJson() throws Exception { - prop.setProperty("crypto.scope", "FIELD"); - prop.setProperty("crypto.enc.enabled", "Y"); - prop.setProperty("crypto.enc.from.path", "/"); - prop.setProperty("crypto.enc.to.path", "/encryptedData"); - - Object result = filter.doPostFilter(null, null, PLAIN_TEXT, prop, mockRequest, null); - - assertEquals("{\"encryptedData\":\"" + ENC_BASE64 + "\"}", result); - } - - @SuppressWarnings("unchecked") - @Test - @DisplayName("3-4. FIELD scope: runtimeContext에 groupSeq가 올바르게 전달된다") - void doPostFilter_fieldScope_runtimeContextPassedToEncrypt() throws Exception { - prop.setProperty("crypto.scope", "FIELD"); - prop.setProperty("crypto.enc.enabled", "Y"); - prop.setProperty("crypto.enc.from.path", "/"); - prop.setProperty("crypto.enc.to.path", "/encryptedData"); - when(mockRequest.getHeader("httpHeaderGroup")).thenReturn(HEADER_JSON_WITH_PIPE); - - filter.doPostFilter(null, null, PLAIN_TEXT, prop, mockRequest, null); - - ArgumentCaptor ctxCaptor = ArgumentCaptor.forClass(Map.class); - verify(mockCryptoService).encrypt(eq(MODULE_NAME), ctxCaptor.capture(), any(byte[].class)); - assertEquals("G001", ctxCaptor.getValue().get("groupSeq")); - } - - // ========================================================================= - // 헬퍼 - // ========================================================================= - - private void setFieldDecryptProps(String fromPath, String toPath) { - prop.setProperty("crypto.scope", "FIELD"); - prop.setProperty("crypto.dec.from.path", fromPath); - prop.setProperty("crypto.dec.to.path", toPath); - } -}