"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:mapping path="/_onl/**"/>
|
||||
<bean class="com.eactive.eai.rms.common.interceptor.ApiKeyInterceptor" />
|
||||
<bean class="com.eactive.eai.rms.common.interceptor.InternalApiTokenInterceptor" />
|
||||
</mvc:interceptor>
|
||||
<!-- BaseRestController 엔드포인트 IP 화이트리스트 인증 -->
|
||||
<mvc:interceptor>
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.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<String, Object> 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<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
|
||||
public void rollback(Approval approval) {
|
||||
logger.debug("Rollback started for approval ID: {}, type: {}, status: {}",
|
||||
|
||||
Reference in New Issue
Block a user