diff --git a/.gitignore b/.gitignore
index 549e00a..843d938 100644
--- a/.gitignore
+++ b/.gitignore
@@ -31,3 +31,9 @@ build/
### VS Code ###
.vscode/
+
+### Key Files ###
+*.p12
+
+### yaml ###
+application-local.yml
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 5514160..bafe3fc 100644
--- a/pom.xml
+++ b/pom.xml
@@ -104,7 +104,12 @@
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
-
+
+ junit
+ junit
+ test
+
+
diff --git a/src/main/java/com/t3t/authenticationapi/AuthenticationApiApplication.java b/src/main/java/com/t3t/authenticationapi/AuthenticationApiApplication.java
index cfcaebf..118386f 100644
--- a/src/main/java/com/t3t/authenticationapi/AuthenticationApiApplication.java
+++ b/src/main/java/com/t3t/authenticationapi/AuthenticationApiApplication.java
@@ -2,9 +2,10 @@
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
-
+@ConfigurationPropertiesScan
@SpringBootApplication
@EnableDiscoveryClient
public class AuthenticationApiApplication {
diff --git a/src/main/java/com/t3t/authenticationapi/config/RestTemplateConfig.java b/src/main/java/com/t3t/authenticationapi/config/RestTemplateConfig.java
new file mode 100644
index 0000000..a5dbf2a
--- /dev/null
+++ b/src/main/java/com/t3t/authenticationapi/config/RestTemplateConfig.java
@@ -0,0 +1,52 @@
+package com.t3t.authenticationapi.config;
+
+import com.t3t.authenticationapi.keymanager.properties.SecretKeyManagerProperties;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.ssl.SSLContextBuilder;
+import org.springframework.boot.web.client.RestTemplateBuilder;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Profile;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
+import org.springframework.web.client.RestTemplate;
+
+import java.io.IOException;
+import java.security.*;
+import java.security.cert.CertificateException;
+import java.time.Duration;
+
+
+@Configuration
+public class RestTemplateConfig {
+
+ /**
+ * Secret Key Manager 인증서를 사용하여 요청을 보내기 위한 RestTemplate 빈 등록
+ * @author woody35545(구건모)
+ */
+ @Bean
+ @Profile("!local")
+ public RestTemplate sslRestTemplate(SecretKeyManagerProperties secretKeyManagerProperties)
+ throws KeyStoreException, IOException, UnrecoverableKeyException, NoSuchAlgorithmException, KeyManagementException, CertificateException {
+
+ KeyStore keyStore = KeyStore.getInstance(secretKeyManagerProperties.getCertKeyType());
+
+ keyStore.load(secretKeyManagerProperties.getCertKey().getInputStream(),
+ secretKeyManagerProperties.getPassword().toCharArray());
+
+ RestTemplate sslRestTemplate = new RestTemplateBuilder()
+ .setConnectTimeout(Duration.ofSeconds(5))
+ .setConnectTimeout(Duration.ofSeconds(5))
+ .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
+ .defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
+ .build();
+
+ sslRestTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory(HttpClients.custom()
+ .setSSLSocketFactory(new SSLConnectionSocketFactory(SSLContextBuilder.create()
+ .loadKeyMaterial(keyStore, secretKeyManagerProperties.getPassword().toCharArray()).build())).build()));
+
+ return sslRestTemplate;
+ }
+}
diff --git a/src/main/java/com/t3t/authenticationapi/exception/SecretKeyManagerApiRequestFailedException.java b/src/main/java/com/t3t/authenticationapi/exception/SecretKeyManagerApiRequestFailedException.java
new file mode 100644
index 0000000..a2d9aa7
--- /dev/null
+++ b/src/main/java/com/t3t/authenticationapi/exception/SecretKeyManagerApiRequestFailedException.java
@@ -0,0 +1,10 @@
+package com.t3t.authenticationapi.exception;
+
+/**
+ * Secret Key Manager API 요청이 실패한 경우 발생하는 예외
+ */
+public class SecretKeyManagerApiRequestFailedException extends RuntimeException{
+ public SecretKeyManagerApiRequestFailedException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/com/t3t/authenticationapi/keymanager/model/response/SecretKeyManagerResponse.java b/src/main/java/com/t3t/authenticationapi/keymanager/model/response/SecretKeyManagerResponse.java
new file mode 100644
index 0000000..b6736f4
--- /dev/null
+++ b/src/main/java/com/t3t/authenticationapi/keymanager/model/response/SecretKeyManagerResponse.java
@@ -0,0 +1,25 @@
+package com.t3t.authenticationapi.keymanager.model.response;
+
+import lombok.Getter;
+
+/**
+ * Secret Key Manager API의 응답 형식을 정의한 클래스
+ * @author woody35545(구건모)
+ */
+@Getter
+public class SecretKeyManagerResponse {
+ private SecretKeyManagerResponseHeaderPartDto header;
+ private SecretKeyManagerResponseBodyPartDto body;
+
+ @Getter
+ public static class SecretKeyManagerResponseHeaderPartDto {
+ private int resultCode;
+ private String resultMessage;
+ private String isSuccessful;
+ }
+
+ @Getter
+ public static class SecretKeyManagerResponseBodyPartDto {
+ private String secret;
+ }
+}
diff --git a/src/main/java/com/t3t/authenticationapi/keymanager/properties/SecretKeyManagerProperties.java b/src/main/java/com/t3t/authenticationapi/keymanager/properties/SecretKeyManagerProperties.java
new file mode 100644
index 0000000..89329b9
--- /dev/null
+++ b/src/main/java/com/t3t/authenticationapi/keymanager/properties/SecretKeyManagerProperties.java
@@ -0,0 +1,25 @@
+package com.t3t.authenticationapi.keymanager.properties;
+
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Profile;
+import org.springframework.core.io.Resource;
+
+/**
+ * Secret Key Manager 에서 사용될 속성을 저장하는 프로퍼티 클래스
+ * @author woody35545(구건모)
+ */
+@Getter
+@Setter
+@Profile("!local")
+@ConfigurationProperties(prefix = "t3t.secret-key-manager")
+public class SecretKeyManagerProperties {
+ private String appKey;
+ private String password;
+ private String certKeyType;
+ private String certKeyPath;
+ @Value("${t3t.secretKeyManager.certKeyPath}")
+ private Resource certKey;
+}
\ No newline at end of file
diff --git a/src/main/java/com/t3t/authenticationapi/keymanager/properties/SecretKeyProperties.java b/src/main/java/com/t3t/authenticationapi/keymanager/properties/SecretKeyProperties.java
new file mode 100644
index 0000000..8b39d03
--- /dev/null
+++ b/src/main/java/com/t3t/authenticationapi/keymanager/properties/SecretKeyProperties.java
@@ -0,0 +1,34 @@
+package com.t3t.authenticationapi.keymanager.properties;
+
+import lombok.Getter;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Profile;
+import org.springframework.stereotype.Component;
+
+/**
+ * Secret Key Manager 에 등록된 기밀 데이터의 key id를 저장하는 프로퍼티 클래스
+ * @author woody35545(구건모)
+ */
+@Profile("!local")
+@Getter
+@Component
+public class SecretKeyProperties {
+ @Value("${t3t.secretKeyManager.secrets.databaseServerIpAddress.keyId}")
+ private String databaseIpAddressKeyId;
+ @Value("${t3t.secretKeyManager.secrets.databaseServerPort.keyId}")
+ private String databasePortKeyId;
+ @Value("${t3t.secretKeyManager.secrets.databaseServerUsername.keyId}")
+ private String databaseNameKeyId;
+ @Value("${t3t.secretKeyManager.secrets.databaseName.keyId}")
+ private String databaseUsernameKeyId;
+ @Value("${t3t.secretKeyManager.secrets.databaseServerPassword.keyId}")
+ private String databasePasswordKeyId;
+ @Value("${t3t.secretKeyManager.secrets.jwtSecretKey.keyId}")
+ private String jwtSecretKeyId;
+ @Value("${t3t.secretKeyManager.secrets.redisServerIpAddress.keyId}")
+ private String redisIpAddressKeyId;
+ @Value("${t3t.secretKeyManager.secrets.redisServerPort.keyId}")
+ private String redisPortKeyId;
+ @Value("${t3t.secretKeyManager.secrets.redisServerPassword.keyId}")
+ private String redisPasswordKeyId;
+}
diff --git a/src/main/java/com/t3t/authenticationapi/keymanager/service/SecretKeyManagerService.java b/src/main/java/com/t3t/authenticationapi/keymanager/service/SecretKeyManagerService.java
new file mode 100644
index 0000000..c54b0c7
--- /dev/null
+++ b/src/main/java/com/t3t/authenticationapi/keymanager/service/SecretKeyManagerService.java
@@ -0,0 +1,59 @@
+package com.t3t.authenticationapi.keymanager.service;
+
+import com.t3t.authenticationapi.exception.SecretKeyManagerApiRequestFailedException;
+import com.t3t.authenticationapi.keymanager.model.response.SecretKeyManagerResponse;
+import com.t3t.authenticationapi.keymanager.properties.SecretKeyManagerProperties;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.annotation.Profile;
+import org.springframework.core.ParameterizedTypeReference;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpMethod;
+import org.springframework.stereotype.Service;
+import org.springframework.web.client.RestTemplate;
+
+/**
+ * Secret Key Manager 에 등록된 Secret 값을 가져오기 위한 서비스 클래스
+ *
+ * @author woody35545(구건모)
+ */
+@Profile("!local")
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class SecretKeyManagerService {
+ private final RestTemplate sslRestTemplate;
+ private final SecretKeyManagerProperties secretKeyManagerProperties;
+
+ private static final ParameterizedTypeReference secretKeyManagerResponseTypeReference
+ = new ParameterizedTypeReference() {
+ };
+
+ /**
+ * Secret Key Manager 에서 Secret 값 조회
+ *
+ * @param keyId 조회할 Key ID(Secret Key Manager 에 등록된 기밀 데이터의 Key ID)
+ * @return Secret Key Manager 에서 조회한 Secret 값을 String 형태로 반환
+ * @author woody35545(구건모)
+ */
+ public String getSecretValue(String keyId) {
+
+ HttpEntity response =
+ sslRestTemplate.exchange("https://api-keymanager.nhncloudservice.com/keymanager/v1.0/appkey/{appKey}/secrets/{keyId}",
+ HttpMethod.GET, null, SecretKeyManagerResponse.class,
+ secretKeyManagerProperties.getAppKey(), keyId);
+
+ SecretKeyManagerResponse responseBody = response.getBody();
+
+ if (responseBody == null) {
+ throw new SecretKeyManagerApiRequestFailedException("Response body is null.");
+ }
+
+ if (responseBody.getHeader() == null || responseBody.getBody() == null || !responseBody.getHeader().getIsSuccessful().equals("true") || responseBody.getBody().getSecret() == null) {
+ log.error("Secret Key Manager API response: {}", responseBody);
+ throw new SecretKeyManagerApiRequestFailedException(new StringBuilder().append("Fail to request Secret Key Manager API (Key ID:").append(keyId).append(")").toString());
+ }
+
+ return responseBody.getBody().getSecret();
+ }
+}
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index 628f4d4..5beb8db 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -27,4 +27,31 @@ spring:
logging:
level:
- org.hibernate.SQL: debug
\ No newline at end of file
+ org.hibernate.SQL: debug
+
+t3t:
+ secretKeyManager:
+ certKeyPath: ${secretKeyManagerCertKeyPath}
+ certKeyType: ${secretKeyManagerCertKeyType}
+ appKey: ${secretKeyManagerAppKey}
+ password: ${secretKeyManagerPassword}
+
+ secrets:
+ databaseName:
+ keyId: "e3203972cbf04433b90c752f695d5736"
+ databaseServerIpAddress:
+ keyId: "62911d2c30064812b2b2c97a8dd90782"
+ databaseServerPort:
+ keyId: "48e191996aa748938a1edb62652336f4"
+ databaseServerUsername:
+ keyId: "f008c1d3f87f4f88ae57bd03871eb10d"
+ databaseServerPassword:
+ keyId: "8a65684780224384a681c3e9035ca7d6"
+ jwtSecretKey:
+ keyId: "e4f4d4a87ccd49e594f03dffee9fa58d"
+ redisServerIpAddress:
+ keyId: "10ee8b6140cc49ffa9e7a7c8a2924a3e"
+ redisServerPort:
+ keyId: "0582f8b117604b7d86e9f3ff26931cde"
+ redisServerPassword:
+ keyId: "ec1eb8e0706e402cbec8487cbcb86564"
diff --git a/src/test/java/com/t3t/authenticationapi/keymanager/SecretKeyManagerTest.java b/src/test/java/com/t3t/authenticationapi/keymanager/SecretKeyManagerTest.java
new file mode 100644
index 0000000..c982a35
--- /dev/null
+++ b/src/test/java/com/t3t/authenticationapi/keymanager/SecretKeyManagerTest.java
@@ -0,0 +1,169 @@
+package com.t3t.authenticationapi.keymanager;
+
+import com.t3t.authenticationapi.keymanager.properties.SecretKeyProperties;
+import com.t3t.authenticationapi.keymanager.service.SecretKeyManagerService;
+import lombok.extern.slf4j.Slf4j;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.TestPropertySource;
+
+/**
+ * Secret Key Manager API 를 통해 기밀 데이터를 정상적으로 가져오는지에 대한 통합 테스트한다.
+ * 해당 테스트는 가져온 기밀데이터가 실제값과 동일한지 확인하는 테스트가 아니라,
+ * Secret Key Manager API 를 통해 값이 조회되는지에 대한 테스트이다.
+ * Secret Key Manager 빈을 위한 실행 환경 변수를 설정하고, application.yml 파일을 통해 테스트에 사용할 key id 를 정의하여 테스트한다.
+ * @apiNote 확인이 필요한 경우에만 실행하도록 설정하기 위해 테스트를 하고자하는 항목을 제외하고는 @Disabled 어노테이션을 선언하여 테스트를 비활성화한다.
+ * @see SecretKeyManagerService
+ * @see SecretKeyProperties
+ * @author woody35545(구건모)
+ */
+@SpringBootTest
+@TestPropertySource(locations = "classpath:application.yml")
+@Slf4j
+class SecretKeyManagerTest {
+
+ @Autowired
+ private SecretKeyProperties secretKeyProperties;
+
+ @Autowired
+ private SecretKeyManagerService secretKeyManagerService;
+
+
+ /**
+ * Secret Key Manager API를 통해 databaseIpAddress 값이 정상적으로 로드되는지 테스트
+ * @author woody35545(구건모)
+ */
+ @Test
+ @Disabled
+ void databaseIpAddressLoadTest () {
+
+ // when & then
+ Assertions.assertDoesNotThrow(()-> secretKeyManagerService.getSecretValue(secretKeyProperties.getDatabaseIpAddressKeyId()));
+ String value = secretKeyManagerService.getSecretValue(secretKeyProperties.getDatabaseIpAddressKeyId());
+ Assertions.assertNotNull(value);
+
+ log.info("databaseIpAddress => {}", value);
+ }
+
+ /**
+ * Secret Key Manager API를 통해 databasePort 값이 정상적으로 로드되는지 테스트
+ * @author woody35545(구건모)
+ */
+ @Test
+ @Disabled
+ void databasePortLoadTest () {
+
+ // when & then
+ Assertions.assertDoesNotThrow(() -> secretKeyManagerService.getSecretValue(secretKeyProperties.getDatabasePortKeyId()));
+ String value = secretKeyManagerService.getSecretValue(secretKeyProperties.getDatabasePortKeyId());
+ Assertions.assertNotNull(value);
+
+ log.info("databasePort => {}", value);
+ }
+
+ /**
+ * Secret Key Manager API를 통해 databaseName 값이 정상적으로 로드되는지 테스트
+ * @author woody35545(구건모)
+ */
+ @Test
+ @Disabled
+ void databaseNameLoadTest () {
+
+ // when & then
+ Assertions.assertDoesNotThrow(() -> secretKeyManagerService.getSecretValue(secretKeyProperties.getDatabaseNameKeyId()));
+ String value = secretKeyManagerService.getSecretValue(secretKeyProperties.getDatabaseNameKeyId());
+ Assertions.assertNotNull(value);
+
+ log.info("databaseName => {}", value);
+ }
+
+ /**
+ * Secret Key Manager API를 통해 databaseUsername 값이 정상적으로 로드되는지 테스트
+ * @author woody35545(구건모)
+ */
+ @Test
+ @Disabled
+ void databaseUsernameLoadTest () {
+
+ // when & then
+ Assertions.assertDoesNotThrow(() -> secretKeyManagerService.getSecretValue(secretKeyProperties.getDatabaseUsernameKeyId()));
+ String value = secretKeyManagerService.getSecretValue(secretKeyProperties.getDatabaseUsernameKeyId());
+ Assertions.assertNotNull(value);
+
+ log.info("databaseUsername => {}", value);
+ }
+
+
+ /**
+ * Secret Key Manager API를 통해 databasePassword 값이 정상적으로 로드되는지 테스트
+ * @author woody35545(구건모)
+ */
+ @Test
+ @Disabled
+ void databasePasswordLoadTest () {
+ Assertions.assertDoesNotThrow(() -> secretKeyManagerService.getSecretValue(secretKeyProperties.getDatabasePasswordKeyId()));
+ String value = secretKeyManagerService.getSecretValue(secretKeyProperties.getDatabasePasswordKeyId());
+
+ Assertions.assertNotNull(value);
+ }
+
+
+ /**
+ * Secret Key Manager API를 통해 jwtSecretKey 값이 정상적으로 로드되는지 테스트
+ * @author woody35545(구건모)
+ */
+ @Test
+ @Disabled
+ void jwtSecretKeyLoadTest () {
+ Assertions.assertDoesNotThrow(() -> secretKeyManagerService.getSecretValue(secretKeyProperties.getJwtSecretKeyId()));
+ String value = secretKeyManagerService.getSecretValue(secretKeyProperties.getJwtSecretKeyId());
+
+ Assertions.assertNotNull(value);
+ }
+
+ /**
+ * Secret Key Manager API 를 통해 redisIpAddress 값이 정상적으로 로드되는지 테스트
+ * @author woody35545(구건모)
+ */
+ @Test
+ @Disabled
+ void redisIpAddressLoadTest() {
+ Assertions.assertDoesNotThrow(() -> secretKeyManagerService.getSecretValue(secretKeyProperties.getRedisIpAddressKeyId()));
+
+ String value = secretKeyManagerService.getSecretValue(secretKeyProperties.getRedisIpAddressKeyId());
+ Assertions.assertNotNull(value);
+
+ log.info("redisIpAddress => {}", value);
+ }
+
+ /**
+ * Secret Key Manager API 를 통해 redisPort 값이 정상적으로 로드되는지 테스트
+ * @author woody35545(구건모)
+ */
+ @Test
+ @Disabled
+ void redisPortLoadTest() {
+ Assertions.assertDoesNotThrow(() -> secretKeyManagerService.getSecretValue(secretKeyProperties.getRedisPortKeyId()));
+
+ String value = secretKeyManagerService.getSecretValue(secretKeyProperties.getRedisPortKeyId());
+ Assertions.assertNotNull(value);
+
+ log.info("redisPort => {}", value);
+ }
+
+ /**
+ * Secret Key Manager API 를 통해 redisPassword 값이 정상적으로 로드되는지 테스트
+ * @author woody35545(구건모)
+ */
+ @Test
+ @Disabled
+ void redisPasswordLoadTest() {
+ Assertions.assertDoesNotThrow(() -> secretKeyManagerService.getSecretValue(secretKeyProperties.getRedisPasswordKeyId()));
+
+ String value = secretKeyManagerService.getSecretValue(secretKeyProperties.getRedisPasswordKeyId());
+ Assertions.assertNotNull(value);
+ }
+}
diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml
index 5e12484..ff6706f 100644
--- a/src/test/resources/application.yml
+++ b/src/test/resources/application.yml
@@ -31,3 +31,30 @@ eureka:
service-url:
defaultZone : http://127.0.0.1:8761/eureka
+
+t3t:
+ secretKeyManager:
+ certKeyPath: ${secretKeyManagerCertKeyPath}
+ certKeyType: ${secretKeyManagerCertKeyType}
+ appKey: ${secretKeyManagerAppKey}
+ password: ${secretKeyManagerPassword}
+
+ secrets:
+ databaseName:
+ keyId: "e3203972cbf04433b90c752f695d5736"
+ databaseServerIpAddress:
+ keyId: "62911d2c30064812b2b2c97a8dd90782"
+ databaseServerPort:
+ keyId: "48e191996aa748938a1edb62652336f4"
+ databaseServerUsername:
+ keyId: "f008c1d3f87f4f88ae57bd03871eb10d"
+ databaseServerPassword:
+ keyId: "8a65684780224384a681c3e9035ca7d6"
+ jwtSecretKey:
+ keyId: "e4f4d4a87ccd49e594f03dffee9fa58d"
+ redisServerIpAddress:
+ keyId: "10ee8b6140cc49ffa9e7a7c8a2924a3e"
+ redisServerPort:
+ keyId: "0582f8b117604b7d86e9f3ff26931cde"
+ redisServerPassword:
+ keyId: "ec1eb8e0706e402cbec8487cbcb86564"