나이스CI REST API 개발완료

master
minkyu1128 3 years ago
parent a1e4bac731
commit ecdd3f83b4

@ -1,8 +1,8 @@
@echo off @echo off
rem Started... rem Started...
rem java -jar ..\webapp\ci-1.0.0-SNAPSHOT.war rem java -jar ..\webapp\ci-1.0.0.war
rem Started background... rem Started background...
@START /b shutdown.bat @START /b shutdown.bat
@START /b C:\xit\spring-tool-suite-4-4.11.0.RELEASE-e4.20.0-win32.win32.x86_64.self-extracting\env-setting\Java\jdk1.8.0_121\bin\java-app-ci "-Dspring.profiles.active=prod" -jar ..\webapp\ci-1.0.0-SNAPSHOT.war @START /b C:\xit\spring-tool-suite-4-4.11.0.RELEASE-e4.20.0-win32.win32.x86_64.self-extracting\env-setting\Java\jdk1.8.0_121\bin\java-app-ci "-Dspring.profiles.active=prod" -jar ..\webapp\ci-1.0.0.war

@ -6,7 +6,7 @@ plugins {
} }
group = 'cokr.xit' group = 'cokr.xit'
version = '1.0.0-SNAPSHOT' version = '1.0.0'
sourceCompatibility = '1.8' sourceCompatibility = '1.8'
configurations { configurations {
@ -20,17 +20,49 @@ repositories {
} }
dependencies { dependencies {
/* ================================================================================== */
/* Core */
/* ================================================================================== */
implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
implementation 'org.springframework.boot:spring-boot-devtools' implementation 'org.springframework.boot:spring-boot-devtools'
/* ================================================================================== */
/* Jsp */
/* ================================================================================== */
implementation 'org.apache.tomcat.embed:tomcat-embed-jasper' implementation 'org.apache.tomcat.embed:tomcat-embed-jasper'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok' /* ================================================================================== */
/* HttpClient */
/* ================================================================================== */
implementation 'org.apache.httpcomponents:httpcore:4.4.6'
implementation 'org.apache.httpcomponents:httpclient:4.5.11'
implementation 'org.apache.commons:commons-io:1.3.2'
providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
/* ================================================================================== */
/* Springdoc */
/* ================================================================================== */
implementation 'org.springdoc:springdoc-openapi-ui:1.6.3'
testImplementation 'org.springframework.boot:spring-boot-starter-test' /* ================================================================================== */
/* Database */
/* ================================================================================== */
runtimeOnly 'com.h2database:h2:1.4.199'
/* ================================================================================== */
/* Lombok */
/* ================================================================================== */
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
/* =================================================================================== */ /* =================================================================================== */

@ -6,13 +6,14 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.ApplicationPidFileWriter; import org.springframework.boot.context.ApplicationPidFileWriter;
import org.springframework.boot.web.servlet.ServletComponentScan; import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import java.util.Optional; import java.util.Optional;
@Slf4j @Slf4j
@ComponentScan(basePackages = "cokr.xit") @ComponentScan(basePackages = "cokr.xit")
@SpringBootApplication @SpringBootApplication
@ServletComponentScan //@ServletComponentScan
public class AppCiApplication { public class AppCiApplication {
public static void main(String[] args) { public static void main(String[] args) {

@ -31,6 +31,7 @@ public enum ErrCd {
, ERR603("유효하지 않은 토큰(OTT) 값") , ERR603("유효하지 않은 토큰(OTT) 값")
, ERR610("응답 데이터에 필수값이 없음") , ERR610("응답 데이터에 필수값이 없음")
, ERR620("API Response Error") , ERR620("API Response Error")
, ERR699("API 기타 오류")
//기타오류 //기타오류
, ERR999("기타 오류") , ERR999("기타 오류")
, ERR901("권한 없음") , ERR901("권한 없음")

@ -0,0 +1,66 @@
package cokr.xit.ci.api.domain;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import javax.persistence.*;
import java.time.LocalDateTime;
@Entity
@Getter @ToString
@Builder
//@DynamicUpdate //변경된 필드에 대해서만 update SQL문 생성
/*
* @NoArgsConstructor, @AllArgsConstructor
* -.@Builder queryDsl Select ("No default constructor for entity")
*/
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "ens_nice_ci_symkey_mng", schema = "", catalog = "")
@Schema(name = "NiceCiSymkeyMng", description = "나이스 CI 대칭키 관리")
public class NiceCiSymkeyMng {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long niceCiSymkeyMngId; //ID(PK)
@Schema(required = true, title = "공개키(PK)", example = " ", description = "")
@Column(nullable = false, length = 1000)
private String pubkey;
@Schema(required = true, title = "대칭키", example = " ", description = "대칭키등록은 1일 1회만 가능하며, 6개월 내 갱신(재등록) 필요")
@Column(nullable = false, length = 32)
private String symkey;
@Schema(required = false, title = "버전", example = " ", description = "대칭키 현재 버전")
@Column(nullable = true, length = 50)
private String version;
@Schema(required = true, title = "Initial Vector 값", example = " ", description = "CI 송수신 시 사용할 암복호화 key 값")
@Column(nullable = false, length = 16)
private String iv;
@Schema(required = true, title = "hmac key", example = " ", description = "무결성체크 시 사용할 HMAC KEY")
@Column(nullable = false, length = 32)
private String hmacKey;
@Schema(required = true, title = "대체키등록API 응답 Json 데이터", example = " ", description = "DataBodySymkeyResp 객체")
@Column(nullable = false, length = 1000)
private String respJsonData;
@Schema(required = false, title = "등록일시", example = " ", description = "")
@Column(name = "regist_dt", nullable = true)
@CreationTimestamp
private LocalDateTime registDt;
@Schema(required = false, title = "최종 수정일시", example = " ", description = "")
@Column(name = "last_updt_dt", nullable = true)
@UpdateTimestamp
private LocalDateTime lastUpdtDt;
}

@ -0,0 +1,11 @@
package cokr.xit.ci.api.domain.repository;
import cokr.xit.ci.api.domain.NiceCiSymkeyMng;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
public interface NiceCiSymkeyMngRepository extends JpaRepository<NiceCiSymkeyMng, Long> {
Optional<NiceCiSymkeyMng> findByPubkey(String pubkey);
}

@ -22,11 +22,11 @@ public class NiceCiController {
private final NiceCiService diCiService; private final NiceCiService diCiService;
// @Value("${nice.ci.socket.site-code ?: }") // @Value("${contract.nice.ci.socket.site-code ?: }")
@Value("${nice.ci.socket.site-code}") @Value("${contract.nice.ci.socket.site-code}")
private String SITE_CODE; private String SITE_CODE;
// @Value("${nice.ci.socket.site-pw ?: }") // @Value("${contract.nice.ci.socket.site-pw ?: }")
@Value("${nice.ci.socket.site-pw}") @Value("${contract.nice.ci.socket.site-pw}")
private String SITE_PW; private String SITE_PW;
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")

@ -2,9 +2,11 @@ package cokr.xit.ci.api.service;
import cokr.xit.ci.api.code.ErrCd; import cokr.xit.ci.api.code.ErrCd;
import cokr.xit.ci.api.model.ResponseVO; import cokr.xit.ci.api.model.ResponseVO;
import cokr.xit.ci.api.service.support.NiceCiGenerator;
import cokr.xit.ci.api.service.support.socket.Interop; import cokr.xit.ci.api.service.support.socket.Interop;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.io.IOException; import java.io.IOException;
@ -21,6 +23,11 @@ import java.util.stream.Collectors;
@RequiredArgsConstructor @RequiredArgsConstructor
public class NiceCiService { public class NiceCiService {
private final NiceCiGenerator niceCiGenerator;
@Value("${contract.nice.ci.type}")
private String type;
/** /**
* CI . * CI .
* -.CI: (Connecting Information) * -.CI: (Connecting Information)
@ -61,7 +68,11 @@ public class NiceCiService {
/* ======================== /* ========================
* api call * api call
======================== */ ======================== */
responseVO = Interop.getCI(siteCode, sitePw, jid); if("socket".equals(type)) {
responseVO = Interop.getCI(siteCode, sitePw, jid);
}else{
responseVO = niceCiGenerator.getCI(jid, null);
}
} catch (Exception e){ } catch (Exception e){
log.error(e.getMessage()); log.error(e.getMessage());
@ -79,8 +90,8 @@ public class NiceCiService {
.collect(Collectors.toMap(m -> String.valueOf(m.get("key")), m -> m.get("value"), (k1, k2)->k1))) .collect(Collectors.toMap(m -> String.valueOf(m.get("key")), m -> m.get("value"), (k1, k2)->k1)))
.build(); .build();
} }
/** /**
* sha256 * sha256
@ -106,6 +117,6 @@ public class NiceCiService {
return sbuf.toString(); return sbuf.toString();
} }
} }

@ -0,0 +1,130 @@
package cokr.xit.ci.api.service.support;
import cokr.xit.ci.api.code.ErrCd;
import cokr.xit.ci.api.domain.NiceCiSymkeyMng;
import cokr.xit.ci.api.domain.repository.NiceCiSymkeyMngRepository;
import cokr.xit.ci.api.model.ResponseVO;
import cokr.xit.ci.api.service.support.rest.NiceCiApiExecutor;
import cokr.xit.ci.api.service.support.rest.code.NiceCiApiCd;
import cokr.xit.ci.api.service.support.rest.model.NiceCiRespVO;
import cokr.xit.ci.api.service.support.rest.model.conf.DataBodyCiResp;
import cokr.xit.ci.api.service.support.rest.model.conf.DataBodyGenerateTokenResp;
import cokr.xit.ci.api.service.support.rest.model.conf.DataBodyPubkeyResp;
import cokr.xit.ci.api.service.support.rest.model.conf.DataBodySymkeyResp;
import cokr.xit.ci.api.service.support.rest.utils.SymmetricKey;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.Optional;
@Log4j2
@Component
@RequiredArgsConstructor
public class NiceCiGenerator {
private final NiceCiSymkeyMngRepository niceCiSymkeyMngRepository;
@Value("${contract.nice.ci.rest.host}")
private String HOST;
@Value("${contract.nice.ci.rest.client-id}")
private String CLIENT_ID;
@Value("${contract.nice.ci.rest.client-secret}")
private String CLIENT_SECRET;
@Value("${contract.nice.ci.rest.api.generate-token}")
private String API_GENERATE_TOKEN;
@Value("${contract.nice.ci.rest.api.revoke-token}")
private String API_REVOKE_TOKEN;
@Value("${contract.nice.ci.rest.api.publickey}")
private String API_PUBLICKEY;
@Value("${contract.nice.ci.rest.api.symmetrickey}")
private String API_SYMMETRICKEY;
@Value("${contract.nice.ci.rest.api.ci}")
private String API_CI;
public ResponseVO<String> getCI(String jid, String clientIp) {
NiceCiApiExecutor executor = NiceCiApiExecutor.builder()
.HOST(this.HOST)
.CLIENT_ID(this.CLIENT_ID)
.CLIENT_SECRET(this.CLIENT_SECRET)
.API_GENERATE_TOKEN(this.API_GENERATE_TOKEN)
.API_REVOKE_TOKEN(this.API_REVOKE_TOKEN)
.API_PUBLICKEY(this.API_PUBLICKEY)
.API_SYMMETRICKEY(this.API_SYMMETRICKEY)
.API_CI(this.API_CI)
.build();
try {
/* ==========================================================================
* 1.
========================================================================== */
NiceCiRespVO<DataBodyGenerateTokenResp> tokenResponseVO = executor.token();
if (!NiceCiApiCd.OK.equals(tokenResponseVO.getErrCode()))
return ResponseVO.<String>builder().errCode(ErrCd.ERR600).errMsg(tokenResponseVO.getErrCode().getCode() + " " + tokenResponseVO.getErrMsg()).build();
/* ==========================================================================
* 2.
========================================================================== */
NiceCiRespVO<DataBodyPubkeyResp> pubkeyResponseVO = executor.pubkey();
if (!NiceCiApiCd.OK.equals(pubkeyResponseVO.getErrCode()))
return ResponseVO.<String>builder().errCode(ErrCd.ERR600).errMsg(pubkeyResponseVO.getErrCode().getCode() + " " + pubkeyResponseVO.getErrMsg()).build();
/* ==========================================================================
* 3.
* -. 1 1 , 1 2 "0099 기타오류"
* -. DB , .
========================================================================== */
// NiceCiRespVO<DataBodySymkeyResp> symkeyResponseVO = executor.symkey();
// if (!NiceCiApiCd.OK.equals(symkeyResponseVO.getErrCode()))
// return ResponseVO.<String>builder().errCode(ErrCd.ERR600).errMsg(symkeyResponseVO.getErrCode().getCode() + " " + symkeyResponseVO.getErrMsg()).build();
ObjectMapper mapper = new ObjectMapper();
DataBodySymkeyResp dataBodySymkeyResp = null;
if (SymmetricKey.isValidStat()) {
dataBodySymkeyResp = SymmetricKey.getInstance().getData();
} else { //대칭키 상태가 유효하지 않으면...
//현재 대칭키 조회(by 공개키)
Optional<NiceCiSymkeyMng> niceCiSymkeyMng = niceCiSymkeyMngRepository.findByPubkey(pubkeyResponseVO.getResultInfo().getPublicKey());
if (niceCiSymkeyMng.isPresent()) {
dataBodySymkeyResp = mapper.readValue(niceCiSymkeyMng.get().getRespJsonData(), DataBodySymkeyResp.class);
//대칭키 싱글톤 객체 초기화
SymmetricKey.getInstance(dataBodySymkeyResp);
} else {
//3. 대칭키 등록 요청
NiceCiRespVO<DataBodySymkeyResp> symkeyResponseVO = executor.symkey();
if (!NiceCiApiCd.OK.equals(symkeyResponseVO.getErrCode()))
return ResponseVO.<String>builder().errCode(ErrCd.ERR600).errMsg(symkeyResponseVO.getErrCode().getCode() + " " + symkeyResponseVO.getErrMsg()).build();
dataBodySymkeyResp = symkeyResponseVO.getResultInfo();
//대칭키 정보 DB 등록
niceCiSymkeyMngRepository.save(NiceCiSymkeyMng.builder()
.pubkey(pubkeyResponseVO.getResultInfo().getPublicKey())
.symkey(dataBodySymkeyResp.getKey())
.version(dataBodySymkeyResp.getSymkeyStatInfo().getCurSymkeyVersion())
.iv(dataBodySymkeyResp.getIv())
.hmacKey(dataBodySymkeyResp.getHmacKey())
.respJsonData(mapper.writeValueAsString(dataBodySymkeyResp))
.build());
}
}
/* ==========================================================================
* 4. CI
========================================================================== */
NiceCiRespVO<DataBodyCiResp> ciResponseVO = executor.ci(jid, clientIp);
if (!NiceCiApiCd.OK.equals(pubkeyResponseVO.getErrCode()))
return ResponseVO.<String>builder().errCode(ErrCd.ERR600).errMsg(ciResponseVO.getErrCode().getCode() + " " + ciResponseVO.getErrMsg()).build();
return ResponseVO.<String>builder().errCode(ErrCd.OK).errMsg(ErrCd.OK.getCodeNm()).resultInfo(ciResponseVO.getResultInfo().getDecEncData().getCi1()).build();
} catch (Exception e) {
return ResponseVO.<String>builder().errCode(ErrCd.ERR699).errMsg(e.getMessage()).build();
}
}
}

@ -1,57 +0,0 @@
package cokr.xit.ci.api.service.support.rest;
import com.google.gson.*;
import java.lang.reflect.Type;
import java.util.*;
public class MapDeserailizer implements JsonDeserializer<Map<String, Object>> {
@Override @SuppressWarnings("unchecked")
public Map<String, Object> deserialize(JsonElement json, Type typeOfT,
JsonDeserializationContext context) throws JsonParseException {
return (Map<String, Object>) read(json);
}
public Object read(JsonElement in) {
if(in.isJsonArray()){ //JsonArray 이면...
//JsonArray인 경우
List<Object> list = new ArrayList<Object>();
JsonArray arr = in.getAsJsonArray();
for (JsonElement anArr : arr) {
//JsonPrimitive 나올 떄까지 for문
list.add(read(anArr));
}
return list;
}else if(in.isJsonObject()){ //JsonObject 이면...
Map<String, Object> map = new HashMap<String, Object>();
JsonObject obj = in.getAsJsonObject();
Set<Map.Entry<String, JsonElement>> entitySet = obj.entrySet();
for(Map.Entry<String, JsonElement> entry: entitySet){
//JsonPrimitive 나올 떄까지 for문
map.put(entry.getKey(), read(entry.getValue()));
}
return map;
}else if( in.isJsonPrimitive()){ //원시 Json 데이터 이면..
JsonPrimitive prim = in.getAsJsonPrimitive();
if(prim.isBoolean()){
//true , fales 형으로
return prim.getAsBoolean();
}else if(prim.isString()){
//String으로
return prim.getAsString();
}else if(prim.isNumber()){
Number num = prim.getAsNumber();
//Math.ceil 소수점을 올림한다.
if(Math.ceil(num.doubleValue()) == num.longValue())
//소수점 버림, Int형으로.
return num.longValue();
else{
//소수점 안버림, Double 형으로
return num.doubleValue();
}
}
}
return null;
}
}

@ -1,567 +0,0 @@
package cokr.xit.ci.api.service.support.rest;
import cokr.xit.ci.api.code.ErrCd;
import cokr.xit.ci.api.model.ResponseVO;
import cokr.xit.ci.api.service.support.rest.code.NiceCiApiCd;
import cokr.xit.ci.api.service.support.rest.model.*;
import cokr.xit.ci.core.utils.DateUtil;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Base64Util;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.HttpServerErrorException;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
import java.net.URI;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@Component
//public class NiceCiApi implements NiceCiApiStruct {
public class NiceCiApi {
private final String PRODUCT_ID = "2101466024";
@Value("${nice.ci.api.host}")
private String HOST;
@Value("${nice.ci.api.generate-token}")
private String API_GENERATE_TOKEN;
@Value("${nice.ci.api.revoke-token}")
private String API_REVOKE_TOKEN;
@Value("${nice.ci.api.publickey}")
private String API_PUBLICKEY;
@Value("${nice.ci.api.symmetrickey}")
private String API_SYMMETRICKEY;
@Value("${nice.ci.api.ci}")
private String API_CI;
private Gson gson = new GsonBuilder().registerTypeAdapter(Map.class, new MapDeserailizer()).disableHtmlEscaping().create();
//@Override
public ResponseVO<GenerateTokenRespDTO> generateToken(String clientId, String clientSecret) {
ErrCd errCd = ErrCd.ERR999;
String errMsg = ErrCd.ERR999.getCodeNm();
try {
/* ==============================================================================
*
============================================================================== */
if (StringUtils.isEmpty(clientId)) {
errCd = ErrCd.ERR401;
errMsg = "클라이언트ID는 필수 입력값 입니다.";
throw new RuntimeException("유효성 검증 실패.");
}
if (StringUtils.isEmpty(clientSecret)) {
errCd = ErrCd.ERR401;
errMsg = "클라이언트비밀번호는 필수 입력값 입니다.";
throw new RuntimeException("유효성 검증 실패.");
}
/* ==============================================================================
* HEADER
============================================================================== */
String authorizationToken = Base64Util.encode(String.format("%s:%s", clientId, clientSecret));
HttpHeaders headers = new HttpHeaders();
headers.setContentType(new MediaType(MediaType.APPLICATION_JSON, Charset.forName("utf-8")));
headers.set("Authorization", String.format("Basic %s", authorizationToken));
/* ==============================================================================
* URL
============================================================================== */
StringBuilder url = new StringBuilder();
url.append(this.HOST)
.append(API_GENERATE_TOKEN);
Map<String, String> mParam = new HashMap<>();
mParam.put("grant_type", "client_credentials");
mParam.put("scope", "default");
String jsonStr = gson.toJson(mParam);
/* ==============================================================================
* API
============================================================================== */
ResponseEntity<String> resp = this.callApi(HttpMethod.POST, url.toString(), jsonStr, headers);
log.info("==================================================================================");
log.info("==== 토큰 생성 ====");
log.info("[Headers]: " + resp.getHeaders().toString());
log.info("[Body]: " + resp.getBody());
log.info("==================================================================================");
/* ==============================================================================
*
============================================================================== */
if (!"1200".equals(resp.getHeaders().get("GW_RSLT_CD").get(0))) {
errCd = ErrCd.ERR620;
errMsg = String.format("[%s] %s.%s", resp.getHeaders().get("GW_RSLT_CD").get(0), resp.getHeaders().get("GW_RSLT_MSG").get(0), resp.getBody());
throw new RuntimeException("응답 오류(GW_RSLT_CD) 입니다.");
}
// Map<String, Object> resultInfo = gson.fromJson(resp.getBody(), Map.class);
// String accessToken = (String) resultInfo.get("access_token"); //사용자 엑세스 토큰 값(모든 API 요청시 헤더에 access_token을 포함하여 전송)
// Long expiresIn = (Long) resultInfo.get("expires_in"); //access token 만료까지 남은시간(초)
// String tokenType = (String) resultInfo.get("token_type"); //bearer로 고정
// String scope = (String) resultInfo.get("scope"); //요청한 scope값(기본 default)
// String expiredDt = DateUtil.absTimeSecToDate(expiresIn, "yyyyMMddHHmmss");
// log.info("[엑세스토큰]: " + accessToken);
// log.info("[엑세스토큰 만료시간]: " + expiredDt);
GenerateTokenRespDTO resultInfo = gson.fromJson(resp.getBody(), GenerateTokenRespDTO.class);
resultInfo.setExpiredDt(DateUtil.absTimeSecToDate(resultInfo.getExpiresIn(), "yyyyMMddHHmmss"));
log.info("[응답데이터]: " + resultInfo.toString());
return ResponseVO.<GenerateTokenRespDTO>builder().errCode(ErrCd.OK).errMsg(ErrCd.OK.getCodeNm()).resultInfo(resultInfo).build();
} catch (Exception e) {
return ResponseVO.<GenerateTokenRespDTO>builder().errCode(errCd).errMsg(errMsg + " " + errMsg).build();
}
}
//@Override
public ResponseVO<RevokeTokenRespDTO> revokeToken(String accessToken, String clientId) {
ErrCd errCd = ErrCd.ERR999;
String errMsg = ErrCd.ERR999.getCodeNm();
try {
/* ==============================================================================
*
============================================================================== */
if (StringUtils.isEmpty(accessToken)) {
errCd = ErrCd.ERR401;
errMsg = "엑세스토큰은 필수 입력값 입니다.";
throw new RuntimeException("유효성 검증 실패.");
}
if (StringUtils.isEmpty(clientId)) {
errCd = ErrCd.ERR401;
errMsg = "클라이언트ID는 필수 입력값 입니다.";
throw new RuntimeException("유효성 검증 실패.");
}
/* ==============================================================================
* HEADER
============================================================================== */
String bearerToken = Base64Util.encode(String.format("%s:%s:%s", accessToken, (new Date().getTime() / 1000), clientId));
HttpHeaders headers = new HttpHeaders();
headers.setContentType(new MediaType(MediaType.APPLICATION_JSON, Charset.forName("utf-8")));
headers.set("Authorization", String.format("bearer %s", bearerToken));
/* ==============================================================================
* URL
============================================================================== */
StringBuilder url = new StringBuilder();
url.append(this.HOST)
.append(API_REVOKE_TOKEN);
/* ==============================================================================
* API
============================================================================== */
ResponseEntity<String> resp = this.callApi(HttpMethod.POST, url.toString(), null, headers);
log.info("==================================================================================");
log.info("==== 토큰 폐기 ====");
log.info("[Headers]: " + resp.getHeaders().toString());
log.info("[Body]: " + resp.getBody());
log.info("==================================================================================");
/* ==============================================================================
*
============================================================================== */
if (!"1200".equals(resp.getHeaders().get("GW_RSLT_CD").get(0))) {
errCd = ErrCd.ERR620;
errMsg = String.format("[%s] %s.%s", resp.getHeaders().get("GW_RSLT_CD").get(0), resp.getHeaders().get("GW_RSLT_MSG").get(0), resp.getBody());
throw new RuntimeException("응답 오류 입니다. ");
}
RevokeTokenRespDTO resultInfo = gson.fromJson(resp.getBody(), RevokeTokenRespDTO.class);
log.info("[응답데이터]: " + resultInfo.toString());
return ResponseVO.<RevokeTokenRespDTO>builder().errCode(ErrCd.OK).errMsg(ErrCd.OK.getCodeNm()).resultInfo(resultInfo).build();
} catch (Exception e) {
return ResponseVO.<RevokeTokenRespDTO>builder().errCode(errCd).errMsg(e.getMessage() + " " + errMsg).build();
}
}
//@Override
public ResponseVO<PublickeyRespDTO> generatePublickey(String accessToken, String clientId, String pubkeyVersion, String symkeyRegInfo) {
ErrCd errCd = ErrCd.ERR999;
String errMsg = ErrCd.ERR999.getCodeNm();
try {
/* ==============================================================================
*
============================================================================== */
if (StringUtils.isEmpty(accessToken)) {
errCd = ErrCd.ERR401;
errMsg = "엑세스토큰은 필수 입력값 입니다.";
throw new RuntimeException("유효성 검증 실패.");
}
if (StringUtils.isEmpty(clientId)) {
errCd = ErrCd.ERR401;
errMsg = "클라이언트ID는 필수 입력값 입니다.";
throw new RuntimeException("유효성 검증 실패.");
}
if (StringUtils.isEmpty(pubkeyVersion)) {
errCd = ErrCd.ERR401;
errMsg = "공개키버전은 필수 입력값 입니다.";
throw new RuntimeException("유효성 검증 실패.");
}
if (StringUtils.isEmpty(symkeyRegInfo)) {
errCd = ErrCd.ERR401;
errMsg = "공개키암호화 값(대칭키를 공개키로 암호화)은 필수 입력값 입니다.";
throw new RuntimeException("유효성 검증 실패.");
}
/* ==============================================================================
* HEADER
============================================================================== */
String bearerToken = Base64Util.encode(String.format("%s:%s:%s", accessToken, (new Date().getTime() / 1000), clientId));
HttpHeaders headers = new HttpHeaders();
headers.setContentType(new MediaType(MediaType.APPLICATION_JSON, Charset.forName("utf-8")));
headers.set("Authorization", String.format("bearer %s", bearerToken));
headers.set("client_id", clientId);
headers.set("productID", PRODUCT_ID);
headers.set("CNTY_CD", "ko"); //이용언어: ko, en, cn...
// headers.set("TRAN_ID", ); //API통신구간에서 요청에 대한 응답을 확인하기 위한 고유번호
/* ==============================================================================
* URL
============================================================================== */
StringBuilder url = new StringBuilder();
url.append(this.HOST)
.append(API_PUBLICKEY);
Map<String, String> mParam = new HashMap<>();
mParam.put("req_dtim", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")));
String jsonStr = gson.toJson(mParam);
/* ==============================================================================
* API
============================================================================== */
ResponseEntity<String> resp = this.callApi(HttpMethod.POST, url.toString(), jsonStr, headers);
log.info("==================================================================================");
log.info("==== 공개키 요청 ====");
log.info("[Headers]: " + resp.getHeaders().toString());
log.info("[Body]: " + resp.getBody());
log.info("==================================================================================");
/* ==============================================================================
*
============================================================================== */
if (!"1200".equals(resp.getHeaders().get("GW_RSLT_CD").get(0))) {
errCd = ErrCd.ERR620;
errMsg = String.format("[%s] %s.%s", resp.getHeaders().get("GW_RSLT_CD").get(0), resp.getHeaders().get("GW_RSLT_MSG").get(0), resp.getBody());
throw new RuntimeException("응답 오류(GW_RSLT_CD) 입니다.");
}
PublickeyRespDTO resultInfo = gson.fromJson(resp.getBody(), PublickeyRespDTO.class);
log.info("[응답데이터]: " + resultInfo.toString());
if (!"P000".equals(resultInfo.getRspCd())) {
errCd = ErrCd.ERR601;
errMsg = String.format("[%s]%s", resultInfo.getRspCd(), NiceCiApiCd.valueOfEnum(resultInfo.getRspCd()).getCodeNm());
throw new RuntimeException("응답코드(rsp_cd) 오류.");
}
if (!"0000".equals(resultInfo.getResultCd())) {
errCd = ErrCd.ERR601;
errMsg = String.format("[%s]%s", resultInfo.getResultCd(), NiceCiApiCd.valueOfEnum("DRSLT_" + resultInfo.getResultCd()).getCodeNm());
throw new RuntimeException("상세결과코드(result_cd) 오류.");
}
return ResponseVO.<PublickeyRespDTO>builder().errCode(ErrCd.OK).errMsg(ErrCd.OK.getCodeNm()).resultInfo(resultInfo).build();
} catch (Exception e) {
return ResponseVO.<PublickeyRespDTO>builder().errCode(errCd).errMsg(e.getMessage() + " " + errMsg).build();
}
}
//@Override
public ResponseVO<SymmetrickeyRespDTO> generateSymmetrickey(String accessToken, String clientId, String pubkeyVersion, String symkeyRegInfo) {
ErrCd errCd = ErrCd.ERR999;
String errMsg = ErrCd.ERR999.getCodeNm();
try {
/* ==============================================================================
*
============================================================================== */
if (StringUtils.isEmpty(accessToken)) {
errCd = ErrCd.ERR401;
errMsg = "엑세스토큰은 필수 입력값 입니다.";
throw new RuntimeException("유효성 검증 실패.");
}
if (StringUtils.isEmpty(clientId)) {
errCd = ErrCd.ERR401;
errMsg = "클라이언트ID는 필수 입력값 입니다.";
throw new RuntimeException("유효성 검증 실패.");
}
if (StringUtils.isEmpty(pubkeyVersion)) {
errCd = ErrCd.ERR401;
errMsg = "공개키버전은 필수 입력값 입니다.";
throw new RuntimeException("유효성 검증 실패.");
}
if (StringUtils.isEmpty(symkeyRegInfo)) {
errCd = ErrCd.ERR401;
errMsg = "JSON암호화값은 필수 입력값 입니다.";
throw new RuntimeException("유효성 검증 실패.");
}
/* ==============================================================================
* HEADER
============================================================================== */
String bearerToken = Base64Util.encode(String.format("%s:%s:%s", accessToken, (new Date().getTime() / 1000), clientId));
HttpHeaders headers = new HttpHeaders();
headers.setContentType(new MediaType(MediaType.APPLICATION_JSON, Charset.forName("utf-8")));
headers.set("Authorization", String.format("bearer %s", bearerToken));
headers.set("client_id", clientId);
headers.set("productID", PRODUCT_ID);
headers.set("CNTY_CD", "ko"); //이용언어: ko, en, cn...
// headers.set("TRAN_ID", ); //API통신구간에서 요청에 대한 응답을 확인하기 위한 고유번호
/* ==============================================================================
* URL
============================================================================== */
StringBuilder url = new StringBuilder();
url.append(this.HOST)
.append(API_PUBLICKEY);
Map<String, String> mParam = new HashMap<>();
mParam.put("pubkey_version", pubkeyVersion);
mParam.put("symkey_reg_info", symkeyRegInfo);
String jsonStr = gson.toJson(mParam);
/* ==============================================================================
* API
============================================================================== */
ResponseEntity<String> resp = this.callApi(HttpMethod.POST, url.toString(), jsonStr, headers);
log.info("==================================================================================");
log.info("==== 대칭키 요청 ====");
log.info("[Headers]: " + resp.getHeaders().toString());
log.info("[Body]: " + resp.getBody());
log.info("==================================================================================");
/* ==============================================================================
*
============================================================================== */
if (!"1200".equals(resp.getHeaders().get("GW_RSLT_CD").get(0))) {
errCd = ErrCd.ERR620;
errMsg = String.format("[%s] %s.%s", resp.getHeaders().get("GW_RSLT_CD").get(0), resp.getHeaders().get("GW_RSLT_MSG").get(0), resp.getBody());
throw new RuntimeException("API 응답 오류 입니다.");
}
SymmetrickeyRespDTO resultInfo = gson.fromJson(resp.getBody(), SymmetrickeyRespDTO.class);
log.info("[응답데이터]: " + resultInfo.toString());
if (!"P000".equals(resultInfo.getRspCd())) {
errCd = ErrCd.ERR601;
errMsg = String.format("[%s]%s", resultInfo.getRspCd(), NiceCiApiCd.valueOfEnum(resultInfo.getRspCd()).getCodeNm());
throw new RuntimeException("응답코드(rsp_cd) 오류.");
}
if (!"0000".equals(resultInfo.getResultCd())) {
errCd = ErrCd.ERR601;
errMsg = String.format("[%s]%s", resultInfo.getResultCd(), NiceCiApiCd.valueOfEnum("DRSLT_" + resultInfo.getResultCd()).getCodeNm());
throw new RuntimeException("상세결과코드(result_cd) 오류.");
}
return ResponseVO.<SymmetrickeyRespDTO>builder().errCode(ErrCd.OK).errMsg(ErrCd.OK.getCodeNm()).resultInfo(resultInfo).build();
} catch (Exception e) {
return ResponseVO.<SymmetrickeyRespDTO>builder().errCode(errCd).errMsg(e.getMessage() + " " + errMsg).build();
}
}
//@Override
public ResponseVO<CiRespDTO> getCi(String clientId, String clientSecret, String symkeyVersion, String encData, String integrityValue) {
ErrCd errCd = ErrCd.ERR999;
String errMsg = ErrCd.ERR999.getCodeNm();
try {
/* ==============================================================================
*
============================================================================== */
if (StringUtils.isEmpty(clientId)) {
errCd = ErrCd.ERR401;
errMsg = "클라이언트ID는 필수 입력값 입니다.";
throw new RuntimeException("유효성 검증 실패.");
}
if (StringUtils.isEmpty(clientSecret)) {
errCd = ErrCd.ERR401;
errMsg = "클라이언트비밀번호는 필수 입력값 입니다.";
throw new RuntimeException("유효성 검증 실패.");
}
if (StringUtils.isEmpty(symkeyVersion)) {
errCd = ErrCd.ERR401;
errMsg = "대칭키 버전 필수 입력값 입니다.";
throw new RuntimeException("유효성 검증 실패.");
}
if (StringUtils.isEmpty(encData)) {
errCd = ErrCd.ERR401;
errMsg = "JSON암호화 값은 필수 입력값 입니다.";
throw new RuntimeException("유효성 검증 실패.");
}
if (StringUtils.isEmpty(integrityValue)) {
errCd = ErrCd.ERR401;
errMsg = "무결성 체크 값은 필수 입력값 입니다.";
throw new RuntimeException("유효성 검증 실패.");
}
/* ==============================================================================
* HEADER
============================================================================== */
// String authorizationToken = Base64.getEncoder().encodeToString(String.format("%s:%s", clientId, clientSecret).getBytes(StandardCharsets.UTF_8));
String authorizationToken = Base64Util.encode(String.format("%s:%s", clientId, clientSecret));
HttpHeaders headers = new HttpHeaders();
headers.setContentType(new MediaType(MediaType.APPLICATION_JSON, Charset.forName("utf-8")));
headers.set("Authorization", String.format("Basic %s", authorizationToken));
headers.set("productID", PRODUCT_ID);
headers.set("CNTY_CD", "ko"); //이용언어: ko, en, cn...
// headers.set("TRAN_ID", ); //API통신구간에서 요청에 대한 응답을 확인하기 위한 고유번호
/* ==============================================================================
* URL
============================================================================== */
StringBuilder url = new StringBuilder();
url.append(this.HOST)
.append(API_CI);
Map<String, String> mParam = new HashMap<>();
mParam.put("symkey_version", symkeyVersion);
mParam.put("enc_data", encData);
mParam.put("integrity_value", integrityValue);
String jsonStr = gson.toJson(mParam);
/* ==============================================================================
* API
============================================================================== */
ResponseEntity<String> resp = this.callApi(HttpMethod.POST, url.toString(), jsonStr, headers);
log.info("==================================================================================");
log.info("==== CI조회 ====");
log.info("[Headers]: " + resp.getHeaders().toString());
log.info("[Body]: " + resp.getBody());
log.info("==================================================================================");
/* ==============================================================================
*
============================================================================== */
if (!"1200".equals(resp.getHeaders().get("GW_RSLT_CD").get(0))) {
errCd = ErrCd.ERR620;
errMsg = String.format("[%s] %s.%s", resp.getHeaders().get("GW_RSLT_CD").get(0), resp.getHeaders().get("GW_RSLT_MSG").get(0), resp.getBody());
throw new RuntimeException("응답 오류(GW_RSLT_CD) 입니다.");
}
CiRespDTO resultInfo = gson.fromJson(resp.getBody(), CiRespDTO.class);
log.info("[응답데이터]: " + resultInfo.toString());
return ResponseVO.<CiRespDTO>builder().errCode(ErrCd.OK).errMsg(ErrCd.OK.getCodeNm()).resultInfo(resultInfo).build();
} catch (Exception e) {
e.printStackTrace();
return ResponseVO.<CiRespDTO>builder().errCode(errCd).errMsg(e.getMessage() + " " + errMsg).build();
}
}
/**
* <pre> : API
* </pre>
*
* @param method
* @param url
* @param body
* @param headers
* @return ResponseEntity
* @author:
*/
private ResponseEntity<String> callApi(HttpMethod method, String url, String body, HttpHeaders headers) {
log.debug("param =======================");
log.debug(body);
ResponseEntity<String> responseEntity = null;
try {
//uri 및 entity(param) 설정
HttpEntity<?> entity = null;
UriComponents uri = null;
switch (method) {
case GET:
entity = new HttpEntity<>(headers);
uri = UriComponentsBuilder
.fromHttpUrl(String.format("%s?%s", url, body == null ? "" : body))
// .encode(StandardCharsets.UTF_8) //"%"기호가 "%25"로 인코딩 발생하여 주석처리 함.
.build(false);
break;
case POST:
entity = new HttpEntity<>(body, headers);
uri = UriComponentsBuilder
.fromHttpUrl(url)
.encode(StandardCharsets.UTF_8)
.build();
break;
default:
break;
}
//api 호출
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
factory.setConnectTimeout(3000); //커넥션타임아웃 설정 3초
factory.setReadTimeout(3000);//타임아웃 설정 3초
RestTemplate restTemplate = new RestTemplate(factory);
System.out.println(" url => " + uri.toString());
System.out.println(" method => " + method);
System.out.println(" headers => " + entity.getHeaders().toString());
System.out.println(" body => " + entity.getBody());
responseEntity = restTemplate.exchange(URI.create(uri.toString()), method, entity, String.class); //이 한줄의 코드로 API를 호출해 String타입으로 전달 받는다.
/*
* HttpStatus
* -.: responseEntity.getStatusCodeValue()
* -.: responseEntity.getStatusCode()
*/
} catch (HttpServerErrorException e) {
responseEntity = new ResponseEntity<String>(e.getResponseBodyAsString(), e.getStatusCode());
log.error(String.format("call API 서버오류[url =>%s param => %s error => %s]", url, body, e.getMessage()));
} catch (HttpClientErrorException e) {
responseEntity = new ResponseEntity<String>(e.getResponseBodyAsString(), e.getStatusCode());
log.error(String.format("call API 클라이언트오류[url =>%s param => %s error => %s]", url, body, e.getMessage()));
} catch (RestClientException e) { //timeout 발생 또는 기타 오류...
responseEntity = new ResponseEntity<String>(e.getMessage(), HttpStatus.REQUEST_TIMEOUT);
log.error(String.format("RestAPI 호출 오류[url =>%s param => %s error => %s]", url, body, e.getMessage()));
} catch (Exception e) {
responseEntity = new ResponseEntity<String>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
log.error(String.format("call API 기타오류[url =>%s param => %s error => %s]", url, body, e.getMessage()));
}
//결과 응답
return responseEntity;
}
}

@ -0,0 +1,115 @@
package cokr.xit.ci.api.service.support.rest;
import cokr.xit.ci.api.service.support.rest.api.CiRequest;
import cokr.xit.ci.api.service.support.rest.api.PubkeyRequest;
import cokr.xit.ci.api.service.support.rest.api.SymkeyRegist;
import cokr.xit.ci.api.service.support.rest.api.TokenGenerate;
import cokr.xit.ci.api.service.support.rest.code.NiceCiApiCd;
import cokr.xit.ci.api.service.support.rest.model.NiceCiRespVO;
import cokr.xit.ci.api.service.support.rest.model.conf.DataBodyCiResp;
import cokr.xit.ci.api.service.support.rest.model.conf.DataBodyGenerateTokenResp;
import cokr.xit.ci.api.service.support.rest.model.conf.DataBodyPubkeyResp;
import cokr.xit.ci.api.service.support.rest.model.conf.DataBodySymkeyResp;
import cokr.xit.ci.api.service.support.rest.utils.PublicKey;
import cokr.xit.ci.api.service.support.rest.utils.SymmetricKey;
import cokr.xit.ci.api.service.support.rest.utils.Token;
import lombok.Builder;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@RequiredArgsConstructor
@Builder
public class NiceCiApiExecutor {
private final String HOST;
private final String CLIENT_ID;
private final String CLIENT_SECRET;
private final String API_GENERATE_TOKEN;
private final String API_REVOKE_TOKEN;
private final String API_PUBLICKEY;
private final String API_SYMMETRICKEY;
private final String API_CI;
public NiceCiRespVO<DataBodyGenerateTokenResp> token() throws Exception {
NiceCiRespVO<DataBodyGenerateTokenResp> result = null;
if (Token.isValidStat()) {
result = NiceCiRespVO.<DataBodyGenerateTokenResp>okBuilder()
.resultInfo(Token.getInstance().getData())
.build();
} else {
result = TokenGenerate.builder()
.HOST(this.HOST)
.API_URL_GENERATE_TOKEN(this.API_GENERATE_TOKEN)
.API_URL_REVOKE_TOKEN(this.API_REVOKE_TOKEN)
.build()
.execute(CLIENT_ID, CLIENT_SECRET);
// 토큰정보 싱글톤 초기화
if (NiceCiApiCd.OK.equals(result.getErrCode()))
Token.getInstance(result.getResultInfo());
}
return result;
}
public NiceCiRespVO<DataBodyPubkeyResp> pubkey() throws Exception {
NiceCiRespVO<DataBodyPubkeyResp> result = null;
if (PublicKey.isValidStat()) {
result = NiceCiRespVO.<DataBodyPubkeyResp>okBuilder()
.resultInfo(PublicKey.getInstance().getData())
.build();
} else {
result = PubkeyRequest.builder()
.HOST(this.HOST)
.API_URL(this.API_PUBLICKEY)
.build()
.execute(this.CLIENT_ID);
// 공개키 싱글톤 초기화
if (NiceCiApiCd.OK.equals(result.getErrCode()))
PublicKey.getInstance(result.getResultInfo());
}
return result;
}
public NiceCiRespVO<DataBodySymkeyResp> symkey() throws Exception {
NiceCiRespVO<DataBodySymkeyResp> result = null;
if(SymmetricKey.isValidStat()){
result = NiceCiRespVO.<DataBodySymkeyResp>okBuilder()
.resultInfo(SymmetricKey.getInstance().getData())
.build();
}else{
result = SymkeyRegist.builder()
.HOST(this.HOST)
.API_URL(this.API_SYMMETRICKEY)
.build()
.execute(this.CLIENT_ID);
// 대칭키 싱글톤 초기화
if(NiceCiApiCd.OK.equals(result.getErrCode()))
SymmetricKey.getInstance(result.getResultInfo());
}
return result;
}
public NiceCiRespVO<DataBodyCiResp> ci(String jid, String clientIp) throws Exception {
NiceCiRespVO<DataBodyCiResp> result = CiRequest.builder()
.HOST(this.HOST)
.API_URL(this.API_CI)
.build()
.execute(this.CLIENT_ID, this.CLIENT_SECRET, jid, clientIp);
return result;
}
}

@ -1,35 +0,0 @@
package cokr.xit.ci.api.service.support.rest;
import cokr.xit.ci.api.model.ResponseVO;
public interface NiceCiApiStruct {
/**
* 1. API
*/
ResponseVO generateToken();
/**
* 2. API
*/
ResponseVO revokeToken();
/**
* API
* -.
*/
ResponseVO generatePublickey();
/**
* API
* -.
*/
ResponseVO generateSymmetrickey();
/**
* CI
*/
ResponseVO getCi();
}

@ -0,0 +1,273 @@
package cokr.xit.ci.api.service.support.rest.api;
import cokr.xit.ci.api.service.support.rest.code.NiceCiApiCd;
import cokr.xit.ci.api.service.support.rest.model.NiceCiRespDTO;
import cokr.xit.ci.api.service.support.rest.model.NiceCiRespVO;
import cokr.xit.ci.api.service.support.rest.model.conf.DataBodyCiResp;
import cokr.xit.ci.api.service.support.rest.model.conf.DataBodySymkeyResp;
import cokr.xit.ci.api.service.support.rest.model.conf.EncData;
import cokr.xit.ci.api.service.support.rest.utils.SymmetricKey;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import lombok.experimental.SuperBuilder;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Base64Util;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.Base64Utils;
import org.springframework.util.StringUtils;
import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.Charset;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@SuperBuilder
public class CiRequest extends NiceCiApiAbstract {
protected final String HOST;
protected final String API_URL;
public NiceCiRespVO<DataBodyCiResp> execute(String clientId, String clientSecret, String jumin, String clientIp) throws Exception {
/* ==============================================================================
*
============================================================================== */
if (StringUtils.isEmpty(clientId))
return NiceCiRespVO.<DataBodyCiResp>errBuilder().errCode(NiceCiApiCd.FAIL).errMsg("클라이언트ID는 필수 입력값 입니다.").build();
if (StringUtils.isEmpty(clientSecret))
return NiceCiRespVO.<DataBodyCiResp>errBuilder().errCode(NiceCiApiCd.FAIL).errMsg("클라이언트비밀번호는 필수 입력값 입니다.").build();
if (StringUtils.isEmpty(SymmetricKey.getInstance().getData().getKey()))
return NiceCiRespVO.<DataBodyCiResp>errBuilder().errCode(NiceCiApiCd.FAIL).errMsg("대칭키는 필수 입력값 입니다.").build();
if (StringUtils.isEmpty(SymmetricKey.getInstance().getData().getSymkeyStatInfo().getCurSymkeyVersion()))
return NiceCiRespVO.<DataBodyCiResp>errBuilder().errCode(NiceCiApiCd.FAIL).errMsg("대칭키 버전은 필수 입력값 입니다.").build();
if (StringUtils.isEmpty(SymmetricKey.getInstance().getData().getSiteCode()))
return NiceCiRespVO.<DataBodyCiResp>errBuilder().errCode(NiceCiApiCd.FAIL).errMsg("사이트코드는 필수 입력값 입니다.").build();
if (StringUtils.isEmpty(SymmetricKey.getInstance().getData().getRequestNo()))
return NiceCiRespVO.<DataBodyCiResp>errBuilder().errCode(NiceCiApiCd.FAIL).errMsg("신청번호는 필수 입력값 입니다.").build();
if (StringUtils.isEmpty(SymmetricKey.getInstance().getData().getIv()))
return NiceCiRespVO.<DataBodyCiResp>errBuilder().errCode(NiceCiApiCd.FAIL).errMsg("IV는 필수 입력값 입니다.").build();
if (StringUtils.isEmpty(SymmetricKey.getInstance().getData().getHmacKey()))
return NiceCiRespVO.<DataBodyCiResp>errBuilder().errCode(NiceCiApiCd.FAIL).errMsg("HmacKey는 필수 입력값 입니다.").build();
if (StringUtils.isEmpty(jumin))
return NiceCiRespVO.<DataBodyCiResp>errBuilder().errCode(NiceCiApiCd.FAIL).errMsg("주민번호는 필수 입력값 입니다.").build();
/* ==============================================================================
* API
============================================================================== */
NiceCiRespDTO<DataBodyCiResp> responseDTO = this.getCi(clientId, clientSecret, SymmetricKey.getInstance().getData(), jumin, clientIp);
/* ==============================================================================
*
============================================================================== */
if (!"1200".equals(responseDTO.getDataHeader().getGW_RSLT_CD()))
return NiceCiRespVO.<DataBodyCiResp>errBuilder()
.errCode(NiceCiApiCd.valueOfEnum("HEAD_" + responseDTO.getDataHeader().getGW_RSLT_CD()))
.errMsg(String.format("[%s]: %s", responseDTO.getDataHeader().getGW_RSLT_CD(), responseDTO.getDataHeader().getGW_RSLT_MSG()))
.build();
if (!"P000".equals(responseDTO.getDataBody().getRspCd()))
return NiceCiRespVO.<DataBodyCiResp>errBuilder()
.errCode(NiceCiApiCd.valueOfEnum(responseDTO.getDataBody().getRspCd()))
.errMsg(String.format("[%s]: %s. %s", responseDTO.getDataBody().getRspCd(), NiceCiApiCd.valueOfEnum(responseDTO.getDataBody().getRspCd()).getCodeNm(), responseDTO.getDataBody().getResMsg()))
.build();
if (!"0000".equals(responseDTO.getDataBody().getResultCd()))
return NiceCiRespVO.<DataBodyCiResp>errBuilder()
.errCode(NiceCiApiCd.valueOfEnum("CI_" + responseDTO.getDataBody().getResultCd()))
.errMsg(String.format("[%s]: %s", responseDTO.getDataBody().getResultCd(), NiceCiApiCd.valueOfEnum("CI_" + responseDTO.getDataBody().getResultCd()).getCodeNm()))
.build();
/* ==============================================================================
*
============================================================================== */
//enc_data와 intergrity_value 비교
if (!responseDTO.getDataBody().getIntegrityValue().equals(createIntegrityValue(responseDTO.getDataBody().getEncData(), SymmetricKey.getInstance().getData().getHmacKey())))
return NiceCiRespVO.<DataBodyCiResp>errBuilder()
.errCode(NiceCiApiCd.FAIL)
.errMsg("응답데이터 무결성 체크 오류")
.build();
/* ==============================================================================
*
============================================================================== */
String decEncDataStr = this.decEncData(responseDTO.getDataBody().getEncData(), SymmetricKey.getInstance().getData().getKey(), SymmetricKey.getInstance().getData().getIv());
responseDTO.getDataBody().setDecEncData(mapper.readValue(decEncDataStr, EncData.class));
return NiceCiRespVO.<DataBodyCiResp>okBuilder().resultInfo(responseDTO.getDataBody()).build();
}
protected NiceCiRespDTO<DataBodyCiResp> getCi(String clientId, String clientSecret, DataBodySymkeyResp symkeyResp, String jumin, String clientIp) throws Exception {
try {
/* ==============================================================================
* HEADER
============================================================================== */
// String authorizationToken = Base64.getEncoder().encodeToString(String.format("%s:%s", clientId, clientSecret).getBytes(StandardCharsets.UTF_8));
String authorizationToken = Base64Util.encode(String.format("%s:%s", clientId, clientSecret));
HttpHeaders headers = new HttpHeaders();
headers.setContentType(new MediaType(MediaType.APPLICATION_JSON, Charset.forName("utf-8")));
headers.set("Authorization", String.format("Basic %s", authorizationToken));
headers.set("productID", PRODUCT_ID);
/* ==============================================================================
* URL
============================================================================== */
StringBuilder url = new StringBuilder();
url.append(this.HOST)
.append(this.API_URL);
final String strEncData = this.createEncData(symkeyResp.getSiteCode(), jumin, symkeyResp.getRequestNo(), LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")), clientIp);
final String encData = this.encEncData(strEncData, symkeyResp.getKey(), symkeyResp.getIv());
final String integrityValue = this.createIntegrityValue(encData, symkeyResp.getHmacKey());
final String jsonStr = this.createMessage(symkeyResp.getSymkeyStatInfo().getCurSymkeyVersion(), encData, integrityValue);
/* ==============================================================================
* API
============================================================================== */
ResponseEntity<String> resp = this.callApi(HttpMethod.POST, url.toString(), jsonStr, headers);
log.info("==================================================================================");
log.info("==== 아이핀 CI 요청 Info... ====");
log.info("[Body]: " + jsonStr);
log.info("[encData]: " + strEncData);
log.info("==== 아이핀 CI 요청 Result Info... ====");
log.info("[Headers]: " + resp.getHeaders().toString());
log.info("[Body]: " + resp.getBody());
log.info("==================================================================================");
/* ==============================================================================
*
============================================================================== */
String strRespBody = resp.getBody().replace("\"\"", "null");
NiceCiRespDTO<DataBodyCiResp> respDTO = mapper.readValue(strRespBody, new TypeReference<NiceCiRespDTO<DataBodyCiResp>>(){});
return respDTO;
} catch (Exception e) {
e.printStackTrace();
throw new Exception("아이핀 CI 요청 실패." + e.getMessage());
}
}
/**
* JSON
*
* @param symkeyVersion
* @param encData
* @param integrityValue
* @return
* @throws JsonProcessingException
*/
protected String createMessage(String symkeyVersion, String encData, String integrityValue) throws JsonProcessingException {
Map<String, String> dataHeader = new HashMap<>();
dataHeader.put("CNTY_CD", "ko"); //[필수]이용언어: ko, en, cn ...
dataHeader.put("TRAN_ID", null); //[선택]API 통신구간에서 요청에 대한 응답을 확인하기 위한 고유번호
Map<String, String> dataBody = new HashMap<>();
dataBody.put("symkey_version", symkeyVersion); //[선택]대칭키 버전
dataBody.put("enc_data", encData); //[필수]JSON암호화 값
dataBody.put("integrity_value", integrityValue); //[선택]무결성체크를 위해 enc_data를 HMAC처리 후, Base64 인코딩한 값
Map<String, Object> m = new HashMap<>();
m.put("dataHeader", dataHeader);
m.put("dataBody", dataBody);
return mapper.writeValueAsString(m);
}
private String createEncData(String siteCode, String jid, String reqNo, String reqDtim, String clientIp) throws JsonProcessingException {
Map<String, String> m = new HashMap<>();
m.put("site_code", siteCode); //[필수]사이트코드(공개키요청시 수신한 사이트코드)
m.put("info_req_type", "1"); //[필수]정보요청유형(1: CI제공)
m.put("jumin_id", jid.replaceAll("[^0-9]", "")); //[필수]주민등록번호(13자리)
m.put("req_no", reqNo); //[필수]이용기관에서 서비스에 대한 요청거래를 확인하기 위한 고유값
m.put("req_dtim", reqDtim); //[필수]거래요청시간(YYYYYMMDDHH24MISS)
m.put("client_ip", clientIp); //[선택]서비스 이용 사용자 IP
return mapper.writeValueAsString(m);
}
private String encEncData(String encData, String symkey, String iv) throws IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException {
// 암호화
SecretKey secureKey = new SecretKeySpec(symkey.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secureKey, new IvParameterSpec(iv.getBytes()));
byte[] encrypted = cipher.doFinal(encData.trim().getBytes());
// Base64 인코딩
String reqDataEnc = Base64Utils.encodeToString(encrypted);
return reqDataEnc;
}
private String decEncData(String encData, String symkey, String iv) throws IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException {
// Base64 디코딩
byte[] respDataEnc = Base64Utils.decode(encData.getBytes());
// 복호화
SecretKey secureKey = new SecretKeySpec(symkey.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secureKey, new IvParameterSpec(iv.getBytes()));
byte[] decrypted = cipher.doFinal(respDataEnc);
String strDecrypted = new String(decrypted);
return strDecrypted;
}
/**
* Hmac (integrity_value)
*
* @param encData
* @param hmacKey
* @return
*/
private String createIntegrityValue(String encData, String hmacKey) {
byte[] hmacSha256 = hmac256(hmacKey.getBytes(), encData.getBytes());
String integrityValue = Base64.getEncoder().encodeToString(hmacSha256);
return integrityValue;
}
private byte[] hmac256(byte[] secretKey, byte[] message) {
byte[] hmac256 = null;
try {
Mac mac = Mac.getInstance("HmacSHA256");
SecretKeySpec sks = new SecretKeySpec(secretKey, "HmacSHA256");
mac.init(sks);
hmac256 = mac.doFinal(message);
return hmac256;
} catch (Exception e) {
throw new RuntimeException("Failed to generate HMACSHA256 encrypt");
}
}
}

@ -0,0 +1,120 @@
package cokr.xit.ci.api.service.support.rest.api;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.experimental.SuperBuilder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.*;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.HttpServerErrorException;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.Random;
@Slf4j
@SuperBuilder
public class NiceCiApiAbstract {
protected final String PRODUCT_ID = "2101466024";
protected final ObjectMapper mapper = new ObjectMapper()
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
/**
* <pre> : API
* </pre>
*
* @param method
* @param url
* @param body
* @param headers
* @return ResponseEntity
* @author:
*/
protected final ResponseEntity<String> callApi(HttpMethod method, String url, Object body, HttpHeaders headers) {
ResponseEntity<String> responseEntity = null;
try {
//uri 및 entity(param) 설정
HttpEntity<?> entity = null;
UriComponents uri = null;
switch (method) {
case GET:
entity = new HttpEntity<>(headers);
uri = UriComponentsBuilder
.fromHttpUrl(String.format("%s?%s", url, body == null ? "" : body))
// .encode(StandardCharsets.UTF_8) //"%"기호가 "%25"로 인코딩 발생하여 주석처리 함.
.build(false);
break;
case POST:
entity = new HttpEntity<>(body, headers);
uri = UriComponentsBuilder
.fromHttpUrl(url)
.encode(StandardCharsets.UTF_8)
.build();
break;
default:
break;
}
//api 호출
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
factory.setConnectTimeout(3000); //커넥션타임아웃 설정 3초
factory.setReadTimeout(3000);//타임아웃 설정 3초
RestTemplate restTemplate = new RestTemplate(factory);
log.info(" url => " + uri.toString());
log.info(" method => " + method);
log.info(" headers => " + entity.getHeaders().toString());
log.info(" body => " + entity.getBody());
responseEntity = restTemplate.exchange(URI.create(uri.toString()), method, entity, String.class); //이 한줄의 코드로 API를 호출해 String타입으로 전달 받는다.
/*
* HttpStatus
* -.: responseEntity.getStatusCodeValue()
* -.: responseEntity.getStatusCode()
*/
} catch (HttpServerErrorException e) {
responseEntity = new ResponseEntity<String>(e.getResponseBodyAsString(), e.getStatusCode());
log.error(String.format("call API 서버오류[url =>%s param => %s error => %s]", url, body, e.getMessage()));
} catch (HttpClientErrorException e) {
responseEntity = new ResponseEntity<String>(e.getResponseBodyAsString(), e.getStatusCode());
log.error(String.format("call API 클라이언트오류[url =>%s param => %s error => %s]", url, body, e.getMessage()));
} catch (RestClientException e) { //timeout 발생 또는 기타 오류...
responseEntity = new ResponseEntity<String>(e.getMessage(), HttpStatus.REQUEST_TIMEOUT);
log.error(String.format("RestAPI 호출 오류[url =>%s param => %s error => %s]", url, body, e.getMessage()));
} catch (Exception e) {
responseEntity = new ResponseEntity<String>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
log.error(String.format("call API 기타오류[url =>%s param => %s error => %s]", url, body, e.getMessage()));
}
//결과 응답
return responseEntity;
}
protected final String randomAlphaWord(int wordLength) {
Random r = new Random();
StringBuilder sb = new StringBuilder(wordLength);
for (int i = 0; i < wordLength; i++) {
char tmp = (char) ('a' + r.nextInt('z' - 'a'));
sb.append(tmp);
}
return sb.toString();
}
}

@ -0,0 +1,145 @@
package cokr.xit.ci.api.service.support.rest.api;
import cokr.xit.ci.api.service.support.rest.code.NiceCiApiCd;
import cokr.xit.ci.api.service.support.rest.model.NiceCiRespDTO;
import cokr.xit.ci.api.service.support.rest.model.NiceCiRespVO;
import cokr.xit.ci.api.service.support.rest.model.conf.DataBodyPubkeyResp;
import cokr.xit.ci.api.service.support.rest.utils.Token;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import lombok.experimental.SuperBuilder;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Base64Util;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.StringUtils;
import java.nio.charset.Charset;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@SuperBuilder
public class PubkeyRequest extends NiceCiApiAbstract {
protected final String HOST;
protected final String API_URL;
public NiceCiRespVO<DataBodyPubkeyResp> execute(String clientId) throws Exception {
/* ==============================================================================
*
============================================================================== */
if (StringUtils.isEmpty(Token.getInstance().getData().getAccessToken()))
return NiceCiRespVO.<DataBodyPubkeyResp>errBuilder().errCode(NiceCiApiCd.FAIL).errMsg("엑세스토큰은 필수 입력값 입니다.").build();
if (StringUtils.isEmpty(clientId))
return NiceCiRespVO.<DataBodyPubkeyResp>errBuilder().errCode(NiceCiApiCd.FAIL).errMsg("클라이언트ID는 필수 입력값 입니다.").build();
/* ==============================================================================
* API
============================================================================== */
NiceCiRespDTO<DataBodyPubkeyResp> responseDTO = this.generatePublickey(Token.getInstance().getData().getAccessToken(), clientId);
/* ==============================================================================
*
============================================================================== */
if (!"1200".equals(responseDTO.getDataHeader().getGW_RSLT_CD()))
return NiceCiRespVO.<DataBodyPubkeyResp>errBuilder()
.errCode(NiceCiApiCd.valueOfEnum("HEAD_" + responseDTO.getDataHeader().getGW_RSLT_CD()))
.errMsg(String.format("[%s]: %s", responseDTO.getDataHeader().getGW_RSLT_CD(), responseDTO.getDataHeader().getGW_RSLT_MSG()))
.build();
if (!"P000".equals(responseDTO.getDataBody().getRspCd()))
return NiceCiRespVO.<DataBodyPubkeyResp>errBuilder()
.errCode(NiceCiApiCd.valueOfEnum(responseDTO.getDataBody().getRspCd()))
.errMsg(String.format("[%s]: %s. %s", responseDTO.getDataBody().getRspCd(), NiceCiApiCd.valueOfEnum(responseDTO.getDataBody().getRspCd()).getCodeNm(), responseDTO.getDataBody().getResMsg()))
.build();
if (!"0000".equals(responseDTO.getDataBody().getResultCd()))
return NiceCiRespVO.<DataBodyPubkeyResp>errBuilder()
.errCode(NiceCiApiCd.valueOfEnum("PUBKEY_" + responseDTO.getDataBody().getResultCd()))
.errMsg(String.format("[%s]: %s", responseDTO.getDataBody().getResultCd(), NiceCiApiCd.valueOfEnum("PUBKEY_" + responseDTO.getDataBody().getResultCd()).getCodeNm()))
.build();
return NiceCiRespVO.<DataBodyPubkeyResp>okBuilder().resultInfo(responseDTO.getDataBody()).build();
}
protected NiceCiRespDTO<DataBodyPubkeyResp> generatePublickey(String accessToken, String clientId) throws Exception {
try {
/* ==============================================================================
* HEADER
============================================================================== */
String bearerToken = Base64Util.encode(String.format("%s:%s:%s", accessToken, (new Date().getTime() / 1000), clientId));
HttpHeaders headers = new HttpHeaders();
headers.setContentType(new MediaType(MediaType.APPLICATION_JSON, Charset.forName("utf-8")));
headers.set("Authorization", String.format("bearer %s", bearerToken));
headers.set("client_id", clientId);
headers.set("productID", PRODUCT_ID);
/* ==============================================================================
* URL
============================================================================== */
StringBuilder url = new StringBuilder();
url.append(this.HOST)
.append(this.API_URL);
String jsonStr = this.createMessage(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")));
/* ==============================================================================
* API
============================================================================== */
ResponseEntity<String> resp = this.callApi(HttpMethod.POST, url.toString(), jsonStr, headers);
log.info("==================================================================================");
log.info("==== 공개키 요청 Result Info... ====");
log.info("[Headers]: " + resp.getHeaders().toString());
log.info("[Body]: " + resp.getBody());
log.info("==================================================================================");
/* ==============================================================================
*
============================================================================== */
// NiceCiRespDTO<DataBodyPubkeyResp> respDTO = mapper.readValue(resp.getBody(), NiceCiRespDTO.class);
String strRespBody = resp.getBody().replace("\"\"", "null");
NiceCiRespDTO<DataBodyPubkeyResp> respDTO = mapper.readValue(strRespBody, new TypeReference<NiceCiRespDTO<DataBodyPubkeyResp>>(){});
return respDTO;
} catch (Exception e) {
e.printStackTrace();
throw new Exception("공개키 요청 실패." + e.getMessage());
}
}
/**
* JSON
*
* @param reqDtim
* @return
* @throws JsonProcessingException
*/
protected String createMessage(String reqDtim) throws JsonProcessingException {
Map<String, String> dataHeader = new HashMap<>();
dataHeader.put("CNTY_CD", "ko"); //[필수]이용언어: ko, en, cn ...
dataHeader.put("TRAN_ID", null); //[선택]API 통신구간에서 요청에 대한 응답을 확인하기 위한 고유번호
Map<String, String> dataBody = new HashMap<>();
dataBody.put("req_dtim", reqDtim); //[필수]공개키 요청일시(YYYYMMDDHH24MISS)
Map<String, Object> m = new HashMap<>();
m.put("dataHeader", dataHeader);
m.put("dataBody", dataBody);
return mapper.writeValueAsString(m);
}
}

@ -0,0 +1,278 @@
package cokr.xit.ci.api.service.support.rest.api;
import cokr.xit.ci.api.service.support.rest.code.NiceCiApiCd;
import cokr.xit.ci.api.service.support.rest.model.NiceCiRespDTO;
import cokr.xit.ci.api.service.support.rest.model.NiceCiRespVO;
import cokr.xit.ci.api.service.support.rest.model.conf.DataBodySymkeyResp;
import cokr.xit.ci.api.service.support.rest.utils.PublicKey;
import cokr.xit.ci.api.service.support.rest.utils.Token;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import lombok.experimental.SuperBuilder;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Base64Util;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.Base64Utils;
import org.springframework.util.StringUtils;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.nio.charset.Charset;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@SuperBuilder
public class SymkeyRegist extends NiceCiApiAbstract {
protected final String HOST;
protected final String API_URL;
public NiceCiRespVO<DataBodySymkeyResp> execute(String clientId) throws Exception {
/* ==============================================================================
*
============================================================================== */
if (StringUtils.isEmpty(Token.getInstance().getData().getAccessToken()))
return NiceCiRespVO.<DataBodySymkeyResp>errBuilder().errCode(NiceCiApiCd.FAIL).errMsg("엑세스토큰은 필수 입력값 입니다.").build();
if (StringUtils.isEmpty(clientId))
return NiceCiRespVO.<DataBodySymkeyResp>errBuilder().errCode(NiceCiApiCd.FAIL).errMsg("클라이언트ID는 필수 입력값 입니다.").build();
if (StringUtils.isEmpty(PublicKey.getInstance().getData().getSiteCode()))
return NiceCiRespVO.<DataBodySymkeyResp>errBuilder().errCode(NiceCiApiCd.FAIL).errMsg("사이트코드는 필수 입력값 입니다.").build();
if (StringUtils.isEmpty(PublicKey.getInstance().getData().getPublicKey()))
return NiceCiRespVO.<DataBodySymkeyResp>errBuilder().errCode(NiceCiApiCd.FAIL).errMsg("공개키는 필수 입력값 입니다.").build();
if (StringUtils.isEmpty(PublicKey.getInstance().getData().getKeyVersion()))
return NiceCiRespVO.<DataBodySymkeyResp>errBuilder().errCode(NiceCiApiCd.FAIL).errMsg("공개키버전은 필수 입력값 입니다.").build();
/* ==============================================================================
* API
============================================================================== */
NiceCiRespDTO<DataBodySymkeyResp> responseDTO = this.generateSymmetrickey(Token.getInstance().getData().getAccessToken(), clientId, PublicKey.getInstance().getData().getSiteCode(), PublicKey.getInstance().getData().getPublicKey(), PublicKey.getInstance().getData().getKeyVersion());
/* ==============================================================================
*
============================================================================== */
if (!"1200".equals(responseDTO.getDataHeader().getGW_RSLT_CD()))
return NiceCiRespVO.<DataBodySymkeyResp>errBuilder()
.errCode(NiceCiApiCd.valueOfEnum("HEAD_" + responseDTO.getDataHeader().getGW_RSLT_CD()))
.errMsg(String.format("[%s]: %s", responseDTO.getDataHeader().getGW_RSLT_CD(), responseDTO.getDataHeader().getGW_RSLT_MSG()))
.build();
if (!"P000".equals(responseDTO.getDataBody().getRspCd()))
return NiceCiRespVO.<DataBodySymkeyResp>errBuilder()
.errCode(NiceCiApiCd.valueOfEnum(responseDTO.getDataBody().getRspCd()))
.errMsg(String.format("[%s]: %s. %s", responseDTO.getDataBody().getRspCd(), NiceCiApiCd.valueOfEnum(responseDTO.getDataBody().getRspCd()).getCodeNm(), responseDTO.getDataBody().getResMsg()))
.build();
if (!"0000".equals(responseDTO.getDataBody().getResultCd()))
return NiceCiRespVO.<DataBodySymkeyResp>errBuilder()
.errCode(NiceCiApiCd.valueOfEnum("SYMKEY_" + responseDTO.getDataBody().getResultCd()))
.errMsg(String.format("[%s]: %s", responseDTO.getDataBody().getResultCd(), NiceCiApiCd.valueOfEnum("SYMKEY_" + responseDTO.getDataBody().getResultCd()).getCodeNm()))
.build();
return NiceCiRespVO.<DataBodySymkeyResp>okBuilder().resultInfo(responseDTO.getDataBody()).build();
}
protected NiceCiRespDTO<DataBodySymkeyResp> generateSymmetrickey(String accessToken, String clientId, String siteCode, String publicKey, String pubkeyVersion) throws Exception {
try {
/* ==============================================================================
* HEADER
============================================================================== */
String bearerToken = Base64Util.encode(String.format("%s:%s:%s", accessToken, (new Date().getTime() / 1000), clientId));
HttpHeaders headers = new HttpHeaders();
headers.setContentType(new MediaType(MediaType.APPLICATION_JSON, Charset.forName("utf-8")));
headers.set("Authorization", String.format("bearer %s", bearerToken));
headers.set("client_id", clientId);
headers.set("productID", PRODUCT_ID);
/* ==============================================================================
* URL
============================================================================== */
StringBuilder url = new StringBuilder();
url.append(this.HOST)
.append(this.API_URL);
final String requestNo = createRequestNo();
final String key = this.createKey();
final String iv = this.createIv();
final String hmacKey = this.createHmacKey();
final String strSymkeyRegInfo = this.createSymkeyRegInfo(siteCode, requestNo, key, iv, hmacKey);
final String jsonStr = this.createMessage(pubkeyVersion, encSymkeyRegInfo(strSymkeyRegInfo, publicKey));
/* ==============================================================================
* API
============================================================================== */
ResponseEntity<String> resp = this.callApi(HttpMethod.POST, url.toString(), jsonStr, headers);
log.info("==================================================================================");
log.info("==== 대칭키 등록 요청 Info... ====");
log.info("[Body]: " + jsonStr);
log.info("[symkeyRegInfo]: " + strSymkeyRegInfo);
log.info("==== 대칭키 등록 요청 Result Info... ====");
log.info("[Headers]: " + resp.getHeaders().toString());
log.info("[Body]: " + resp.getBody());
log.info("==================================================================================");
/* ==============================================================================
*
============================================================================== */
// NiceCiRespDTO<DataBodySymkeyResp> respDTO = mapper.readValue(resp.getBody(), NiceCiRespDTO.class);
String strRespBody = resp.getBody().replace("\"\"", "null");
strRespBody = strRespBody.replace("\\", "").replace("\"{", "{").replace("}\"", "}"); //symkey_stat_info 데이터의 쌍따옴표를 제거하여 Json데이터로 인식하도록 replace 적용
NiceCiRespDTO<DataBodySymkeyResp> respDTO = mapper.readValue(strRespBody, new TypeReference<NiceCiRespDTO<DataBodySymkeyResp>>(){});
if (!(respDTO.getDataBody() == null || "".equals(respDTO.getDataBody()))) {
respDTO.getDataBody().setSiteCode(siteCode);
respDTO.getDataBody().setRequestNo(requestNo);
respDTO.getDataBody().setKey(key);
respDTO.getDataBody().setIv(iv);
respDTO.getDataBody().setHmacKey(hmacKey);
}
return respDTO;
} catch (Exception e) {
e.printStackTrace();
throw new Exception("대칭키 등록 요청 실패." + e.getMessage());
}
}
private String createSymkeyRegInfo(String siteCode, String requestNo, String key, String iv, String hmacKey) throws JsonProcessingException {
Map<String, String> m = new HashMap<>();
m.put("site_code", siteCode); //[필수]사이트코드(공개키요청시 수신한 사이트코드)
m.put("request_no", requestNo); //[필수]요청고유번호(이용기관에서 임의 생성한 값)
m.put("key", key); //[필수]회원사에서 사용할 암호화 KEY 세팅(32byte AES256 bits, 16byte AES128 bits). NICE에 Key 등록 후 최대 6개월 내 갱신 필요
m.put("iv", iv); //[필수]데이터를 암호화할 Initial Vector. 회원사에서 생성(16 byte 딱 맞게 생성)
m.put("hmac_key", hmacKey); //[필수]무결성체크값에 사용할 Hmac Key. 회원사에서 생성(32 byte 딱 맞게 생성)
return mapper.writeValueAsString(m);
}
/**
*
* -. KEY NICE 6
* -. 6 , AP
* -. 6
* <p>
* []
* 1) ,
* 2)
* 3) API (enc_data)
* => API )
*
* @return
*/
private String createKey() {
return randomAlphaWord(32);
}
/**
* Initail Vector
* -. Initial Vector
* -. 16 byte
*
* @return
*/
private String createIv() {
return randomAlphaWord(16);
}
/**
*
* -.
*
* @return
*/
private String createRequestNo() {
return randomAlphaWord(30);
}
/**
* hamc_key
* -. Hmac Key
* -. 32 byte
*
* @return
*/
private String createHmacKey() {
return randomAlphaWord(32);
}
/**
* .
*
* @param symkeyRegInfo
* @param sPublicKey
* @return
* @throws NoSuchAlgorithmException
* @throws InvalidKeySpecException
* @throws NoSuchPaddingException
* @throws InvalidKeyException
* @throws IllegalBlockSizeException
* @throws BadPaddingException
*/
protected final String encSymkeyRegInfo(String symkeyRegInfo, String sPublicKey) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
//공개키 변환
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
// byte[] cipherEnc = Base64Utils.decode(sPublicKey.getBytes());
byte[] cipherEnc = Base64.getDecoder().decode(sPublicKey);
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(cipherEnc);
java.security.PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);
//암호화
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] bytePlain = cipher.doFinal(symkeyRegInfo.getBytes());
// Base64 인코딩
String keyInfoEnc = Base64Utils.encodeToString(bytePlain);
return keyInfoEnc;
}
/**
* JSON
*
* @param pubkeyVersion
* @param symkeyRegInfo
* @return
* @throws JsonProcessingException
*/
protected String createMessage(String pubkeyVersion, String symkeyRegInfo) throws JsonProcessingException {
Map<String, String> dataHeader = new HashMap<>();
dataHeader.put("CNTY_CD", "ko"); //[필수]이용언어: ko, en, cn ...
dataHeader.put("TRAN_ID", null); //[선택]API 통신구간에서 요청에 대한 응답을 확인하기 위한 고유번호
Map<String, String> dataBody = new HashMap<>();
dataBody.put("pubkey_version", pubkeyVersion); //[필수]공개키 버전
dataBody.put("symkey_reg_info", symkeyRegInfo); //[필수]JSON암호화 값
Map<String, Object> m = new HashMap<>();
m.put("dataHeader", dataHeader);
m.put("dataBody", dataBody);
return mapper.writeValueAsString(m);
}
}

@ -0,0 +1,225 @@
package cokr.xit.ci.api.service.support.rest.api;
import cokr.xit.ci.api.service.support.rest.code.NiceCiApiCd;
import cokr.xit.ci.api.service.support.rest.model.NiceCiRespDTO;
import cokr.xit.ci.api.service.support.rest.model.NiceCiRespVO;
import cokr.xit.ci.api.service.support.rest.model.conf.DataBodyGenerateTokenResp;
import cokr.xit.ci.api.service.support.rest.model.conf.DataBodyRevokeTokenResp;
import cokr.xit.ci.core.utils.DateUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import lombok.experimental.SuperBuilder;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Base64Util;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
@Slf4j
@SuperBuilder
public class TokenGenerate extends NiceCiApiAbstract {
protected final String HOST;
protected final String API_URL_GENERATE_TOKEN;
protected final String API_URL_REVOKE_TOKEN;
public NiceCiRespVO<DataBodyGenerateTokenResp> execute(String clientId, String clientSecret) throws Exception {
/* ==============================================================================
*
============================================================================== */
if (StringUtils.isEmpty(clientId))
return NiceCiRespVO.<DataBodyGenerateTokenResp>errBuilder().errCode(NiceCiApiCd.FAIL).errMsg("클라이언트ID는 필수 입력값 입니다.").build();
if (StringUtils.isEmpty(clientSecret))
return NiceCiRespVO.<DataBodyGenerateTokenResp>errBuilder().errCode(NiceCiApiCd.FAIL).errMsg("클라이언트비밀번호는 필수 입력값 입니다.").build();
/* ==============================================================================
* API
============================================================================== */
NiceCiRespDTO<DataBodyGenerateTokenResp> generateResponseDTO = this.generateToken(clientId, clientSecret);
/* ==============================================================================
*
* [ ]
* -.or API GW_RSLT_CD 1800 "token 요청"
* -. 30 (&)
============================================================================== */
if ("1800".equals(generateResponseDTO.getDataHeader().getGW_RSLT_CD())) //결과코드가 "1800" 이면...
generateResponseDTO = this.generateToken(clientId, clientSecret);
if (!"1200".equals(generateResponseDTO.getDataHeader().getGW_RSLT_CD()))
return NiceCiRespVO.<DataBodyGenerateTokenResp>errBuilder()
.errCode(NiceCiApiCd.valueOfEnum("HEAD_" + generateResponseDTO.getDataHeader().getGW_RSLT_CD()))
.errMsg(String.format("[%s]: %s", generateResponseDTO.getDataHeader().getGW_RSLT_CD(), generateResponseDTO.getDataHeader().getGW_RSLT_MSG()))
.build();
if (DateUtil.secToDays(generateResponseDTO.getDataBody().getExpiresIn()) < 30) { //토큰만료일이 30일 미만이면..
//토큰폐기
NiceCiRespDTO<DataBodyRevokeTokenResp> revokeResponseDTO = this.revokeToken(generateResponseDTO.getDataBody().getAccessToken(), clientId);
if (!"1200".equals(revokeResponseDTO.getDataHeader().getGW_RSLT_CD()))
return NiceCiRespVO.<DataBodyGenerateTokenResp>errBuilder()
.errCode(NiceCiApiCd.valueOfEnum("HEAD_" + generateResponseDTO.getDataHeader().getGW_RSLT_CD()))
.errMsg(String.format("[%s]: %s", generateResponseDTO.getDataHeader().getGW_RSLT_CD(), generateResponseDTO.getDataHeader().getGW_RSLT_MSG()))
.build();
if(!revokeResponseDTO.getDataBody().getResult())
return NiceCiRespVO.<DataBodyGenerateTokenResp>errBuilder().errCode(NiceCiApiCd.FAIL).errMsg(String.format("토큰(%s) 폐기에 실패 했습니다.", generateResponseDTO.getDataBody().getAccessToken())).build();
//토큰요청
generateResponseDTO = this.generateToken(clientId, clientSecret);
if (!"1200".equals(generateResponseDTO.getDataHeader().getGW_RSLT_CD()))
return NiceCiRespVO.<DataBodyGenerateTokenResp>errBuilder()
.errCode(NiceCiApiCd.valueOfEnum("HEAD_" + generateResponseDTO.getDataHeader().getGW_RSLT_CD()))
.errMsg(String.format("[%s]: %s", generateResponseDTO.getDataHeader().getGW_RSLT_CD(), generateResponseDTO.getDataHeader().getGW_RSLT_MSG()))
.build();
}
return NiceCiRespVO.<DataBodyGenerateTokenResp>okBuilder().resultInfo(generateResponseDTO.getDataBody()).build();
}
protected NiceCiRespDTO<DataBodyGenerateTokenResp> generateToken(String clientId, String clientSecret) throws Exception {
try {
/* ==============================================================================
* HEADER
============================================================================== */
String authorizationToken = Base64Util.encode(String.format("%s:%s", clientId, clientSecret));
HttpHeaders headers = new HttpHeaders();
headers.setContentType(new MediaType(MediaType.APPLICATION_FORM_URLENCODED, Charset.forName("utf-8")));
headers.set("Authorization", String.format("Basic %s", authorizationToken));
/* ==============================================================================
* URL
============================================================================== */
StringBuilder url = new StringBuilder();
url.append(this.HOST)
.append(API_URL_GENERATE_TOKEN);
MultiValueMap<String, String> body = this.createMessage();
/* ==============================================================================
* API
============================================================================== */
ResponseEntity<String> resp = this.callApi(HttpMethod.POST, url.toString(), body, headers);
log.info("==================================================================================");
log.info("==== 토큰발급 요청 Result Info... ====");
log.info("[Headers]: " + resp.getHeaders().toString());
log.info("[Body]: " + resp.getBody());
log.info("==================================================================================");
/* ==============================================================================
*
* => {"dataHeader":{"GW_RSLT_CD":"1200","GW_RSLT_MSG":"오류 없음"},"dataBody":{"access_token":"8c680964-eec7-485c-ad58-6534e90cc653","token_type":"bearer","expires_in":1576914752,"scope":"default"}}
============================================================================== */
// NiceCiRespDTO<DataBodyGenerateTokenResp> respDTO = mapper.readValue(resp.getBody(), NiceCiRespDTO.class);
String strRespBody = resp.getBody().replace("\"\"", "null");
NiceCiRespDTO<DataBodyGenerateTokenResp> respDTO = mapper.readValue(strRespBody, new TypeReference<NiceCiRespDTO<DataBodyGenerateTokenResp>>(){});
if(!(respDTO.getDataBody()==null||"".equals(respDTO.getDataBody())))
respDTO.getDataBody().setExpiredDt(this.addSec(respDTO.getDataBody().getExpiresIn(), "yyyyMMddHHmmss"));
return respDTO;
} catch (Exception e) {
e.printStackTrace();
throw new Exception("토큰발급 요청 실패." + e.getMessage());
}
}
protected NiceCiRespDTO<DataBodyRevokeTokenResp> revokeToken(String accessToken, String clientId) throws Exception {
try {
/* ==============================================================================
* HEADER
============================================================================== */
String bearerToken = Base64Util.encode(String.format("%s:%s:%s", accessToken, (new Date().getTime() / 1000), clientId));
HttpHeaders headers = new HttpHeaders();
headers.setContentType(new MediaType(MediaType.APPLICATION_FORM_URLENCODED, Charset.forName("utf-8")));
headers.set("Authorization", String.format("Basic %s", bearerToken));
/* ==============================================================================
* URL
============================================================================== */
StringBuilder url = new StringBuilder();
url.append(this.HOST)
.append(API_URL_REVOKE_TOKEN);
/* ==============================================================================
* API
============================================================================== */
ResponseEntity<String> resp = this.callApi(HttpMethod.POST, url.toString(), null, headers);
log.info("==================================================================================");
log.info("==== 토큰폐기 요청 Result Info... ====");
log.info("[Headers]: " + resp.getHeaders().toString());
log.info("[Body]: " + resp.getBody());
log.info("==================================================================================");
/* ==============================================================================
*
============================================================================== */
// NiceCiRespDTO<DataBodyRevokeTokenResp> respDTO = mapper.readValue(resp.getBody(), NiceCiRespDTO.class);
String strRespBody = resp.getBody().replace("\"\"", "null");
NiceCiRespDTO<DataBodyRevokeTokenResp> respDTO = mapper.readValue(strRespBody, new TypeReference<NiceCiRespDTO<DataBodyRevokeTokenResp>>(){});
return respDTO;
} catch (Exception e) {
e.printStackTrace();
throw new Exception("토큰폐기 요청 실패." + e.getMessage());
}
}
/**
* JSON
*
* @return
* @throws JsonProcessingException
*/
protected MultiValueMap<String, String> createMessage() throws JsonProcessingException {
MultiValueMap<String, String> m = new LinkedMultiValueMap<>();
m.add("grant_type", "client_credentials");
m.add("scope", "default");
return m;
}
/**
* (sec) .
* @param sec
* @param pattern
* @return
*/
private String addSec(Integer sec, String pattern){
if(sec==null)
return null;
if("".equals(pattern)||pattern==null)
pattern = "yyyyMMdd";
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis((sec*1000L) + (new Date().getTime()));
Date date = calendar.getTime();
return new SimpleDateFormat(pattern).format(date);
}
}

@ -3,23 +3,26 @@ package cokr.xit.ci.api.service.support.rest.code;
public enum NiceCiApiCd { public enum NiceCiApiCd {
UNKNOWN("알수없음") UNKNOWN("알수없음")
/* ======================================================================= , OK("정상")
* HTTP , FAIL("실패")
* -. HTTP prefix "HTTP_" // /* =======================================================================
======================================================================= */ // * HTTP 응답코드
, HTTP_200("No Error") // * -. HTTP의 코드값을 열거형 상수로 선언하기 위해 prefix "HTTP_"를 추가 함
, HTTP_400("Bad Request or Inavalid Token") // ======================================================================= */
, HTTP_401("Authorized required") // , HTTP_200("No Error")
, HTTP_402("unAuthorized") // , HTTP_400("Bad Request or Inavalid Token")
, HTTP_403("service Disabled") // , HTTP_401("Authorized required")
, HTTP_404("Service Not Found") // , HTTP_402("unAuthorized")
, HTTP_500("Internal Server Error") // , HTTP_403("service Disabled")
, HTTP_501("Access Denied by Protected Service") // , HTTP_404("Service Not Found")
, HTTP_502("Bad Response from Protected Service") // , HTTP_500("Internal Server Error")
, HTTP_503("Service Temporarily Unavailable") // , HTTP_501("Access Denied by Protected Service")
// , HTTP_502("Bad Response from Protected Service")
// , HTTP_503("Service Temporarily Unavailable")
/* ======================================================================= /* =======================================================================
* APIM (dataHeader) * APIM (dataHeader.GW_RSLT_CD)
* -. DataHeader GW_RSLT_CD "1200" , dataBody
* -. Data Header prefix "HEAD_" * -. Data Header prefix "HEAD_"
======================================================================= */ ======================================================================= */
, HEAD_1200("오류 없음 (정상)") , HEAD_1200("오류 없음 (정상)")
@ -60,7 +63,8 @@ public enum NiceCiApiCd {
/* ======================================================================= /* =======================================================================
* (rsp_cd) * (dataBody.rsp_cd)
* -.dataBody rsp_cd P000 , result_cd
======================================================================= */ ======================================================================= */
, P000("정상응답") , P000("정상응답")
, S603("내부 DB 오류") , S603("내부 DB 오류")
@ -71,7 +75,8 @@ public enum NiceCiApiCd {
/* ======================================================================= /* =======================================================================
* APIM (dataBody) * APIM (dataBody.???)
* -.dataBody result_cd "0000"
======================================================================= */ ======================================================================= */
, EAPI2500("맵핑정보 없음 - {0}") , EAPI2500("맵핑정보 없음 - {0}")
, EAPI2510("요청맵핑 데이터가 없습니다.") , EAPI2510("요청맵핑 데이터가 없습니다.")
@ -97,13 +102,49 @@ public enum NiceCiApiCd {
/* ======================================================================= /* =======================================================================
* * (result_cd) -
* -. prefix "DRSLT_" * -. rsp_cd "P000"
* -. prefix "PUBKEY_"
======================================================================= */
, PUBKEY_0000("공개키 발급")
, PUBKEY_0001("필수입력값 오류")
, PUBKEY_0003("공개키 발급 대상 회원사 아님")
, PUBKEY_0099("기타오류")
/* =======================================================================
* (result_cd) -
* -. rsp_cd "P000"
* -. prefix "SYMKEY_"
======================================================================= */
, SYMKEY_0000("대칭키 발급")
, SYMKEY_0001("공개키 기간 만료")
, SYMKEY_0002("공개키를 찾을 수 없음")
, SYMKEY_0003("공개키를 발급한 회원사 아님")
, SYMKEY_0004("복호화 오류")
, SYMKEY_0005("필수입력값 오류 (key_version, key_info 필수값 확인)")
, SYMKEY_0006("대칭키 등록 가능 회원사 아님")
, SYMKEY_0007("key 중복 오류 (현재 및 직전에 사용한 Key 사용 불가)")
, SYMKEY_0008("요청사이트코드와 공개키발급 사이트코드 다름")
, SYMKEY_0099("기타오류")
/* =======================================================================
* (result_cd) - CI
* -. rsp_cd "P000"
* -. prefix "CI_"
======================================================================= */ ======================================================================= */
, DRSLT_0000("공개키 발급") , CI_0000("처리완료")
, DRSLT_0001("필수입력값 오류") , CI_0001("대칭키 기간 만료")
, DRSLT_0003("공개키 발급 대상 회원사 아님") , CI_0002("대칭키를 찾을 수 없음")
, DRSLT_0099("기타오류") , CI_0003("대칭키를 발급한 회원사 아님")
, CI_0004("복호화 오류")
, CI_0005("필수입력값 오류 (integrity_value, enc_data 내 필수값 확인)")
, CI_0006("데이터 무결성 오류 (hmac값 불일치)")
, CI_0007("정보요청유형 입력값 오류 (info_req_type이 1 아님)")
, CI_0008("주민번호 유효성 오류 (생년월일 유효성 및 숫자 아님)")
, CI_0009("거래요청시간 포멧오류 (req_dtim 자릿수 및 숫자 아님)")
, CI_0099("기타오류")
; ;

@ -1,26 +0,0 @@
package cokr.xit.ci.api.service.support.rest.model;
import cokr.xit.ci.api.service.support.rest.model.conf.EncData;
import com.fasterxml.jackson.annotation.JsonAlias;
import lombok.Builder;
import lombok.Getter;
import lombok.ToString;
@Getter
@ToString
@Builder
public class CiRespDTO implements TransDTO {
@JsonAlias({"rsp_cd"})
private String rspCd; //dataBody 정상처리여부 (P000 성공, 이외 모두 오류)
@JsonAlias({"res_msg"})
private String resMsg; //rsp_cd가 "EAPI"로 시작될 경우 오류 메시지 세팅
@JsonAlias({"enc_data"})
private EncData encData; //JSON암호화값(rsp_cd가 P000일 때 나감) - 응답정보를 회원사에서 요청시 전달한 대칭키로 암호화한 값
@JsonAlias({"integrity_value"})
private String integrityValue; //무결성체크를 위해 enc_data를 HMAC처리 후, BASE64 인코딩한 값
}

@ -1,28 +0,0 @@
package cokr.xit.ci.api.service.support.rest.model;
import com.fasterxml.jackson.annotation.JsonAlias;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@ToString
@Builder
public class GenerateTokenRespDTO implements TransDTO {
@JsonAlias({"access_token"})
private String accessToken; //사용자 엑세스 토큰 값(모든 API 요청시 헤더에 access_token을 포함하여 전송)
@JsonAlias({"expires_in"})
private Long expiresIn; //access token 만료까지 남은시간(초)
@JsonAlias({"token_type"})
private String tokenType; //bearer로 고정
@JsonAlias({"scope"})
private String scope; //요청한 scope값(기본 default)
@Setter
private String expiredDt; //access token 만료시간(yyyyMmddHHmmss)
}

@ -0,0 +1,22 @@
package cokr.xit.ci.api.service.support.rest.model;
import cokr.xit.ci.api.service.support.rest.model.conf.DataHeader;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Data
public class NiceCiRespDTO<T> {
private DataHeader dataHeader;
private T dataBody;
}

@ -0,0 +1,34 @@
package cokr.xit.ci.api.service.support.rest.model;
import cokr.xit.ci.api.service.support.rest.code.NiceCiApiCd;
import lombok.Builder;
import lombok.Getter;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Getter
@ToString
public class NiceCiRespVO<T> {
private NiceCiApiCd errCode;
private String errMsg;
private T resultInfo;
@Builder(builderClassName = "okBuilder" ,builderMethodName = "okBuilder")
NiceCiRespVO(T resultInfo) {
this.errCode = NiceCiApiCd.OK;
this.errMsg = NiceCiApiCd.OK.getCodeNm();
this.resultInfo = resultInfo;
}
@Builder(builderClassName = "errBuilder" ,builderMethodName = "errBuilder")
NiceCiRespVO(NiceCiApiCd errCode, String errMsg, T resultInfo) {
this.errCode = errCode;
this.errMsg = errMsg;
this.resultInfo = resultInfo;
}
}

@ -1,37 +0,0 @@
package cokr.xit.ci.api.service.support.rest.model;
import com.fasterxml.jackson.annotation.JsonAlias;
import lombok.Builder;
import lombok.Getter;
import lombok.ToString;
@Getter
@ToString
@Builder
public class PublickeyRespDTO implements TransDTO {
@JsonAlias({"rsp_cd"})
private String rspCd; //dataBody 정상처리여부 (P000 성공, 이외 모두 오류)
@JsonAlias({"res_msg"})
private String resMsg; //rsp_cd가 "EAPI"로 시작될 경우 오류 메시지 세팅
@JsonAlias({"result_cd"})
private String resultCd; //rsp_cd가 P000일 때 상세결과코드(0000:공개키발급, 0001:필수입력값 오류, 0003:공개키 발급 대상 회원사 아님, 0099: 기타오류)
@JsonAlias({"site_code"})
private String siteCode; //사이트코드
@JsonAlias({"key_version"})
private String keyVersion; //공개키 버전
@JsonAlias({"public_key"})
private String publicKey; //공개키
@JsonAlias({"valid_dtim"})
private String validDtim; //공개키 만료일시(YYYYMMDDHH24MISS)
}

@ -1,16 +0,0 @@
package cokr.xit.ci.api.service.support.rest.model;
import com.fasterxml.jackson.annotation.JsonAlias;
import lombok.Builder;
import lombok.Getter;
import lombok.ToString;
@Getter
@ToString
@Builder
public class RevokeTokenRespDTO implements TransDTO {
@JsonAlias({"result"})
private Boolean result; //폐기여부(true: 폐기 성공, false: 폐기 실패)
}

@ -1,27 +0,0 @@
package cokr.xit.ci.api.service.support.rest.model;
import cokr.xit.ci.api.service.support.rest.model.conf.SymkeyStatInfo;
import com.fasterxml.jackson.annotation.JsonAlias;
import lombok.Builder;
import lombok.Getter;
import lombok.ToString;
@Getter
@ToString
@Builder
public class SymmetrickeyRespDTO implements TransDTO {
@JsonAlias({"rsp_cd"})
private String rspCd; //dataBody 정상처리여부 (P000 성공, 이외 모두 오류)
@JsonAlias({"res_msg"})
private String resMsg; //rsp_cd가 "EAPI"로 시작될 경우 오류 메시지 세팅
@JsonAlias({"result_cd"})
private String resultCd; //rsp_cd가 P000일 때 상세결과코드(0000:대칭키발급, 0001:공개키기간만료, 0002:공개키를 찾을 수 없음, 0003:공개키를 발급한 회원사 아님, 0004:복호화 오류, 0005:필수입력값 오류(key_version, key_info 필수값 확인), 0006:대칭키 등록 가능 회원사 아님, 0007:key 중복 오류(현재 및 직전에 사용한 Key 사용 불가), 0008:요청사이트코드와 공개키발급 사이트코드 다름, 0099: 기타오류)
@JsonAlias({"symkey_stat_info"})
private SymkeyStatInfo symkeyStatInfo; //JSON값(회원사에 생성되어 있는 대칭키 버전별 유효기간(result_cd 가 0000, 0007일 경우에 나감)
}

@ -1,5 +0,0 @@
package cokr.xit.ci.api.service.support.rest.model;
public interface TransDTO
{
}

@ -0,0 +1,41 @@
package cokr.xit.ci.api.service.support.rest.model.conf;
import com.fasterxml.jackson.annotation.JsonAlias;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import lombok.experimental.SuperBuilder;
@Getter
@ToString
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@Schema(name = "DataBodyCiResp", description = "아이핀 CI 요청 응답DataBody")
public class DataBodyCiResp {
@JsonAlias({"rsp_cd"})
@Schema(required = true, title = "dataBody 응답 코드", example = "P000", description = "dataBody 정상처리여부 (P000 성공, 이외 모두 오류)")
private String rspCd;
@JsonAlias({"res_msg"})
@Schema(required = false, title = "dataBody 응답 메시지", example = " ", description = "rsp_cd가 \"EAP\"로 시작될 경우 오류 메시지 세팅됨")
private String resMsg;
@JsonAlias({"result_cd"})
@Schema(required = false, title = "상세결과코드", example = " ", description = "rsp_cd가 P000일때 상세결과코드(0000:처리완료, 0001:대칭키 기간 만료, 0002:대칭키를 찾을 수 없음, 0003:대칭키를 발급한 회원사 아님, 0004:복호화 오류, 0005:필수입력값 오류(integrity_value, enc_data 내 필수값 확인), 0006:데이터 무결성 오류(hmac값 불일치), 0007:정보요청유형 입력값 오류(info_req_type이 1 아님), 0008:주민번호 유효성 오류(생년월일 유효성 및 숫자 아님), 0009:거래요청시간 포멧오류(req_dtim 자릿수 및 숫자 아님), 0099:기타오류)")
private String resultCd;
@JsonAlias({"enc_data"})
@Schema(required = false, title = "JSON 암호화값", example = " ", description = "P000일때 나감. 응답정보를 회원사에서 요청 시 전달한 대칭키로 암호화한 값")
private String encData;
@JsonAlias({"integrity_value"})
@Schema(required = false, title = "base64 인코딩 값", example = " ", description = "무결성체크를 위해 enc_data를 HMAC처리 후, BASE64 인코딩한 값")
private String integrityValue;
@Schema(required = false, title = "JSON 디코딩 값", example = " ", description = "encData 를 디코딩한 값")
@Setter
private EncData decEncData;
}

@ -0,0 +1,35 @@
package cokr.xit.ci.api.service.support.rest.model.conf;
import com.fasterxml.jackson.annotation.JsonAlias;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import lombok.experimental.SuperBuilder;
@Getter
@ToString
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@Schema(name = "DataBodyGenerateTokenResp", description = "토큰발급 API 응답DataBody")
public class DataBodyGenerateTokenResp {
@JsonAlias({"access_token"})
@Schema(required = true, title = "엑세스토큰", example = " ", description = "사용자 엑세스 토큰 값(모든 API 요청시 헤더에 access_token을 포함하여 전송)")
private String accessToken;
@JsonAlias({"expires_in"})
@Schema(required = true, title = "엑세스토큰 만료 절대시간(sec)", example = " ", description = "access token 만료까지 남은시간(초)")
private Integer expiresIn;
@JsonAlias({"token_type"})
@Schema(required = true, title = "토큰타입", example = " ", description = "bearer로 고정")
private String tokenType;
@JsonAlias({"scope"})
@Schema(required = true, title = "요청한 scope값", example = " ", description = "요청한 scope값(기본 default)")
private String scope;
@Setter
@Schema(required = true, title = "access token 만료시간(yyyyMmddHHmmss)", example = " ", description = "사용자 응답을 위해 절대시간(expires_in)을 yyyyMMddHHmmss 포맷으로 변경한 값")
private String expiredDt;
}

@ -0,0 +1,50 @@
package cokr.xit.ci.api.service.support.rest.model.conf;
import com.fasterxml.jackson.annotation.JsonAlias;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.experimental.SuperBuilder;
@Getter
@ToString
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@Schema(name = "DataBodyPubkeyResp", description = "공개키 요청 API 응답DataBody")
public class DataBodyPubkeyResp {
@JsonAlias({"rsp_cd"})
@Schema(required = true, title = "dataBody 응답코드", example = " ", description = "dataBody 정상처리여부 (P000 성공, 이외 모두 오류)")
private String rspCd;
@JsonAlias({"res_msg"})
@Schema(required = false, title = "dataBody 응답메시지", example = " ", description = "rsp_cd가 \"EAPI\"로 시작될 경우 오류 메시지 세팅")
private String resMsg;
@JsonAlias({"result_cd"})
@Schema(required = false, title = "상세결과코드", example = " ", description = "rsp_cd가 P000일 때 상세결과코드(0000:공개키발급, 0001:필수입력값 오류, 0003:공개키 발급 대상 회원사 아님, 0099: 기타오류)")
private String resultCd;
@JsonAlias({"site_code"})
@Schema(required = false, title = "사이트코드", example = " ", description = "사이트코드")
private String siteCode;
@JsonAlias({"key_version"})
@Schema(required = false, title = "공개키 버전", example = " ", description = "공개키 버전")
private String keyVersion;
@JsonAlias({"public_key"})
@Schema(required = false, title = "공개키", example = " ", description = "공개키")
private String publicKey;
@JsonAlias({"valid_dtim"})
@Schema(required = false, title = "공개키 만료일시", example = " ", description = "공개키 만료일시(YYYYMMDDHH24MISS)")
private String validDtim;
}

@ -0,0 +1,23 @@
package cokr.xit.ci.api.service.support.rest.model.conf;
import com.fasterxml.jackson.annotation.JsonAlias;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.experimental.SuperBuilder;
@Getter
@ToString
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@Schema(name = "DataBodyRevokeTokenResp", description = "토큰폐기 API 응답DataBody")
public class DataBodyRevokeTokenResp {
@JsonAlias({"result"})
@Schema(required = true, title = "토큰폐기 여부", example = " ", description = "폐기여부(true: 폐기 성공, false: 폐기 실패)")
private Boolean result;
}

@ -0,0 +1,48 @@
package cokr.xit.ci.api.service.support.rest.model.conf;
import com.fasterxml.jackson.annotation.JsonAlias;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import lombok.experimental.SuperBuilder;
@Getter
@ToString
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@Schema(name = "DataBodySymkeyResp", description = "대칭키 요청 API 응답DataBody")
public class DataBodySymkeyResp {
@JsonAlias({"rsp_cd"})
@Schema(required = true, title = "dataBody 응답코드", example = " ", description = "dataBody 정상처리여부 (P000 성공, 이외 모두 오류)")
private String rspCd;
@JsonAlias({"res_msg"})
@Schema(required = false, title = "dataBody 응답메시지", example = " ", description = "rsp_cd가 \"EAPI\"로 시작될 경우 오류 메시지 세팅")
private String resMsg;
@JsonAlias({"result_cd"})
@Schema(required = false, title = "상세결과코드", example = " ", description = "rsp_cd가 P000일 때 상세결과코드(0000:대칭키발급, 0001:공개키기간만료, 0002:공개키를 찾을 수 없음, 0003:공개키를 발급한 회원사 아님, 0004:복호화 오류, 0005:필수입력값 오류(key_version, key_info 필수값 확인), 0006:대칭키 등록 가능 회원사 아님, 0007:key 중복 오류(현재 및 직전에 사용한 Key 사용 불가), 0008:요청사이트코드와 공개키발급 사이트코드 다름, 0099: 기타오류)")
private String resultCd;
@JsonAlias({"symkey_stat_info"})
@Schema(required = false, title = "JSON값", example = " ", description = "JSON값(회원사에 생성되어 있는 대칭키 버전별 유효기간(result_cd 가 0000, 0007일 경우에 나감)")
private SymkeyStatInfo symkeyStatInfo;
/* ===========================================================================
* CI API
=========================================================================== */
@Setter
private String siteCode; //[필수]사이트코드
@Setter
private String requestNo; //[필수]요청고유번호(이용기관에서 임의 생성한 값)
@Setter
private String key; //[필수]회원사에서 사용할 암호화 KEY
@Setter
private String iv; //[필수]Initial Vector값
@Setter
private String hmacKey; //[필수]회원사에서 사용한 HMAC KEY
}

@ -0,0 +1,30 @@
package cokr.xit.ci.api.service.support.rest.model.conf;
import com.fasterxml.jackson.annotation.JsonAlias;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotEmpty;
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Data
public class DataHeader {
@NotEmpty(message = "응답코드 값이 없습니다.")
@Schema(required = true, title = "응답코드", example = "1200", description = "정상:1200 그외는 오류")
@JsonAlias({"GW_RSLT_CD"})
private String GW_RSLT_CD;
@NotEmpty(message = "응답메시지 값이 없습니다.")
@Schema(required = true, title = "응답메시지", example = " ", description = "한글 또는 영문")
@JsonAlias({"GW_RSLT_MSG"})
private String GW_RSLT_MSG;
@Schema(required = false, title = "교환ID", example = " ", description = "API 호출 시 요청한 값 그대로 리턴(적용대상: 공개키/대칭키/아이핀CI 요청API)")
@JsonAlias({"TRAN_ID"})
private String TRAN_ID;
}

@ -1,29 +1,32 @@
package cokr.xit.ci.api.service.support.rest.model.conf; package cokr.xit.ci.api.service.support.rest.model.conf;
import cokr.xit.ci.api.service.support.rest.model.TransDTO;
import com.fasterxml.jackson.annotation.JsonAlias; import com.fasterxml.jackson.annotation.JsonAlias;
import lombok.Builder; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter; import lombok.*;
import lombok.ToString;
@Getter @Getter
@ToString @ToString
@Builder @Builder
public class EncData implements TransDTO { @NoArgsConstructor
@AllArgsConstructor
@JsonAlias({"result_cd"}) @Schema(name = "EncData", description = "아이핀 CI 요청 응답DTO의 JSON 데이터")
private String resultCd; //rsp_cd가 P000일때 상세결과코드(0000:처리완료, 0001:대칭키 기간 만료, 0002:대칭키를 찾을 수 없음, 0003:대칭키를 발급한 회원사 아님, 0004:복호화 오류, 0005:필수입력값 오류(integrity_value, enc_data 내 필수값 확인), 0006:데이터 무결성 오류(hmac값 불일치), 0007:정보요청유형 입력값 오류(info_req_type이 1 아님), 0008:주민번호 유효성 오류(생년월일 유효성 및 숫자 아님), 0009:거래요청시간 포멧오류(req_dtim 자릿수 및 숫자 아님), 0099:기타오류) public class EncData {
@JsonAlias({"ci1"}) @JsonAlias({"ci1"})
private String ci1; //연계정보1(Connection Info로 다른 웹사이트간 고객확인용으로 사용) @Schema(required = false, title = "연계정보1", example = " ", description = "Connection Info로 다른 웹사이트간 고객확인용으로 사용")
private String ci1;
@JsonAlias({"ci2"}) @JsonAlias({"ci2"})
private String ci2; //연계정보2(연계정보1의 Key 유출에 대비한 예비값) @Schema(required = false, title = "연계정보2", example = " ", description = "연계정보1의 Key 유출에 대비한 예비값")
private String ci2;
@JsonAlias({"updt_cnt"}) @JsonAlias({"updt_cnt"})
private String updtCnt; //갱신횟수(연계정보 Key 유출될 경우 갱신 횟수(초기값 1세팅)) @Schema(required = false, title = "갱신횟수", example = " ", description = "연계정보 Key 유출될 경우 갱신 횟수(초기값 1세팅)")
private String updtCnt;
@JsonAlias({"tx_unique_no"}) @JsonAlias({"tx_unique_no"})
private String txUniqueNo; //거래고유번호(result_cd가 0000일 경우 NICE에서 제공하는 거래일련번호) @Schema(required = false, title = "거래고유번호", example = " ", description = "result_cd가 0000일 경우 NICE에서 제공하는 거래일련번호")
private String txUniqueNo;
} }

@ -1,27 +1,33 @@
package cokr.xit.ci.api.service.support.rest.model.conf; package cokr.xit.ci.api.service.support.rest.model.conf;
import cokr.xit.ci.api.service.support.rest.model.TransDTO;
import com.fasterxml.jackson.annotation.JsonAlias; import com.fasterxml.jackson.annotation.JsonAlias;
import lombok.Builder; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter; import lombok.*;
import lombok.ToString;
@Getter @Getter
@ToString @ToString
@Builder @Builder
public class SymkeyStatInfo implements TransDTO { @NoArgsConstructor
@AllArgsConstructor
@Schema(name = "SymkeyStatInfo", description = "대칭키 요청 API 응답DTO의 JSON 데이터")
public class SymkeyStatInfo {
@JsonAlias({"cur_symkey_version"}) @JsonAlias({"cur_symkey_version"})
private String curSymkeyVersion; //현재 등록요청한 대칭키 버전 @Schema(required = false, title = "현재 대칭키 버전", example = " ", description = "현재 등록요청한 대칭키 버전")
private String curSymkeyVersion;
@JsonAlias({"cur_valid_dtim"}) @JsonAlias({"cur_valid_dtim"})
private String curValidDtim; //현재 등록된 대칭키 만료일시(YYYYMMDDHH24MISS) @Schema(required = false, title = "현재 대칭키 만료일시", example = " ", description = "현재 등록된 대칭키 만료일시(YYYYMMDDHH24MISS)")
private String curValidDtim;
@JsonAlias({"bef_symkey_version"}) @JsonAlias({"bef_symkey_version"})
private String befSymkeyVersion; //이전 등록된 대칭키 버전 @Schema(required = false, title = "이전 대칭키 버전", example = " ", description = "이전 등록된 대칭키 버전")
private String befSymkeyVersion;
@JsonAlias({"bef_valid_dtim"}) @JsonAlias({"bef_valid_dtim"})
private String befValidDtim; //이전 등록된 대칭키 만료일시(YYYYMMDDHH24MISS) @Schema(required = false, title = "이전 대칭키 만료일시", example = " ", description = "이전 등록된 대칭키 만료일시(YYYYMMDDHH24MISS)")
private String befValidDtim;
} }

@ -0,0 +1,62 @@
package cokr.xit.ci.api.service.support.rest.utils;
import cokr.xit.ci.api.service.support.rest.model.conf.DataBodyPubkeyResp;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class PublicKey {
private volatile static PublicKey instance;
private static DataBodyPubkeyResp data;
// private PubkeyInfo() {
//
// }
//
// public static PubkeyInfo getInstance() {
// if (instance == null)
// synchronized (PubkeyInfo.class) {
// if (instance == null)
// instance = new PubkeyInfo();
// }
//
// return instance;
// }
private PublicKey(DataBodyPubkeyResp data) {
this.data = data;
}
public static PublicKey getInstance() {
return instance;
}
public static PublicKey getInstance(DataBodyPubkeyResp data) {
if(!isValidStat())
synchronized (PublicKey.class) {
if(!isValidStat())
instance = new PublicKey(data);
}
return instance;
}
public static boolean isValidStat() {
if (instance == null)
return false;
if (data == null)
return false;
if (Long.parseLong(data.getValidDtim()) < Long.parseLong(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"))))
return false;
return true;
}
public DataBodyPubkeyResp getData() {
return data;
}
}

@ -0,0 +1,71 @@
package cokr.xit.ci.api.service.support.rest.utils;
import cokr.xit.ci.api.service.support.rest.model.conf.DataBodySymkeyResp;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class SymmetricKey {
private volatile static SymmetricKey instance;
private static DataBodySymkeyResp data;
// private static String key;
// private static String version;
private static Long expireDt;
// private SymkeyInfo() {
//
// }
//
// public static SymkeyInfo getInstance() {
// if (instance == null)
// synchronized (SymkeyInfo.class) {
// if (instance == null)
// instance = new SymkeyInfo();
// }
//
// return instance;
// }
private SymmetricKey(DataBodySymkeyResp data) {
this.data = data;
// this.expireDt = Long.parseLong(LocalDateTime.now().plusMonths(5L).format(DateTimeFormatter.ofPattern("yyyyMMdd")));
this.expireDt = Long.parseLong(LocalDateTime.now().plusDays(1L).format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")));
}
public static SymmetricKey getInstance() {
return instance;
}
public static SymmetricKey getInstance(DataBodySymkeyResp data) {
if(!isValidStat())
synchronized (SymmetricKey.class) {
if(!isValidStat())
instance = new SymmetricKey(data);
}
return instance;
}
public static boolean isValidStat() {
if (instance == null)
return false;
if (data == null)
return false;
if (expireDt < Long.parseLong(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"))))
return false;
return true;
}
public DataBodySymkeyResp getData() {
return data;
}
public Long getExpireDe(){
return expireDt;
}
}

@ -0,0 +1,62 @@
package cokr.xit.ci.api.service.support.rest.utils;
import cokr.xit.ci.api.service.support.rest.model.conf.DataBodyGenerateTokenResp;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class Token {
private volatile static Token instance;
private static DataBodyGenerateTokenResp data;
// private PubkeyInfo() {
//
// }
//
// public static PubkeyInfo getInstance() {
// if (instance == null)
// synchronized (PubkeyInfo.class) {
// if (instance == null)
// instance = new PubkeyInfo();
// }
//
// return instance;
// }
private Token(DataBodyGenerateTokenResp data) {
this.data = data;
}
public static Token getInstance() {
return instance;
}
public static Token getInstance(DataBodyGenerateTokenResp data) {
if(!isValidStat())
synchronized (Token.class) {
if(!isValidStat())
instance = new Token(data);
}
return instance;
}
public static boolean isValidStat() {
if (instance == null)
return false;
if (data == null)
return false;
if (Long.parseLong(data.getExpiredDt()) < Long.parseLong(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"))))
return false;
return true;
}
public DataBodyGenerateTokenResp getData() {
return data;
}
}

@ -0,0 +1,8 @@
package cokr.xit.ci.core.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
@Configuration
@EnableJpaRepositories(basePackages = "cokr.xit.ci")
public class JpaConfig {
}

@ -5,6 +5,7 @@ import lombok.extern.slf4j.Slf4j;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.Optional; import java.util.Optional;
@ -180,4 +181,66 @@ public class DateUtil {
); );
} }
/**
* (date) (day) .
* @param fromDate
* @param toDate
* @return
*/
public static int daysByFromBetweenTo(Date fromDate, Date toDate){
return daysByFromBetweenTo(fromDate.getTime(), toDate.getTime());
}
/**
* (sec) (day) .
* @param fromSec
* @param toSec
* @return
*/
public static int daysByFromBetweenTo(int fromSec, int toSec){
return daysByFromBetweenTo(fromSec*1000L, toSec*1000L);
}
/**
* (ms) (day) .
* @param fromMs
* @param toMs
* @return
*/
public static int daysByFromBetweenTo(long fromMs, long toMs){
return (int) ((fromMs - toMs)/(24*60*60*1000));
}
/**
* (sec) (day) .
* @return
*/
public static int secToDays(int sec){
return msToDays(sec*1000L);
}
/**
* (ms) (day) .
* @return
*/
public static int msToDays(long ms){
return (int) (ms/(24*60*60*1000));
}
/**
* (sec) .
* @param sec
* @param pattern
* @return
*/
public static String addSec(int sec, String pattern){
if("".equals(pattern)||pattern==null)
pattern = "yyyyMMdd";
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis((sec*1000L) + (new Date().getTime()));
Date date = calendar.getTime();
return new SimpleDateFormat(pattern).format(date);
}
} }

@ -12,20 +12,3 @@ spring:
config: config:
activate: activate:
on-profile: local on-profile: local
# =====================================================
# NICE api 계약정보
# =====================================================
nice:
ci:
socket:
site-code: GI72
site-pw: "00000000"
api:
host: https://svc.niceapi.co.kr:22001
generate-token: /digital/niceid/oauth/oauth/token
revoke-token: /digital/niceid/oauth/oauth/revokeById
publickey: /digital/niceid/api/v1.0/common/crypto/publickey
symmetrickey: /digital/niceid/api/v1.0/common/crypto/symmetrickey
ci: /digital/niceid/cert/v1.0/ipin/addinfo/ci

@ -13,21 +13,3 @@ spring:
activate: activate:
on-profile: prod on-profile: prod
# =====================================================
# NICE api 계약정보
# =====================================================
nice:
ci:
socket:
site-code: GI72
site-pw: "00000000"
api:
host: https://svc.niceapi.co.kr:22001
generate-token: /digital/niceid/oauth/oauth/token
revoke-token: /digital/niceid/oauth/oauth/revokeById
publickey: /digital/niceid/api/v1.0/common/crypto/publickey
symmetrickey: /digital/niceid/api/v1.0/common/crypto/symmetrickey
ci: /digital/niceid/cert/v1.0/ipin/addinfo/ci

@ -14,6 +14,39 @@ spring:
pid: pid:
file: app-ci.pid file: app-ci.pid
# ===================================================================================================================================
# Database 설정
# ===================================================================================================================================
h2:
console:
enabled: true
path: /app-ci-db
sql:
init:
# DB 초기화(always / never). Embeded 인메모리DB는 기본적으로 초기화를 수행한다.
mode: always
# Database 벤더(h2,oracle,mysql,postgresql,...)
platform: h2
datasource:
# url: "jdbc:h2:tcp://localhost:19092/~/ens"
# url: jdbc:h2:mem:xitdb?rewriteBatchedStatements=true&profileSQL=true&logger=Slf4JLogger&maxQuerySizeToLog=999999
url: jdbc:h2:file:c:/data/app-ci-db;AUTO_SERVER=TRUE
driver-class-name: org.h2.Driver
username: sa
password:
hikari:
driver-class-name: ${spring.datasource.driver-class-name}
jdbc-url: ${spring.datasource.url}
username: ${spring.datasource.username}
password: ${spring.datasource.password}
jpa:
database-platform: org.hibernate.dialect.H2Dialect
hibernate:
# create / create-drop / update / validate / none
ddl-auto: update
open-in-view: true
# ===================================================== # =====================================================
# Logging 설정 # Logging 설정
# ===================================================== # =====================================================
@ -30,3 +63,25 @@ logging:
root: info root: info
'[org.hibernate.sql]': info '[org.hibernate.sql]': info
# =====================================================
# NICE api 계약정보
# =====================================================
contract:
nice:
ci:
type: rest
socket:
site-code: GI72
site-pw: "00000000"
rest:
host: https://svc.niceapi.co.kr:22001
client-id: "6c3eb1ff-530d-458a-9a6e-e02e3346f679"
client-secret: "960f204ec45bb312b7ad2d6b54b984d9c353b8"
api:
generate-token: /digital/niceid/oauth/oauth/token
revoke-token: /digital/niceid/oauth/oauth/token/revokeById
publickey: /digital/niceid/api/v1.0/common/crypto/publickey
symmetrickey: /digital/niceid/api/v1.0/common/crypto/symmetrickey
ci: /digital/niceid/cert/v1.0/ipin/addinfo/ci

Loading…
Cancel
Save