"X-API-KEY 방식 제거 및 내부 API 토큰 검증으로 변경
- ApiKeyInterceptor 삭제, InternalApiTokenInterceptor 도입 - 관련 config 및 테스트 코드 X-Internal-Token 방식으로 수정 - PortalPropertyService 연관 코드 정리 및 불필요 메서드 제거"
This commit is contained in:
@@ -110,7 +110,7 @@
|
|||||||
</mvc:interceptor>
|
</mvc:interceptor>
|
||||||
<mvc:interceptor>
|
<mvc:interceptor>
|
||||||
<mvc:mapping path="/_onl/**"/>
|
<mvc:mapping path="/_onl/**"/>
|
||||||
<bean class="com.eactive.eai.rms.common.interceptor.ApiKeyInterceptor" />
|
<bean class="com.eactive.eai.rms.common.interceptor.InternalApiTokenInterceptor" />
|
||||||
</mvc:interceptor>
|
</mvc:interceptor>
|
||||||
<!-- BaseRestController 엔드포인트 IP 화이트리스트 인증 -->
|
<!-- BaseRestController 엔드포인트 IP 화이트리스트 인증 -->
|
||||||
<mvc:interceptor>
|
<mvc:interceptor>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
POST http://localhost:7090/monitoring/_onl/admin/authserver/clientMan.json?serviceType=APIGW
|
POST http://localhost:7090/monitoring/_onl/admin/authserver/clientMan.json?serviceType=APIGW
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
User-Agent: insomnia/10.1.1
|
User-Agent: insomnia/10.1.1
|
||||||
X-API-KEY: EAPIM_KEY
|
X-Internal-Token: {{internalApiToken}}
|
||||||
action: insert
|
action: insert
|
||||||
target_host: stg
|
target_host: stg
|
||||||
|
|
||||||
|
|||||||
@@ -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<String, String> 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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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/**} 내부 연동 요청을 공유 내부 토큰으로 검증한다.
|
||||||
|
*
|
||||||
|
* <p>토큰과 헤더명은 PTL_PROPERTY {@code Portal / internal.api.header-name},
|
||||||
|
* {@code Portal / internal.api.token}을 사용한다. admin은 토큰을 생성하지 않고 읽기 전용으로
|
||||||
|
* 검증하므로, portal이 먼저 기동되어 토큰을 생성해야 한다.</p>
|
||||||
|
*/
|
||||||
|
@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));
|
||||||
|
}
|
||||||
|
}
|
||||||
-83
@@ -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.PortalOrg;
|
||||||
import com.eactive.apim.portal.portalorg.entity.PortalOrgEnums.ApprovalStatus;
|
import com.eactive.apim.portal.portalorg.entity.PortalOrgEnums.ApprovalStatus;
|
||||||
import com.eactive.apim.portal.portalorg.entity.PortalOrgEnums.OrgStatus;
|
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.PortalUser;
|
||||||
import com.eactive.apim.portal.portaluser.entity.PortalUserEnums;
|
import com.eactive.apim.portal.portaluser.entity.PortalUserEnums;
|
||||||
import com.eactive.apim.portal.template.entity.MessageCode;
|
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.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.portalorg.PortalOrgService;
|
||||||
import com.eactive.eai.rms.data.entity.onl.apim.portaluser.PortalUserService;
|
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.LocalDateTime;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import lombok.RequiredArgsConstructor;
|
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.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
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.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.web.client.RestTemplate;
|
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@Transactional
|
@Transactional
|
||||||
@@ -42,36 +29,9 @@ import org.springframework.web.client.RestTemplate;
|
|||||||
public class PortalUserApprovalListener implements ApprovalListener {
|
public class PortalUserApprovalListener implements ApprovalListener {
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(PortalUserApprovalListener.class);
|
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 PortalOrgService portalOrgService;
|
||||||
private final PortalUserService portalUserService;
|
private final PortalUserService portalUserService;
|
||||||
private final MessageSendService messageSendService;
|
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
|
@Override
|
||||||
public void execute(Approval approval, Map<String, Object> options) throws ApprovalDeployException {
|
public void execute(Approval approval, Map<String, Object> options) throws ApprovalDeployException {
|
||||||
@@ -88,7 +48,6 @@ public class PortalUserApprovalListener implements ApprovalListener {
|
|||||||
portalOrg.setOrgStatus(OrgStatus.ACTIVE);
|
portalOrg.setOrgStatus(OrgStatus.ACTIVE);
|
||||||
portalOrgService.save(portalOrg);
|
portalOrgService.save(portalOrg);
|
||||||
|
|
||||||
// syncStaging(portalOrgUIMapper.toVo(portalOrg));
|
|
||||||
sendApprovalResult(portalUser);
|
sendApprovalResult(portalUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,48 +65,6 @@ public class PortalUserApprovalListener implements ApprovalListener {
|
|||||||
messageSendService.sendMessage(MessageCode.MANAGER_WITH_ORG_REGISTER_APPROVED, recipient, params);
|
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<String, String> 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<PortalOrgUI> request = new HttpEntity<>(portalOrgUI, headers);
|
|
||||||
//
|
|
||||||
// try {
|
|
||||||
// ResponseEntity<String> 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
|
@Override
|
||||||
public void rollback(Approval approval) {
|
public void rollback(Approval approval) {
|
||||||
logger.debug("Rollback started for approval ID: {}, type: {}, status: {}",
|
logger.debug("Rollback started for approval ID: {}, type: {}, status: {}",
|
||||||
|
|||||||
Reference in New Issue
Block a user