From 91631ea7504b4e88233e146327f5068189c99d10 Mon Sep 17 00:00:00 2001 From: Rinjae Date: Thu, 3 Sep 2026 15:46:43 +0900 Subject: [PATCH] =?UTF-8?q?"X-API-KEY=20=EB=B0=A9=EC=8B=9D=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0=20=EB=B0=8F=20=EB=82=B4=EB=B6=80=20API=20=ED=86=A0?= =?UTF-8?q?=ED=81=B0=20=EA=B2=80=EC=A6=9D=EC=9C=BC=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20-=20ApiKeyInterceptor=20=EC=82=AD=EC=A0=9C,=20Inter?= =?UTF-8?q?nalApiTokenInterceptor=20=EB=8F=84=EC=9E=85=20-=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20config=20=EB=B0=8F=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20X-Internal-Token=20=EB=B0=A9=EC=8B=9D?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=88=98=EC=A0=95=20-=20PortalPropertySer?= =?UTF-8?q?vice=20=EC=97=B0=EA=B4=80=20=EC=BD=94=EB=93=9C=20=EC=A0=95?= =?UTF-8?q?=EB=A6=AC=20=EB=B0=8F=20=EB=B6=88=ED=95=84=EC=9A=94=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=20=EC=A0=9C=EA=B1=B0"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- WebContent/WEB-INF/springapp-servlet.xml | 2 +- http/client-man-auth-key-test.http | 4 +- .../common/interceptor/ApiKeyInterceptor.java | 64 -------------- .../InternalApiTokenInterceptor.java | 44 ++++++++++ .../user/PortalUserApprovalListener.java | 83 ------------------- 5 files changed, 47 insertions(+), 150 deletions(-) delete mode 100644 src/main/java/com/eactive/eai/rms/common/interceptor/ApiKeyInterceptor.java create mode 100644 src/main/java/com/eactive/eai/rms/common/interceptor/InternalApiTokenInterceptor.java diff --git a/WebContent/WEB-INF/springapp-servlet.xml b/WebContent/WEB-INF/springapp-servlet.xml index 66d4464..f0f58b4 100644 --- a/WebContent/WEB-INF/springapp-servlet.xml +++ b/WebContent/WEB-INF/springapp-servlet.xml @@ -110,7 +110,7 @@ - + diff --git a/http/client-man-auth-key-test.http b/http/client-man-auth-key-test.http index 040f379..e1ff35b 100644 --- a/http/client-man-auth-key-test.http +++ b/http/client-man-auth-key-test.http @@ -2,7 +2,7 @@ POST http://localhost:7090/monitoring/_onl/admin/authserver/clientMan.json?serviceType=APIGW Content-Type: application/json User-Agent: insomnia/10.1.1 -X-API-KEY: EAPIM_KEY +X-Internal-Token: {{internalApiToken}} action: insert target_host: stg @@ -118,4 +118,4 @@ target_host: stg "errTransformYn": "N" } ] -} \ No newline at end of file +} diff --git a/src/main/java/com/eactive/eai/rms/common/interceptor/ApiKeyInterceptor.java b/src/main/java/com/eactive/eai/rms/common/interceptor/ApiKeyInterceptor.java deleted file mode 100644 index dacde3f..0000000 --- a/src/main/java/com/eactive/eai/rms/common/interceptor/ApiKeyInterceptor.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.eactive.eai.rms.common.interceptor; - -import com.eactive.apim.portal.portalproperty.service.PortalPropertyService; -import java.io.IOException; -import java.util.Map; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; -import org.springframework.util.StringUtils; -import org.springframework.web.servlet.HandlerInterceptor; - -@Component -@Slf4j -public class ApiKeyInterceptor implements HandlerInterceptor { - - private static final String API_KEY_HEADER = "X-API-KEY"; - private static final String PORTAL_PROPERTY_GROUP = "Portal"; - - @Autowired - private PortalPropertyService portalPropertyService; - - @Override - public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { - try { - String requestApiKey = request.getHeader(API_KEY_HEADER); - - if (StringUtils.isEmpty(requestApiKey)) { - log.warn("Request received without API key. URI: {}", request.getRequestURI()); - sendErrorResponse(response, HttpServletResponse.SC_UNAUTHORIZED, "API key is missing"); - return false; - } - - Map properties = portalPropertyService.getPortalPropertiesAsMap(PORTAL_PROPERTY_GROUP); - String validApiKey = properties.get("apiKey"); - - if (StringUtils.isEmpty(validApiKey)) { - log.error("API key not configured in properties"); - sendErrorResponse(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "API key is not configured"); - return false; - } - - if (!validApiKey.equals(requestApiKey)) { - log.warn("Invalid API key received. URI: {}", request.getRequestURI()); - sendErrorResponse(response, HttpServletResponse.SC_FORBIDDEN, "Invalid API key"); - return false; - } - - return true; - - } catch (Exception e) { - log.error("Error processing API key validation", e); - sendErrorResponse(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Error processing request"); - return false; - } - } - - private void sendErrorResponse(HttpServletResponse response, int status, String message) throws IOException { - response.setStatus(status); - response.setContentType("application/json"); - response.getWriter().write(String.format("{\"error\": \"%s\"}", message)); - } -} diff --git a/src/main/java/com/eactive/eai/rms/common/interceptor/InternalApiTokenInterceptor.java b/src/main/java/com/eactive/eai/rms/common/interceptor/InternalApiTokenInterceptor.java new file mode 100644 index 0000000..63e32b5 --- /dev/null +++ b/src/main/java/com/eactive/eai/rms/common/interceptor/InternalApiTokenInterceptor.java @@ -0,0 +1,44 @@ +package com.eactive.eai.rms.common.interceptor; + +import com.eactive.apim.portal.common.internal.InternalApiTokenService; +import java.io.IOException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.HandlerInterceptor; + +/** + * {@code /_onl/**} 내부 연동 요청을 공유 내부 토큰으로 검증한다. + * + *

토큰과 헤더명은 PTL_PROPERTY {@code Portal / internal.api.header-name}, + * {@code Portal / internal.api.token}을 사용한다. admin은 토큰을 생성하지 않고 읽기 전용으로 + * 검증하므로, portal이 먼저 기동되어 토큰을 생성해야 한다.

+ */ +@Component +@RequiredArgsConstructor +@Slf4j +public class InternalApiTokenInterceptor implements HandlerInterceptor { + + private final InternalApiTokenService internalApiTokenService; + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException { + String headerName = internalApiTokenService.findHeaderName(); + String presentedToken = request.getHeader(headerName); + + if (!internalApiTokenService.matchesReadOnly(presentedToken)) { + log.warn("Invalid internal API token. URI: {}, header: {}", request.getRequestURI(), headerName); + sendErrorResponse(response, HttpServletResponse.SC_UNAUTHORIZED, "Internal API token is invalid"); + return false; + } + return true; + } + + private void sendErrorResponse(HttpServletResponse response, int status, String message) throws IOException { + response.setStatus(status); + response.setContentType("application/json;charset=UTF-8"); + response.getWriter().write(String.format("{\"error\": \"%s\"}", message)); + } +} diff --git a/src/main/java/com/eactive/eai/rms/onl/apim/approval/user/PortalUserApprovalListener.java b/src/main/java/com/eactive/eai/rms/onl/apim/approval/user/PortalUserApprovalListener.java index 210c823..f152071 100644 --- a/src/main/java/com/eactive/eai/rms/onl/apim/approval/user/PortalUserApprovalListener.java +++ b/src/main/java/com/eactive/eai/rms/onl/apim/approval/user/PortalUserApprovalListener.java @@ -6,7 +6,6 @@ import com.eactive.apim.portal.approval.statemachine.listener.ApprovalListener; import com.eactive.apim.portal.portalorg.entity.PortalOrg; import com.eactive.apim.portal.portalorg.entity.PortalOrgEnums.ApprovalStatus; import com.eactive.apim.portal.portalorg.entity.PortalOrgEnums.OrgStatus; -import com.eactive.apim.portal.portalproperty.service.PortalPropertyService; import com.eactive.apim.portal.portaluser.entity.PortalUser; import com.eactive.apim.portal.portaluser.entity.PortalUserEnums; import com.eactive.apim.portal.template.entity.MessageCode; @@ -14,27 +13,15 @@ import com.eactive.apim.portal.template.service.MessageRecipient; import com.eactive.apim.portal.template.service.MessageSendService; import com.eactive.eai.rms.data.entity.onl.apim.portalorg.PortalOrgService; import com.eactive.eai.rms.data.entity.onl.apim.portaluser.PortalUserService; -import com.eactive.eai.rms.onl.apim.portalorg.PortalOrgUI; -import com.eactive.eai.rms.onl.apim.portalorg.PortalOrgUIMapper; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.HashMap; import java.util.Map; import lombok.RequiredArgsConstructor; -import org.apache.http.client.HttpClient; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.impl.client.HttpClientBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.client.RestTemplate; @Service @Transactional @@ -42,36 +29,9 @@ import org.springframework.web.client.RestTemplate; public class PortalUserApprovalListener implements ApprovalListener { private static final Logger logger = LoggerFactory.getLogger(PortalUserApprovalListener.class); - private static final String PORTAL_PROPERTY_GROUP = "Portal"; - private final PortalPropertyService portalPropertyService; private final PortalOrgService portalOrgService; private final PortalUserService portalUserService; private final MessageSendService messageSendService; - private final PortalOrgUIMapper portalOrgUIMapper; - - public static RestTemplate createRestTemplateWithTimeout(int connectTimeout) { - - RequestConfig requestConfig = RequestConfig.custom() - .setConnectionRequestTimeout(connectTimeout) - .setConnectTimeout(connectTimeout) - .setSocketTimeout(connectTimeout) - .build(); - - HttpClient httpClient = - HttpClientBuilder.create() - .setMaxConnTotal(50) - .setMaxConnPerRoute(20) - .setDefaultRequestConfig(requestConfig) - .build(); - - HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(); - factory.setHttpClient(httpClient); - - RestTemplate restTemplate = new RestTemplate(); - restTemplate.setRequestFactory(factory); - - return restTemplate; - } @Override public void execute(Approval approval, Map options) throws ApprovalDeployException { @@ -88,7 +48,6 @@ public class PortalUserApprovalListener implements ApprovalListener { portalOrg.setOrgStatus(OrgStatus.ACTIVE); portalOrgService.save(portalOrg); - // syncStaging(portalOrgUIMapper.toVo(portalOrg)); sendApprovalResult(portalUser); } @@ -106,48 +65,6 @@ public class PortalUserApprovalListener implements ApprovalListener { messageSendService.sendMessage(MessageCode.MANAGER_WITH_ORG_REGISTER_APPROVED, recipient, params); } - /** - * @deprecated 광주은행에서는 사용하지 않음. (개발/운영 완전 분리) - * @param portalOrgUI - * @throws ApprovalDeployException - */ - private void syncStaging(PortalOrgUI portalOrgUI) throws ApprovalDeployException { - throw new RuntimeException("Unable use this method(syncStaging())"); - -// // 프록시를 통한 스테이징 서버 호출 추가 -// Map properties = portalPropertyService.getPortalPropertiesAsMap(PORTAL_PROPERTY_GROUP); -// String apiKey = properties.get("apiKey"); -// String proxyUrl = properties.get("portal.url"); -// String timeout = properties.getOrDefault("deploy.proxy_timeout", "10000"); -// String proxyEndpoint = proxyUrl + "/_onl/apim/portalorg/portalOrgMan.json"; -// -// HttpHeaders headers = new HttpHeaders(); -// headers.setContentType(MediaType.APPLICATION_JSON); -// headers.set("X-API-KEY", apiKey); -// headers.set("target_host", "stg"); -// headers.set("action", "insert"); -// -// portalOrgUI.setCompRegFile(null); -// HttpEntity request = new HttpEntity<>(portalOrgUI, headers); -// -// try { -// ResponseEntity response = createRestTemplateWithTimeout(Integer.parseInt(timeout)).exchange( -// proxyEndpoint, -// HttpMethod.POST, -// request, -// String.class -// ); -// -// if (!response.getStatusCode().is2xxSuccessful()) { -// logger.error("Failed to sync with staging server. Status: {}, Body: {}", response.getStatusCode(), response.getBody()); -// throw new ApprovalDeployException("기관 정보 배포에 실패 했습니다."); -// } -// } catch (Exception e) { -// logger.error("Failed to sync with staging server. {}", e.getMessage()); -// throw new ApprovalDeployException("기관 정보 배포에 실패 했습니다."); -// } - } - @Override public void rollback(Approval approval) { logger.debug("Rollback started for approval ID: {}, type: {}, status: {}",