From a1e4bac73186666d7a820db5f9a5d5b4e431ccd5 Mon Sep 17 00:00:00 2001 From: minkyu1128 Date: Fri, 29 Apr 2022 18:47:43 +0900 Subject: [PATCH] =?UTF-8?q?=EB=82=98=EC=9D=B4=EC=8A=A4=20CI=20=EC=97=B0?= =?UTF-8?q?=EA=B3=84=20REST=20API=20=EB=B0=A9=EC=8B=9D=20=EC=84=9C?= =?UTF-8?q?=EB=B9=84=EC=8A=A4=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cokr/xit/ci/AppCiApplication.java | 3 - .../cokr/xit/ci/api/model/ResponseVO.java | 4 +- .../ci/api/presentation/NiceCiController.java | 6 +- .../xit/ci/api/service/NiceCiService.java | 2 +- .../service/support/rest/MapDeserailizer.java | 57 ++ .../api/service/support/rest/NiceCiApi.java | 567 ++++++++++++++++++ .../service/support/rest/NiceCiApiStruct.java | 35 ++ .../support/rest/code/NiceCiApiCd.java | 140 +++++ .../service/support/rest/model/CiRespDTO.java | 26 + .../rest/model/GenerateTokenRespDTO.java | 28 + .../support/rest/model/PublickeyRespDTO.java | 37 ++ .../rest/model/RevokeTokenRespDTO.java | 16 + .../rest/model/SymmetrickeyRespDTO.java | 27 + .../service/support/rest/model/TransDTO.java | 5 + .../support/rest/model/conf/EncData.java | 29 + .../rest/model/conf/SymkeyStatInfo.java | 27 + .../service/support/{ => socket}/Interop.java | 5 +- .../java/cokr/xit/ci/core/utils/DateUtil.java | 183 ++++++ src/main/resources/application-local.yml | 13 +- src/main/resources/application-prod.yml | 15 +- 20 files changed, 1210 insertions(+), 15 deletions(-) create mode 100644 src/main/java/cokr/xit/ci/api/service/support/rest/MapDeserailizer.java create mode 100644 src/main/java/cokr/xit/ci/api/service/support/rest/NiceCiApi.java create mode 100644 src/main/java/cokr/xit/ci/api/service/support/rest/NiceCiApiStruct.java create mode 100644 src/main/java/cokr/xit/ci/api/service/support/rest/code/NiceCiApiCd.java create mode 100644 src/main/java/cokr/xit/ci/api/service/support/rest/model/CiRespDTO.java create mode 100644 src/main/java/cokr/xit/ci/api/service/support/rest/model/GenerateTokenRespDTO.java create mode 100644 src/main/java/cokr/xit/ci/api/service/support/rest/model/PublickeyRespDTO.java create mode 100644 src/main/java/cokr/xit/ci/api/service/support/rest/model/RevokeTokenRespDTO.java create mode 100644 src/main/java/cokr/xit/ci/api/service/support/rest/model/SymmetrickeyRespDTO.java create mode 100644 src/main/java/cokr/xit/ci/api/service/support/rest/model/TransDTO.java create mode 100644 src/main/java/cokr/xit/ci/api/service/support/rest/model/conf/EncData.java create mode 100644 src/main/java/cokr/xit/ci/api/service/support/rest/model/conf/SymkeyStatInfo.java rename src/main/java/cokr/xit/ci/api/service/support/{ => socket}/Interop.java (95%) create mode 100644 src/main/java/cokr/xit/ci/core/utils/DateUtil.java diff --git a/src/main/java/cokr/xit/ci/AppCiApplication.java b/src/main/java/cokr/xit/ci/AppCiApplication.java index 5c91ffb..ea04460 100644 --- a/src/main/java/cokr/xit/ci/AppCiApplication.java +++ b/src/main/java/cokr/xit/ci/AppCiApplication.java @@ -1,11 +1,8 @@ package cokr.xit.ci; -import jdk.internal.module.Checks; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.context.ApplicationPidFileWriter; import org.springframework.boot.web.servlet.ServletComponentScan; import org.springframework.context.annotation.ComponentScan; diff --git a/src/main/java/cokr/xit/ci/api/model/ResponseVO.java b/src/main/java/cokr/xit/ci/api/model/ResponseVO.java index 33f9200..60b9306 100644 --- a/src/main/java/cokr/xit/ci/api/model/ResponseVO.java +++ b/src/main/java/cokr/xit/ci/api/model/ResponseVO.java @@ -7,9 +7,9 @@ import lombok.*; @Getter @ToString @EqualsAndHashCode -public class ResponseVO { +public class ResponseVO { private ErrCd errCode; private String errMsg; - private Object resultInfo; + private T resultInfo; } diff --git a/src/main/java/cokr/xit/ci/api/presentation/NiceCiController.java b/src/main/java/cokr/xit/ci/api/presentation/NiceCiController.java index 5923b8d..75c707e 100644 --- a/src/main/java/cokr/xit/ci/api/presentation/NiceCiController.java +++ b/src/main/java/cokr/xit/ci/api/presentation/NiceCiController.java @@ -22,9 +22,11 @@ public class NiceCiController { 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; - @Value("${nice.api.ci.site-pw ?: }") +// @Value("${nice.ci.socket.site-pw ?: }") + @Value("${nice.ci.socket.site-pw}") private String SITE_PW; @SuppressWarnings("deprecation") diff --git a/src/main/java/cokr/xit/ci/api/service/NiceCiService.java b/src/main/java/cokr/xit/ci/api/service/NiceCiService.java index 148cd68..94e7829 100644 --- a/src/main/java/cokr/xit/ci/api/service/NiceCiService.java +++ b/src/main/java/cokr/xit/ci/api/service/NiceCiService.java @@ -2,7 +2,7 @@ package cokr.xit.ci.api.service; import cokr.xit.ci.api.code.ErrCd; 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.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; diff --git a/src/main/java/cokr/xit/ci/api/service/support/rest/MapDeserailizer.java b/src/main/java/cokr/xit/ci/api/service/support/rest/MapDeserailizer.java new file mode 100644 index 0000000..2af735e --- /dev/null +++ b/src/main/java/cokr/xit/ci/api/service/support/rest/MapDeserailizer.java @@ -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> { + + @Override @SuppressWarnings("unchecked") + public Map deserialize(JsonElement json, Type typeOfT, + JsonDeserializationContext context) throws JsonParseException { + return (Map) read(json); + } + + public Object read(JsonElement in) { + if(in.isJsonArray()){ //JsonArray 이면... + //JsonArray인 경우 + List list = new ArrayList(); + JsonArray arr = in.getAsJsonArray(); + for (JsonElement anArr : arr) { + //JsonPrimitive 나올 떄까지 for문 + list.add(read(anArr)); + } + return list; + }else if(in.isJsonObject()){ //JsonObject 이면... + Map map = new HashMap(); + JsonObject obj = in.getAsJsonObject(); + Set> entitySet = obj.entrySet(); + for(Map.Entry 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; + } +} diff --git a/src/main/java/cokr/xit/ci/api/service/support/rest/NiceCiApi.java b/src/main/java/cokr/xit/ci/api/service/support/rest/NiceCiApi.java new file mode 100644 index 0000000..82bc33d --- /dev/null +++ b/src/main/java/cokr/xit/ci/api/service/support/rest/NiceCiApi.java @@ -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 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 mParam = new HashMap<>(); + mParam.put("grant_type", "client_credentials"); + mParam.put("scope", "default"); + String jsonStr = gson.toJson(mParam); + + + /* ============================================================================== + * API 호출 + ============================================================================== */ + ResponseEntity 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 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.builder().errCode(ErrCd.OK).errMsg(ErrCd.OK.getCodeNm()).resultInfo(resultInfo).build(); + } catch (Exception e) { + return ResponseVO.builder().errCode(errCd).errMsg(errMsg + " " + errMsg).build(); + } + } + + + //@Override + public ResponseVO 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 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.builder().errCode(ErrCd.OK).errMsg(ErrCd.OK.getCodeNm()).resultInfo(resultInfo).build(); + } catch (Exception e) { + return ResponseVO.builder().errCode(errCd).errMsg(e.getMessage() + " " + errMsg).build(); + } + } + + + //@Override + public ResponseVO 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 mParam = new HashMap<>(); + mParam.put("req_dtim", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"))); + String jsonStr = gson.toJson(mParam); + + + /* ============================================================================== + * API 호출 + ============================================================================== */ + ResponseEntity 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.builder().errCode(ErrCd.OK).errMsg(ErrCd.OK.getCodeNm()).resultInfo(resultInfo).build(); + } catch (Exception e) { + return ResponseVO.builder().errCode(errCd).errMsg(e.getMessage() + " " + errMsg).build(); + } + } + + + //@Override + public ResponseVO 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 mParam = new HashMap<>(); + mParam.put("pubkey_version", pubkeyVersion); + mParam.put("symkey_reg_info", symkeyRegInfo); + String jsonStr = gson.toJson(mParam); + + + /* ============================================================================== + * API 호출 + ============================================================================== */ + ResponseEntity 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.builder().errCode(ErrCd.OK).errMsg(ErrCd.OK.getCodeNm()).resultInfo(resultInfo).build(); + } catch (Exception e) { + return ResponseVO.builder().errCode(errCd).errMsg(e.getMessage() + " " + errMsg).build(); + } + } + + + //@Override + public ResponseVO 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 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 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.builder().errCode(ErrCd.OK).errMsg(ErrCd.OK.getCodeNm()).resultInfo(resultInfo).build(); + } catch (Exception e) { + e.printStackTrace(); + return ResponseVO.builder().errCode(errCd).errMsg(e.getMessage() + " " + errMsg).build(); + } + + + } + + + /** + *
메소드 설명: API 호출
+     * 
+ * + * @param method + * @param url + * @param body + * @param headers + * @return ResponseEntity 요청처리 후 응답객체 + * @author: 박민규 + */ + private ResponseEntity callApi(HttpMethod method, String url, String body, HttpHeaders headers) { + log.debug("param ======================="); + log.debug(body); + + + ResponseEntity 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(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(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(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(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR); + log.error(String.format("call API 기타오류[url =>%s param => %s error => %s]", url, body, e.getMessage())); + } + + //결과 응답 + return responseEntity; + } + +} diff --git a/src/main/java/cokr/xit/ci/api/service/support/rest/NiceCiApiStruct.java b/src/main/java/cokr/xit/ci/api/service/support/rest/NiceCiApiStruct.java new file mode 100644 index 0000000..12d1471 --- /dev/null +++ b/src/main/java/cokr/xit/ci/api/service/support/rest/NiceCiApiStruct.java @@ -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(); + + +} diff --git a/src/main/java/cokr/xit/ci/api/service/support/rest/code/NiceCiApiCd.java b/src/main/java/cokr/xit/ci/api/service/support/rest/code/NiceCiApiCd.java new file mode 100644 index 0000000..aaf965f --- /dev/null +++ b/src/main/java/cokr/xit/ci/api/service/support/rest/code/NiceCiApiCd.java @@ -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; + } +} diff --git a/src/main/java/cokr/xit/ci/api/service/support/rest/model/CiRespDTO.java b/src/main/java/cokr/xit/ci/api/service/support/rest/model/CiRespDTO.java new file mode 100644 index 0000000..8efb7a2 --- /dev/null +++ b/src/main/java/cokr/xit/ci/api/service/support/rest/model/CiRespDTO.java @@ -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 인코딩한 값 + +} diff --git a/src/main/java/cokr/xit/ci/api/service/support/rest/model/GenerateTokenRespDTO.java b/src/main/java/cokr/xit/ci/api/service/support/rest/model/GenerateTokenRespDTO.java new file mode 100644 index 0000000..561a17e --- /dev/null +++ b/src/main/java/cokr/xit/ci/api/service/support/rest/model/GenerateTokenRespDTO.java @@ -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) +} diff --git a/src/main/java/cokr/xit/ci/api/service/support/rest/model/PublickeyRespDTO.java b/src/main/java/cokr/xit/ci/api/service/support/rest/model/PublickeyRespDTO.java new file mode 100644 index 0000000..1cc3b71 --- /dev/null +++ b/src/main/java/cokr/xit/ci/api/service/support/rest/model/PublickeyRespDTO.java @@ -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) + + + + +} diff --git a/src/main/java/cokr/xit/ci/api/service/support/rest/model/RevokeTokenRespDTO.java b/src/main/java/cokr/xit/ci/api/service/support/rest/model/RevokeTokenRespDTO.java new file mode 100644 index 0000000..cf97c5a --- /dev/null +++ b/src/main/java/cokr/xit/ci/api/service/support/rest/model/RevokeTokenRespDTO.java @@ -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: 폐기 실패) + +} diff --git a/src/main/java/cokr/xit/ci/api/service/support/rest/model/SymmetrickeyRespDTO.java b/src/main/java/cokr/xit/ci/api/service/support/rest/model/SymmetrickeyRespDTO.java new file mode 100644 index 0000000..d08ec98 --- /dev/null +++ b/src/main/java/cokr/xit/ci/api/service/support/rest/model/SymmetrickeyRespDTO.java @@ -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일 경우에 나감) + + +} diff --git a/src/main/java/cokr/xit/ci/api/service/support/rest/model/TransDTO.java b/src/main/java/cokr/xit/ci/api/service/support/rest/model/TransDTO.java new file mode 100644 index 0000000..efdb248 --- /dev/null +++ b/src/main/java/cokr/xit/ci/api/service/support/rest/model/TransDTO.java @@ -0,0 +1,5 @@ +package cokr.xit.ci.api.service.support.rest.model; + +public interface TransDTO +{ +} diff --git a/src/main/java/cokr/xit/ci/api/service/support/rest/model/conf/EncData.java b/src/main/java/cokr/xit/ci/api/service/support/rest/model/conf/EncData.java new file mode 100644 index 0000000..a34834e --- /dev/null +++ b/src/main/java/cokr/xit/ci/api/service/support/rest/model/conf/EncData.java @@ -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에서 제공하는 거래일련번호) + +} diff --git a/src/main/java/cokr/xit/ci/api/service/support/rest/model/conf/SymkeyStatInfo.java b/src/main/java/cokr/xit/ci/api/service/support/rest/model/conf/SymkeyStatInfo.java new file mode 100644 index 0000000..61963ea --- /dev/null +++ b/src/main/java/cokr/xit/ci/api/service/support/rest/model/conf/SymkeyStatInfo.java @@ -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) + + +} diff --git a/src/main/java/cokr/xit/ci/api/service/support/Interop.java b/src/main/java/cokr/xit/ci/api/service/support/socket/Interop.java similarity index 95% rename from src/main/java/cokr/xit/ci/api/service/support/Interop.java rename to src/main/java/cokr/xit/ci/api/service/support/socket/Interop.java index ed9e632..287fd4e 100644 --- a/src/main/java/cokr/xit/ci/api/service/support/Interop.java +++ b/src/main/java/cokr/xit/ci/api/service/support/socket/Interop.java @@ -1,4 +1,4 @@ -package cokr.xit.ci.api.service.support; +package cokr.xit.ci.api.service.support.socket; import KISINFO.VNO.VNOInterop; @@ -35,7 +35,10 @@ public class Interop // 인증요청처리 iRtnCI = vnoInterop.fnRequestConnInfo(sSiteCode, sSitePw, sJumin, sFlag); log.info("======================================================================="); + log.info("siteCode=" + sSiteCode); + log.info("sitePw=" + sSitePw); log.info("JID=" + sJumin); + log.info("flag=" + sFlag); log.info("iRtnCI=" + iRtnCI); // 인증결과코드에 따른 처리 diff --git a/src/main/java/cokr/xit/ci/core/utils/DateUtil.java b/src/main/java/cokr/xit/ci/core/utils/DateUtil.java new file mode 100644 index 0000000..9a9469b --- /dev/null +++ b/src/main/java/cokr/xit/ci/core/utils/DateUtil.java @@ -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 { + + + + + + + + /** + *
메소드 설명: 절대시간(단위: second)을 현재 시간으로 반환 한다.
+ * @param sec + * @param fmt + * @return String 요청처리 후 응답객체 + * @author: 박민규 + * @date: 2021. 11. 19. + */ + public static String absTimeSecToDate(int sec, String fmt) { + return absTimeSecToDate(sec * 1L, fmt); + } + /** + *
메소드 설명: 절대시간(단위: second)을 현재 시간으로 반환 한다.
+ * @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); + } + /** + *
메소드 설명: 절대시간(단위: ms)을 현재 시간으로 반환 한다.
+ * @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); + } + + + /** + *
메소드 설명: 시간을 절대시간(단위: sec)로 반환 한다.
+ * @param expireDt 만료일시(yyyyMMddHHmmss) + * @return Long 요청처리 후 응답객체 + * @author: 박민규 + * @date: 2021. 11. 19. + */ + public static Long dateToAbsTimeSec(String expireDt) { + return dateToAbsTime(expireDt)/1000; + } + /** + *
메소드 설명: 시간을 절대시간(단위: sec)로 반환 한다.
+ * @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; + } + /** + *
메소드 설명: 시간을 절대시간(단위: ms)로 반환 한다.
+ * @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); + } + + } + /** + *
메소드 설명: 시간을 절대시간(단위: ms)로 반환 한다.
+ * @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(); + } + + /** + *
메소드 설명: 일수를 상대시간(단위: ms)로 반환 한다.
+ * @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; + } + + /** + *
메소드 설명: 일시(yyyyMMddHHmmss)를 LocalDateTime 으로 반환 한다.
+ * @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)) + ); + } + +} diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml index 57cb653..406df70 100644 --- a/src/main/resources/application-local.yml +++ b/src/main/resources/application-local.yml @@ -18,7 +18,14 @@ spring: # NICE api 계약정보 # ===================================================== nice: - api: - ci: + ci: + socket: 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 diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 9f58cf8..51e058f 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -18,7 +18,16 @@ spring: # NICE api 계약정보 # ===================================================== nice: - api: - ci: + ci: + socket: 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 + +