diff --git a/mens-api/src/main/java/kr/xit/core/spring/config/SpringDocsApiConfig.java b/mens-api/src/main/java/kr/xit/core/spring/config/SpringDocsApiConfig.java index c27fa0a..adec7d6 100644 --- a/mens-api/src/main/java/kr/xit/core/spring/config/SpringDocsApiConfig.java +++ b/mens-api/src/main/java/kr/xit/core/spring/config/SpringDocsApiConfig.java @@ -45,10 +45,20 @@ public class SpringDocsApiConfig { .build(); } + @Bean + public GroupedOpenApi niceCi() { + return GroupedOpenApi.builder() + .group("3. Nice CI API") + .pathsToMatch( + "/api/nice/v1/**" + ) + .build(); + } + @Bean public GroupedOpenApi bizDoc() { return GroupedOpenApi.builder() - .group("3. 전자고지 카카오문서 확인 API") + .group("6. 전자고지 문서 확인 API") .pathsToMatch( "/api/ens/v1/**" ) diff --git a/mens-api/src/main/java/kr/xit/ens/support/cmm/service/CmmEnsFileService.java b/mens-api/src/main/java/kr/xit/ens/support/cmm/service/CmmEnsFileService.java index bb8fd49..7fa7d08 100644 --- a/mens-api/src/main/java/kr/xit/ens/support/cmm/service/CmmEnsFileService.java +++ b/mens-api/src/main/java/kr/xit/ens/support/cmm/service/CmmEnsFileService.java @@ -1,13 +1,10 @@ package kr.xit.ens.support.cmm.service; -import java.io.File; -import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Iterator; import java.util.List; -import java.util.Random; import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; @@ -17,11 +14,10 @@ import javax.validation.Validator; import kr.xit.biz.ens.model.cmm.CmmEnsFileDTO.FmcExcel; import kr.xit.biz.ens.model.cmm.CmmEnsFileDTO.FmcExcelUpload; import kr.xit.core.exception.BizRuntimeException; -import kr.xit.core.spring.util.ApiWebClient; +import kr.xit.core.spring.util.ApiWebClientUtil; import kr.xit.core.support.xlsx.StreamingReader; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; @@ -51,7 +47,7 @@ public class CmmEnsFileService extends EgovAbstractServiceImpl implements ICmmEn private final static int FMC_EXCEL_DATA_START_ROW = 3; private final static int FMC_EXCEL_CELL_CNT = 7; - private final ApiWebClient webClient; + private final ApiWebClientUtil webClient; private static final Validator validator = Validation.buildDefaultValidatorFactory().getValidator(); @Override diff --git a/mens-api/src/main/java/kr/xit/ens/support/nice/service/INiceCiService.java b/mens-api/src/main/java/kr/xit/ens/support/nice/service/INiceCiService.java new file mode 100644 index 0000000..2d4a90f --- /dev/null +++ b/mens-api/src/main/java/kr/xit/ens/support/nice/service/INiceCiService.java @@ -0,0 +1,24 @@ +package kr.xit.ens.support.nice.service; + +import kr.xit.biz.ens.model.nice.NiceCiDTO.TokenRequest; +import kr.xit.biz.ens.model.nice.NiceCiDTO.TokenResponse; + +/** + *
+ * description :
+ *
+ * packageName : kr.xit.ens.support.nice.service
+ * fileName    : INiceCiService
+ * author      : limju
+ * date        : 2023-09-06
+ * ======================================================================
+ * 변경일         변경자        변경 내용
+ * ----------------------------------------------------------------------
+ * 2023-09-06    limju       최초 생성
+ *
+ * 
+ */ +public interface INiceCiService { + TokenResponse generateToken(final TokenRequest reqDTO); + TokenResponse revokeToken(final String token); +} diff --git a/mens-api/src/main/java/kr/xit/ens/support/nice/service/NiceCiService.java b/mens-api/src/main/java/kr/xit/ens/support/nice/service/NiceCiService.java new file mode 100644 index 0000000..6f2af27 --- /dev/null +++ b/mens-api/src/main/java/kr/xit/ens/support/nice/service/NiceCiService.java @@ -0,0 +1,84 @@ +package kr.xit.ens.support.nice.service; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import kr.xit.biz.ens.model.kakao.KkopayDocBulkDTO.BulkSendResponses; +import kr.xit.biz.ens.model.nice.NiceCiDTO.TokenRequest; +import kr.xit.biz.ens.model.nice.NiceCiDTO.TokenResponse; +import kr.xit.core.spring.util.ApiWebClientUtil; +import kr.xit.core.support.utils.JsonUtils; +import lombok.RequiredArgsConstructor; +import org.apache.logging.log4j.util.Base64Util; +import org.egovframe.rte.fdl.cmmn.EgovAbstractServiceImpl; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; + +/** + *
+ * description :
+ *
+ * packageName : kr.xit.ens.support.nice.service
+ * fileName    : NiceCiService
+ * author      : limju
+ * date        : 2023-09-06
+ * ======================================================================
+ * 변경일         변경자        변경 내용
+ * ----------------------------------------------------------------------
+ * 2023-09-06    limju       최초 생성
+ *
+ * 
+ */ +@RequiredArgsConstructor +@Service +public class NiceCiService extends EgovAbstractServiceImpl implements INiceCiService { + @Value("${contract.nice.host}") + private String HOST; + @Value("${contract.nice.client-id}") + private String CLIENT_ID; + @Value("${contract.nice.client-secret}") + private String CLIENT_SECRET; + @Value("${contract.nice.api.generate-token}") + private String API_GENERATE_TOKEN; + @Value("${contract.nice.api.revoke-token}") + private String API_REVOKE_TOKEN; + @Value("${contract.nice.api.publickey}") + private String API_PUBLICKEY; + @Value("${contract.nice.api.symmetrickey}") + private String API_SYMMETRICKEY; + @Value("${contract.nice.api.ci}") + private String API_CI; + + private final ApiWebClientUtil webClient; + + public TokenResponse generateToken(final TokenRequest reqDTO){ + return webClient.exchange(HOST + API_GENERATE_TOKEN, HttpMethod.POST, JsonUtils.toJson(reqDTO), TokenResponse.class, getHeaderMap()); + } + + public TokenResponse revokeToken(final String token){ + Map map = new HashMap<>(); + map.put(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE); + map.put(HttpHeaders.AUTHORIZATION, String.format("Basic %s", Base64Util.encode(String.format("%s:%s:%s", token, (new Date().getTime() / 1000), this.CLIENT_SECRET)))); + return webClient.exchange(HOST + API_GENERATE_TOKEN, HttpMethod.POST, null, TokenResponse.class, getHeaderMap()); + } + + + + //------------------------------------------------------------------------------- + private Map getHeaderMap(){ + Map map = new HashMap<>(); + map.put(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE); + map.put(HttpHeaders.AUTHORIZATION, String.format("Basic %s", Base64Util.encode(String.format("%s:%s", this.CLIENT_ID, this.CLIENT_SECRET)))); + return map; + } +} diff --git a/mens-api/src/main/java/kr/xit/ens/support/nice/web/NiceCiController.java b/mens-api/src/main/java/kr/xit/ens/support/nice/web/NiceCiController.java new file mode 100644 index 0000000..2375770 --- /dev/null +++ b/mens-api/src/main/java/kr/xit/ens/support/nice/web/NiceCiController.java @@ -0,0 +1,54 @@ +package kr.xit.ens.support.nice.web; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import kr.xit.biz.ens.model.kakao.KkopayDocDTO.ValidTokenRequest; +import kr.xit.biz.ens.model.nice.NiceCiDTO.TokenRequest; +import kr.xit.core.model.ApiResponseDTO; +import kr.xit.ens.support.nice.service.INiceCiService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +/** + *
+ * description :
+ *
+ * packageName : kr.xit.ens.support.nice.web
+ * fileName    : NiceCiController
+ * author      : limju
+ * date        : 2023-09-06
+ * ======================================================================
+ * 변경일         변경자        변경 내용
+ * ----------------------------------------------------------------------
+ * 2023-09-06    limju       최초 생성
+ *
+ * 
+ */ +@Tag(name = "NiceCiController", description = "Nice CI API") +@RequiredArgsConstructor +@RestController +@RequestMapping(value = "/api/nice/v1") +public class NiceCiController { + private final INiceCiService service; + + @Operation(summary = "기관용 토큰 발급 요청", description = "기관용 토큰 발급 요청") + @PostMapping(value = "/generateToken", produces = MediaType.APPLICATION_JSON_VALUE) + public ApiResponseDTO generateToken( + @RequestBody final TokenRequest reqDTO + ) { + return ApiResponseDTO.success(service.generateToken(reqDTO)); + } + + @Operation(summary = "기관용 토큰 폐기", description = "기관용 토큰 폐기") + @PostMapping(value = "/revokeToken", produces = MediaType.APPLICATION_JSON_VALUE) + public ApiResponseDTO revokeToken( + @RequestParam final String accessToken + ) { + return ApiResponseDTO.success(service.revokeToken(accessToken)); + } +} diff --git a/mens-core/src/main/java/kr/xit/biz/ens/model/nice/NiceCiDTO.java b/mens-core/src/main/java/kr/xit/biz/ens/model/nice/NiceCiDTO.java new file mode 100644 index 0000000..ae53e18 --- /dev/null +++ b/mens-core/src/main/java/kr/xit/biz/ens/model/nice/NiceCiDTO.java @@ -0,0 +1,205 @@ +package kr.xit.biz.ens.model.nice; + +import com.fasterxml.jackson.annotation.JsonInclude; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.media.Schema.RequiredMode; +import java.util.List; +import javax.validation.Valid; +import javax.validation.constraints.Digits; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import kr.xit.biz.common.ApiConstants; +import kr.xit.core.model.IApiResponse; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +/** + *
+ * description : Nice CI DTO
+ *
+ * packageName : kr.xit.biz.ens.model.nice
+ * fileName    : NiceCiDTO
+ * author      : limju
+ * date        : 2023-09-06
+ * ======================================================================
+ * 변경일         변경자        변경 내용
+ * ----------------------------------------------------------------------
+ * 2023-09-06    limju       최초 생성
+ *
+ * 
+ */ +public class NiceCiDTO { + + /** + *
+     * 기관용 Token(50년 유효) 발급 요청
+     * url : /digital/niceid/oauth/oauth/token
+     * content-type : application/x-www-form-urlencoded
+     * Authorization : Basic + Base64Encoding(access_token:current_timestamp:client_id)
+     * - access_token : 만료할 access_token
+     * - client_id : access_token발급에 사용된 client_id
+     * - current_timestamp
+     *   Date currentDate = new Date();
+     *   long current_timestamp = currentDate.getTime() /1000
+     * 
+ */ + @Schema(name = "Token", description = "기관용 Token(50년 유효) 발급 요청 파라메터 DTO") + @Data + @NoArgsConstructor + @AllArgsConstructor + @SuperBuilder + @JsonInclude(JsonInclude.Include.NON_NULL) + public static class TokenRequest { + /** + * default 로 고정 + */ + @Schema(requiredMode = RequiredMode.REQUIRED, title = "scope", example = "default") + @NotBlank(message = "scope는 필수입니다") + private String scope; + + /** + * clinet_credentials 로 고정 + */ + @Schema(requiredMode = RequiredMode.REQUIRED, title = "grant_type", example = "clinet_credentials") + @NotBlank(message = "grant_type은 필수입니다") + private String grant_type; + } + + /** + *
+     * 기관용 Token(50년 유효) 발급 요청 응답
+     * url : /digital/niceid/oauth/oauth/token
+     * content-type : application/json
+     * 
+ */ + @Schema(name = "TokenResponse", description = "기관용 Token(50년 유효) 발급 요청 결과 DTO") + @Data + @NoArgsConstructor + @AllArgsConstructor + @SuperBuilder + @JsonInclude(JsonInclude.Include.NON_NULL) + public static class TokenResponse implements IApiResponse { + @Schema(requiredMode = RequiredMode.REQUIRED) + private DataHeader dataHeader; + + @Schema(requiredMode = RequiredMode.REQUIRED) + private DataBody dataBody; + } + + /** + *
+     * 기관용 Token 폐기 응답
+     * 요청
+     * url : /digital/niceid/oauth/oauth/token/revokeById
+     * Authorization : Basic + Base64Encoding(access_token:current_timestamp:client_id)
+     * - access_token : 만료할 access_token
+     * - client_id : access_token발급에 사용된 client_id
+     * - current_timestamp
+     *   Date currentDate = new Date();
+     *   long current_timestamp = currentDate.getTime() /1000
+     * 
+ */ + @Schema(name = "TokenRevokeResponse", description = "기관용 Token 폐기 요청 결과 DTO") + @Data + @NoArgsConstructor + @AllArgsConstructor + @SuperBuilder + @JsonInclude(JsonInclude.Include.NON_NULL) + public static class TokenRevokeResponse implements IApiResponse { + + @Schema(requiredMode = RequiredMode.REQUIRED) + private DataHeader dataHeader; + + } + + /** + * 기관용 토큰 발급 응답 dataHeader + */ + @Schema(name = "DataHeader", description = "TokenResponse dataHeader DTO") + @Data + @NoArgsConstructor + @AllArgsConstructor + @SuperBuilder + @JsonInclude(JsonInclude.Include.NON_NULL) + public static class DataHeader { + /** + * 응답코드 + * 정상 : 1200, 그외 오류 + */ + @Schema(requiredMode = RequiredMode.REQUIRED, title = "응답코드", example = "1200") + @NotBlank + private String GW_RSLT_CD; + + /** + * 응답메세지 + */ + @Schema(requiredMode = RequiredMode.REQUIRED, title = "응답메세지", example = "오류없음") + @NotBlank + private String GW_RSLT_MSG; + + /** + * TRAN_ID + */ + @Schema(requiredMode = RequiredMode.AUTO, title = "TRAN_ID", example = "20230906120000") + private String TRAN_ID; + } + + /** + * 기관용 토큰 발급 응답 dataHeader + */ + @Schema(name = "DataBody", description = "TokenResponse dataBody DTO") + @Data + @NoArgsConstructor + @AllArgsConstructor + @SuperBuilder + @JsonInclude(JsonInclude.Include.NON_NULL) + public static class DataBody { + //----------------------------------------------------------------------- + // 토근 발급 요청시 필수 + //----------------------------------------------------------------------- + /** + * 사용자 엑세스 토큰 값 : token 발급시 필수 + * 모든 API 요청시 헤더에 access_token을 포함하여 전송 + */ + @Schema(requiredMode = RequiredMode.AUTO, title = "사용자 엑세스 토큰 값", example = " ") + @NotBlank + private String access_token; + + /** + * token_type : token 발급시 필수 + * bearer로 고정 + */ + @Schema(requiredMode = RequiredMode.AUTO, title = "token_type", example = "bearer") + @NotBlank + private String token_type; + + /** + * access 토큰 만료 시간(초) : token 발급시 필수 + */ + @Schema(requiredMode = RequiredMode.AUTO, title = "access 토큰 만료 시간(초)", example = "1.57698305E9") + @NotBlank + private int expires_in; + + /** + * 요청한 scope 값 : token 발급시 필수 + * 기본 : default + */ + @Schema(requiredMode = RequiredMode.AUTO, title = "요청한 scope 값", example = "default") + @NotBlank + private String scope; + + //----------------------------------------------------------------------- + // 토큰 폐기 요청시 필수 + //----------------------------------------------------------------------- + /** + * token 폐기시 필수 + * true or false + */ + @Schema(requiredMode = RequiredMode.AUTO) + @NotBlank + private boolean result; + } +} diff --git a/mens-core/src/main/java/kr/xit/core/support/utils/SecureUtils.java b/mens-core/src/main/java/kr/xit/core/support/utils/SecureUtils.java new file mode 100644 index 0000000..b2f7416 --- /dev/null +++ b/mens-core/src/main/java/kr/xit/core/support/utils/SecureUtils.java @@ -0,0 +1,52 @@ +package kr.xit.core.support.utils; + +import java.io.IOException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import kr.xit.core.exception.BizRuntimeException; + +/** + *
+ * description :
+ *
+ * packageName : kr.xit.core.support.utils
+ * fileName    : SecureUtils
+ * author      : limju
+ * date        : 2023-09-06
+ * ======================================================================
+ * 변경일         변경자        변경 내용
+ * ----------------------------------------------------------------------
+ * 2023-09-06    limju       최초 생성
+ *
+ * 
+ */ +public class SecureUtils { + /** + * sha256 암호화 + * + * @param text + * @return + * @throws IOException + * @throws NoSuchAlgorithmException + */ + public static String hexSha256(String text) { + StringBuffer sbuf = new StringBuffer(); + + try { + MessageDigest mDigest = MessageDigest.getInstance("SHA-256"); + mDigest.update(text.getBytes()); + + byte[] msgStr = mDigest.digest(); + + for(int i = 0; i < msgStr.length; i++) { + byte tmpStrByte = msgStr[i]; + String tmpEncTxt = Integer.toString((tmpStrByte & 0xff) + 0x100, 16).substring(1); + + sbuf.append(tmpEncTxt); + } + } catch (NoSuchAlgorithmException nae){ + throw BizRuntimeException.create(nae.getMessage()); + } + return sbuf.toString(); + } +}