나이스 CI 연계 REST API 방식 서비스추가

master
minkyu1128 3 years ago
parent 51b8e4f048
commit a1e4bac731

@ -1,11 +1,8 @@
package cokr.xit.ci; package cokr.xit.ci;
import jdk.internal.module.Checks;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
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;

@ -7,9 +7,9 @@ import lombok.*;
@Getter @Getter
@ToString @ToString
@EqualsAndHashCode @EqualsAndHashCode
public class ResponseVO { public class ResponseVO<T> {
private ErrCd errCode; private ErrCd errCode;
private String errMsg; private String errMsg;
private Object resultInfo; private T resultInfo;
} }

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

@ -2,7 +2,7 @@ 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.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.stereotype.Component; import org.springframework.stereotype.Component;

@ -0,0 +1,57 @@
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;
}
}

@ -0,0 +1,567 @@
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,35 @@
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,140 @@
package cokr.xit.ci.api.service.support.rest.code;
public enum NiceCiApiCd {
UNKNOWN("알수없음")
/* =======================================================================
* HTTP
* -. HTTP prefix "HTTP_"
======================================================================= */
, HTTP_200("No Error")
, HTTP_400("Bad Request or Inavalid Token")
, HTTP_401("Authorized required")
, HTTP_402("unAuthorized")
, HTTP_403("service Disabled")
, HTTP_404("Service Not Found")
, HTTP_500("Internal Server Error")
, HTTP_501("Access Denied by Protected Service")
, HTTP_502("Bad Response from Protected Service")
, HTTP_503("Service Temporarily Unavailable")
/* =======================================================================
* APIM (dataHeader)
* -. Data Header prefix "HEAD_"
======================================================================= */
, HEAD_1200("오류 없음 (정상)")
, HEAD_1300("request body가 비었습니다.")
, HEAD_1400("잘못된 요청")
, HEAD_1401("인증 필요")
, HEAD_1402("권한없음")
, HEAD_1403("서비스 사용 중지됨")
, HEAD_1404("서비스를 찾을 수 없음")
, HEAD_1500("서버 내부 오류")
, HEAD_1501("보호된 서비스에서 엑세스가 거부되었습니다.")
, HEAD_1502("보호된 서비스에서 응답이 잘못되었습니다.")
, HEAD_1503("일시적으로 사용할 수 없는 서비스")
, HEAD_1700("엑세스가 허용되지 않습니다. - Client ID")
, HEAD_1701("엑세스가 허용되지 않습니다. - Service URI")
, HEAD_1702("엑세스가 허용되지 않습니다. - Client ID + Client IP")
, HEAD_1703("엑세스가 허용되지 않습니다. - Client ID + Service URI")
, HEAD_1705("엑세스가 허용되지 않습니다. - Client ID + Black List Client IP")
, HEAD_1706("액세스가 허용되지 않습니다 - Client ID + Product Code")
, HEAD_1711("거래제한된 요일입니다.")
, HEAD_1712("거래제한된 시간입니다.")
, HEAD_1713("거래제한된 요일/시간입니다.")
, HEAD_1714("거래제한된 일자입니다.")
, HEAD_1715("거래제한된 일자/시간입니다.")
, HEAD_1716("공휴일 거래가 제한된 서비스입니다.")
, HEAD_1717("SQL인젝션, XSS방어")
, HEAD_1800("잘못된 토큰")
, HEAD_1801("잘못된 클라이언트 정보")
, HEAD_1900("초과된 연결 횟수")
, HEAD_1901("초과 된 토큰 조회 실패")
, HEAD_1902("초과된 토큰 체크 실패")
, HEAD_1903("초과된 접속자 수 ")
, HEAD_1904("전송 크기 초과")
, HEAD_1905("접속량이 너무 많음")
, HEAD_1906("상품이용 한도 초과")
, HEAD_1907("API 이용 주기 초과")
, HEAD_1908("상품 이용 주기 초과")
/* =======================================================================
* (rsp_cd)
======================================================================= */
, P000("정상응답")
, S603("내부 DB 오류")
, P013("이용기관 개시상태 아님")
, E998("서비스 권한 오류")
, E999("내부시스템 오류")
, Exxx("기타시스템 오류")
/* =======================================================================
* APIM (dataBody)
======================================================================= */
, EAPI2500("맵핑정보 없음 - {0}")
, EAPI2510("요청맵핑 데이터가 없습니다.")
, EAPI2530("응답전문 맵핑 오류")
, EAPI2540("대응답 정보 없음")
, EAPI2550("숫자타입 입력 오류")
, EAPI2560("실수타입 입력 오류")
, EAPI2561("실수형 타입 길이정보 문법 에러 ( 형식 : \"전체길이,실수부길이\")")
, EAPI2562("실수형 타입 논리 에러 ( 전체 길이는 소수부 길이보다 커야합니다. )")
, EAPI2563("실수형 타입 파싱 에러( 입력값을 실수값으로 변환할 수 없습니다. )")
, EAPI2564("실수형 타입 정수부 길이 에러")
, EAPI2565("실수형 타입 소수부 길이 에러")
, EAPI2600("내부 시스템 오류")
, EAPI2700("외부 시스템 연동 오류")
, EAPI2701("타임아웃이 발생하였습니다.")
, EAPI2702("DISCONNECTION_OK")
, EAPI2703("DISCONNECTION_FAIL")
, EAPI2704("RESULT_OK")
, EAPI2705("RESULT_FAIL")
, EAPI2892("반복부 카운터 에러(지정된 건수보다 크거나 작습니다)")
, EAPI5001("schema 검증 정보가 없습니다.")
, EAPI5002("schema 검증 실패")
/* =======================================================================
*
* -. prefix "DRSLT_"
======================================================================= */
, DRSLT_0000("공개키 발급")
, DRSLT_0001("필수입력값 오류")
, DRSLT_0003("공개키 발급 대상 회원사 아님")
, DRSLT_0099("기타오류")
;
private final String code; //코드
private final String codeNm; //코드명
NiceCiApiCd(String codeNm) {
this.code = this.name();
this.codeNm = codeNm;
}
public String getCode() {
return this.code;
}
public String getCodeNm() {
return this.codeNm;
}
public static NiceCiApiCd valueOfEnum(String code){
if(code == null)
return NiceCiApiCd.UNKNOWN;
NiceCiApiCd ensErrCd = null;
try {
ensErrCd = NiceCiApiCd.valueOf(code);
} catch (IllegalArgumentException e){
ensErrCd = NiceCiApiCd.UNKNOWN;
}
return ensErrCd;
}
}

@ -0,0 +1,26 @@
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 인코딩한 값
}

@ -0,0 +1,28 @@
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,37 @@
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)
}

@ -0,0 +1,16 @@
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: 폐기 실패)
}

@ -0,0 +1,27 @@
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일 경우에 나감)
}

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

@ -0,0 +1,29 @@
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 lombok.Builder;
import lombok.Getter;
import lombok.ToString;
@Getter
@ToString
@Builder
public class EncData implements TransDTO {
@JsonAlias({"result_cd"})
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:기타오류)
@JsonAlias({"ci1"})
private String ci1; //연계정보1(Connection Info로 다른 웹사이트간 고객확인용으로 사용)
@JsonAlias({"ci2"})
private String ci2; //연계정보2(연계정보1의 Key 유출에 대비한 예비값)
@JsonAlias({"updt_cnt"})
private String updtCnt; //갱신횟수(연계정보 Key 유출될 경우 갱신 횟수(초기값 1세팅))
@JsonAlias({"tx_unique_no"})
private String txUniqueNo; //거래고유번호(result_cd가 0000일 경우 NICE에서 제공하는 거래일련번호)
}

@ -0,0 +1,27 @@
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 lombok.Builder;
import lombok.Getter;
import lombok.ToString;
@Getter
@ToString
@Builder
public class SymkeyStatInfo implements TransDTO {
@JsonAlias({"cur_symkey_version"})
private String curSymkeyVersion; //현재 등록요청한 대칭키 버전
@JsonAlias({"cur_valid_dtim"})
private String curValidDtim; //현재 등록된 대칭키 만료일시(YYYYMMDDHH24MISS)
@JsonAlias({"bef_symkey_version"})
private String befSymkeyVersion; //이전 등록된 대칭키 버전
@JsonAlias({"bef_valid_dtim"})
private String befValidDtim; //이전 등록된 대칭키 만료일시(YYYYMMDDHH24MISS)
}

@ -1,4 +1,4 @@
package cokr.xit.ci.api.service.support; package cokr.xit.ci.api.service.support.socket;
import KISINFO.VNO.VNOInterop; import KISINFO.VNO.VNOInterop;
@ -35,7 +35,10 @@ public class Interop
// 인증요청처리 // 인증요청처리
iRtnCI = vnoInterop.fnRequestConnInfo(sSiteCode, sSitePw, sJumin, sFlag); iRtnCI = vnoInterop.fnRequestConnInfo(sSiteCode, sSitePw, sJumin, sFlag);
log.info("======================================================================="); log.info("=======================================================================");
log.info("siteCode=" + sSiteCode);
log.info("sitePw=" + sSitePw);
log.info("JID=" + sJumin); log.info("JID=" + sJumin);
log.info("flag=" + sFlag);
log.info("iRtnCI=" + iRtnCI); log.info("iRtnCI=" + iRtnCI);
// 인증결과코드에 따른 처리 // 인증결과코드에 따른 처리

@ -0,0 +1,183 @@
package cokr.xit.ci.core.utils;
import lombok.extern.slf4j.Slf4j;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.Optional;
@Slf4j
public class DateUtil {
/**
* <pre> : (: second) .</pre>
* @param sec
* @param fmt
* @return String
* @author:
* @date: 2021. 11. 19.
*/
public static String absTimeSecToDate(int sec, String fmt) {
return absTimeSecToDate(sec * 1L, fmt);
}
/**
* <pre> : (: second) .</pre>
* @param sec
* @param fmt
* @return String
* @author:
* @date: 2021. 11. 19.
*/
public static String absTimeSecToDate(Long sec, String fmt) {
if(sec==null)
return null;
return absTimeToDate(sec * 1000L, fmt);
}
/**
* <pre> : (: ms) .</pre>
* @param millionSec
* @param fmt
* @return String
* @author:
* @date: 2021. 11. 19.
*/
public static String absTimeToDate(Long millionSec, String fmt) {
if(fmt == null || "".equals(fmt)) fmt = "yyyy-MM-dd HH:mm:ss";
Date date = new Date(millionSec);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(fmt);
return simpleDateFormat.format(date);
}
/**
* <pre> : (: sec) .</pre>
* @param expireDt (yyyyMMddHHmmss)
* @return Long
* @author:
* @date: 2021. 11. 19.
*/
public static Long dateToAbsTimeSec(String expireDt) {
return dateToAbsTime(expireDt)/1000;
}
/**
* <pre> : (: sec) .</pre>
* @param year
* @param month
* @param day
* @param hour
* @param minute
* @param sec
* @return Long
* @author:
* @date: 2021. 11. 19.
*/
public static Long dateToAbsTimeSec(int year, int month, int day, int hour, int minute, int sec) {
return dateToAbsTime(year, month, day, hour, minute, sec)/1000;
}
/**
* <pre> : (: ms) .</pre>
* @param expireDt (yyyyMMddHHmmss)
* @return Long
* @author:
* @date: 2021. 11. 19.
*/
@SuppressWarnings("finally")
public static Long dateToAbsTime(String expireDt) {
if(expireDt==null)
return 0L;
expireDt = expireDt.replaceAll("[^0-9]", "");
if("".equals(expireDt))
return 0L;
int year, month, day, hour, minute, sec;
year = month = day = hour = minute = sec = 0;
try {
year = Integer.parseInt(expireDt.substring(0,4));
month = Integer.parseInt(expireDt.substring(4,6));
day = Integer.parseInt(expireDt.substring(6,8));
hour = Integer.parseInt(expireDt.substring(8,10));
minute = Integer.parseInt(expireDt.substring(10,12));
sec = Integer.parseInt(expireDt.substring(12,14));
} catch(Exception e) {
log.error(String.format("dateToAbsTime fail... expireDt is [%s]", expireDt));
} finally {
return dateToAbsTime(year, month, day, hour, minute, sec);
}
}
/**
* <pre> : (: ms) .</pre>
* @param year
* @param month
* @param day
* @param hour
* @param minute
* @param sec
* @return Long
* @author:
* @date: 2021. 11. 19.
*/
@SuppressWarnings("deprecation")
public static Long dateToAbsTime(int year, int month, int day, int hour, int minute, int sec) {
if(month < 0) month = 1;
if(month > 11) month = 12;
if(day < 1 || day > 31) day = 1;
if(hour < 0 || hour > 23) hour = 0;
if(minute < 0 || minute > 59) minute = 0;
if(sec < 0 || sec > 59) sec = 0;
Date curDate = new Date (year-1900, month-1, day, hour, minute, sec);
return curDate.getTime();
}
/**
* <pre> : (: ms) .</pre>
* @param days
* @return Long
* @author:
* @date: 2021. 11. 19.
*/
public static Long daysToRelTime(int days) {
if(days<0) days=0;
return days * 24L * 60L * 60L * 1000L;
}
/**
* <pre> : (yyyyMMddHHmmss) LocalDateTime .</pre>
* @param yyyyMMddHHmmss
* @return Long
* @author:
* @date: 2022. 3. 15.
*/
public static LocalDateTime toLocalDateTime(String yyyyMMddHHmmss){
String date = Optional.ofNullable(yyyyMMddHHmmss).orElseGet(()->null);
if(date==null) return null;
date = date.replaceAll("[^0-9]", "");
if(date.length()!=14)
return null;
return LocalDateTime.of(
Integer.parseInt(date.substring(0,4))
, Integer.parseInt(date.substring(4,6))
, Integer.parseInt(date.substring(6,8))
, Integer.parseInt(date.substring(8,10))
, Integer.parseInt(date.substring(10,12))
, Integer.parseInt(date.substring(12,14))
);
}
}

@ -18,7 +18,14 @@ spring:
# NICE api 계약정보 # NICE api 계약정보
# ===================================================== # =====================================================
nice: nice:
api: ci:
ci: socket:
site-code: GI72 site-code: GI72
site-pw: 00000000 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

@ -18,7 +18,16 @@ spring:
# NICE api 계약정보 # NICE api 계약정보
# ===================================================== # =====================================================
nice: nice:
api: ci:
ci: socket:
site-code: GI72 site-code: GI72
site-pw: 00000000 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

Loading…
Cancel
Save