From 700819073bc6bd8db87b48de290362adf01d55c0 Mon Sep 17 00:00:00 2001 From: mjkhan21 Date: Tue, 31 Oct 2023 15:19:03 +0900 Subject: [PATCH] =?UTF-8?q?=EC=B5=9C=EC=B4=88=20=EC=BB=A4=EB=B0=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 147 ++++++++++++++ .../interfaces/publicinfo/Configuration.java | 139 +++++++++++++ .../interfaces/publicinfo/ServiceClient.java | 69 +++++++ .../interfaces/publicinfo/ServiceMessage.java | 187 ++++++++++++++++++ src/main/resources/intf-conf/gpki.conf | 25 +++ src/main/resources/intf-conf/public-info.conf | 12 ++ src/main/resources/spring/context-common.xml | 68 +++++++ .../resources/spring/context-datasource.xml | 50 +++++ .../resources/sql/mapper/base/test-mapper.xml | 13 ++ src/main/resources/sql/mybatis-config.xml | 25 +++ .../publicinfo/ConfigurationTest.java | 121 ++++++++++++ 11 files changed, 856 insertions(+) create mode 100644 pom.xml create mode 100644 src/main/java/cokr/xit/interfaces/publicinfo/Configuration.java create mode 100644 src/main/java/cokr/xit/interfaces/publicinfo/ServiceClient.java create mode 100644 src/main/java/cokr/xit/interfaces/publicinfo/ServiceMessage.java create mode 100644 src/main/resources/intf-conf/gpki.conf create mode 100644 src/main/resources/intf-conf/public-info.conf create mode 100644 src/main/resources/spring/context-common.xml create mode 100644 src/main/resources/spring/context-datasource.xml create mode 100644 src/main/resources/sql/mapper/base/test-mapper.xml create mode 100644 src/main/resources/sql/mybatis-config.xml create mode 100644 src/test/java/cokr/xit/interfaces/publicinfo/ConfigurationTest.java diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..ebc3f05 --- /dev/null +++ b/pom.xml @@ -0,0 +1,147 @@ + + 4.0.0 + + cokr.xit.interfaces + xit-public-info + 23.04.01-SNAPSHOT + jar + + xit-public-info + http://maven.apache.org + + + UTF-8 + + 17 + ${java.version} + ${java.version} + + + + + mvn2s + https://repo1.maven.org/maven2/ + + true + + + true + + + + egovframe + https://maven.egovframe.go.kr/maven/ + + true + + + false + + + + maven-public + https://nas.xit.co.kr:8888/repository/maven-public/ + + + + + + + + cokr.xit.interfaces + xit-gpki + 23.04.01-SNAPSHOT + + + + org.mariadb.jdbc + mariadb-java-client + 2.7.2 + + + + + + install + ${basedir}/target + ${artifactId}-${version} + + + ${basedir}/src/main/resources + + + ${basedir}/src/test/resources + ${basedir}/src/main/resources + + + + + org.apache.maven.plugins + maven-jar-plugin + 3.3.0 + + + **/*.class + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0 + + true + xml + + **/Abstract*.java + **/*Suite.java + + + **/*Test.java + + + + + org.codehaus.mojo + emma-maven-plugin + true + + + org.apache.maven.plugins + maven-source-plugin + 2.2 + + + attach-sources + + jar + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.9.1 + + + + + + + + + maven-snapshot + https://nas.xit.co.kr:8888/repository/maven-snapshots/ + + + + maven-release + https://nas.xit.co.kr:8888/repository/maven-releases/ + + + + diff --git a/src/main/java/cokr/xit/interfaces/publicinfo/Configuration.java b/src/main/java/cokr/xit/interfaces/publicinfo/Configuration.java new file mode 100644 index 0000000..b4dbc16 --- /dev/null +++ b/src/main/java/cokr/xit/interfaces/publicinfo/Configuration.java @@ -0,0 +1,139 @@ +package cokr.xit.interfaces.publicinfo; + +import java.util.LinkedHashMap; +import java.util.Map; + +import org.springframework.core.io.ClassPathResource; + +import com.fasterxml.jackson.core.type.TypeReference; + +import cokr.xit.foundation.Assert; +import cokr.xit.foundation.data.JSON; + +public class Configuration { + private static final LinkedHashMap confs; + + static { + try { + ClassPathResource res = new ClassPathResource("intf-conf/public-info.conf"); + confs = new JSON().parse(res.getInputStream(), new TypeReference>() {}); + confs.forEach((k, v) -> v.name = k); + } catch (Exception e) { + throw Assert.runtimeException(e); + } + } + + public static final Map get() { + return confs; + } + + public static final Configuration get(String name) { + return Assert.notEmpty(confs.get(name), name); + } + + private String + name, + /** 행정정보 공동이용 서비스가 발급한 api key */ + apiKey, + /** 서비스 api url */ + apiUrl, + + /** 이용기관 gpki server id */ + userServerId, + /** 보유기관 gpki server id */ + providerServerId; + + private boolean + /** 데이터 암복호화 사용 여부 */ + gpki, + /** 보유기관 가상 데이터 사용 여부 */ + mock; + + public String getName() { + return name; + } + + /**행정정보 공동이용 서비스가 발급한 api key를 반환한다. + * @return 행정정보 공동이용 서비스가 발급한 apiKey + */ + public String getApiKey() { + return apiKey; + } + + /**행정정보 공동이용 서비스가 발급한 api key를 설정한다. + * @param apiKey 행정정보 공동이용 서비스가 발급한 apiKey + */ + public void setApiKey(String apiKey) { + this.apiKey = apiKey; + } + + /**서비스 api url을 반환한다. + * @return 서비스 api url + */ + public String getApiUrl() { + return apiUrl; + } + + /**서비스 api url을 설정한다. + * @param apiUrl 서비스 api url + */ + public void setApiUrl(String apiUrl) { + this.apiUrl = apiUrl; + } + + /**이용기관 gpki server id를 반환한다. + * @return 이용기관 gpki server id + */ + public String getUserServerId() { + return userServerId; + } + + /**이용기관 gpki server id를 설정한다. + * @param userServerId 이용기관 gpki server id + */ + public void setUserServerId(String userServerId) { + this.userServerId = userServerId; + } + + /**보유기관 gpki server id를 반환한다. + * @return 보유기관 gpki server id + */ + public String getProviderServerId() { + return providerServerId; + } + + /**보유기관 gpki server id를 설정한다. + * @param providerServerId 보유기관 gpki server id + */ + public void setProviderServerId(String providerServerId) { + this.providerServerId = providerServerId; + } + + /**데이터 암복호화 사용 여부를 반환한다. + * @return 데이터 암복호화 사용 여부 + */ + public boolean isGpki() { + return gpki; + } + + /**데이터 암복호화 사용 여부를 설정한다. + * @param gpki 데이터 암복호화 사용 여부 + */ + public void setGpki(boolean gpki) { + this.gpki = gpki; + } + + /**보유기관 가상 데이터 사용 여부를 반환한다. + * @return 보유기관 가상 데이터 사용 여부 + */ + public boolean isMock() { + return mock; + } + + /**보유기관 가상 데이터 사용 여부를 설정한다. + * @param mock 보유기관 가상 데이터 사용 여부 + */ + public void setMock(boolean mock) { + this.mock = mock; + } +} \ No newline at end of file diff --git a/src/main/java/cokr/xit/interfaces/publicinfo/ServiceClient.java b/src/main/java/cokr/xit/interfaces/publicinfo/ServiceClient.java new file mode 100644 index 0000000..3e98f84 --- /dev/null +++ b/src/main/java/cokr/xit/interfaces/publicinfo/ServiceClient.java @@ -0,0 +1,69 @@ +package cokr.xit.interfaces.publicinfo; + +import java.net.http.HttpResponse; + +import cokr.xit.foundation.AbstractComponent; +import cokr.xit.foundation.data.JSON; +import cokr.xit.foundation.web.WebClient; +import cokr.xit.interfaces.gpki.GPKI; + +public class ServiceClient extends AbstractComponent { + private JSON json; + private Configuration conf; + private GPKI gpki; + + public ServiceClient setConf(String confName) { + conf = Configuration.get(notEmpty(confName, "confName")); + gpki = conf.isGpki() ? new GPKI() : null; + return this; + } + + public ServiceClient setJSON(JSON json) { + this.json = json; + return this; + } + + public ServiceMessage.Response request(Object... objs) { + if (isEmpty(objs)) return null; + + ServiceMessage.Response resp = new ServiceMessage.Response(); + try { + HttpResponse hresp = new WebClient().post(req -> + req.uri(conf.getApiUrl()) + .json(json) + .header(ServiceMessage.Support.header(conf.getName())) + .bodyData(reqBody(objs)) + ); + + int statusCode = hresp.statusCode(); + String body = hresp.body(); + + log().debug("statusCode: {}", statusCode); + log().debug("body:\n{}", body); + + return resp + .setStatus(statusCode) + .setHeaders(hresp.headers().map()) + .setBody(200 == statusCode ? respBody(body) : body); + } catch (Exception e) { + return resp.setError(rootCause(e)); + } + } + + private String reqBody(Object... objs) { + ServiceMessage msg = new ServiceMessage().addData(objs); + String str = json.stringify(msg); + if (gpki != null) + log().debug("request body:\n{}", json.stringify(msg, true)); + return gpki == null ? str : gpki.encrypt(conf.getProviderServerId(), str); + } + + private String respBody(String str) { + if (gpki == null) + return str; + + String body = gpki.decrypt(str); + log().debug("decrypted:\n{}", body); + return body; + } +} \ No newline at end of file diff --git a/src/main/java/cokr/xit/interfaces/publicinfo/ServiceMessage.java b/src/main/java/cokr/xit/interfaces/publicinfo/ServiceMessage.java new file mode 100644 index 0000000..39fdfa0 --- /dev/null +++ b/src/main/java/cokr/xit/interfaces/publicinfo/ServiceMessage.java @@ -0,0 +1,187 @@ +package cokr.xit.interfaces.publicinfo; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Random; + +import cokr.xit.foundation.Assert; + +/**서비스 메시지 + * @author mjkhan + */ +public class ServiceMessage { + private List data; + + /**data를 반환한다. + * @return data + */ + public List getData() { + return Assert.ifEmpty(data, () -> data = new ArrayList<>()); + } + + /**data를 설정한다. + * @param data data + */ + public void setData(List data) { + this.data = data; + } + + /**사용자 객체를 데이터에 추가한다. + * @param objs 사용자 객체 + * @return 현재 ServiceMessage + */ + public ServiceMessage addData(Object... objs) { + if (!Assert.isEmpty(objs)) + getData().addAll(List.of(objs)); + return this; + } + + /**서비스 응답 메시지 + * @author mjkhan + */ + public static class Response { + /** 응답코드 */ + private int status; + /** 응답헤더 */ + private Map> headers; + /** 응답본문 */ + private String body; + /** 에러 */ + private Throwable error; + + /**응답코드를 반환한다. + * @return 응답코드 + */ + public int getStatus() { + return status; + } + + /**응답코드를 설정한다. + * @param status 응답코드 + * @return 현재 Response + */ + public Response setStatus(int status) { + this.status = status; + return this; + } + + /**응답헤더를 반환한다. + * @return 응답헤더 + */ + public Map> getHeaders() { + return headers; + } + + public List getHeader(String name) { + List values = headers != null ? headers.get(name) : null; + return values != null ? values : Collections.emptyList(); + } + + public String header(String name) { + List values = getHeader(name); + return values.isEmpty() ? values.get(0) : ""; + } + + /**응답헤더를 설정한다. + * @param headers 응답헤더 + * @return 현재 Response + */ + public Response setHeaders(Map> headers) { + this.headers = headers; + return this; + } + + /**응답본문을 반환한다. + * @return 응답본문 + */ + public String getBody() { + return body; + } + + /**응답본문을 설정한다. + * @param body 응답본문 + * @return 현재 Response + */ + public Response setBody(String body) { + this.body = body; + return this; + } + + public boolean success() { + return error == null && status == 200; + } + + /**오류를 반환한다. + * @return 오류 + */ + public Throwable getError() { + return error; + } + + /**오류를 설정한다. + * @param error 오류 + * @return 현재 Response + */ + public Response setError(Throwable error) { + this.error = error; + return this; + } + } + + public static class Support { + private static final SimpleDateFormat dataFormat = new SimpleDateFormat("yyyyMMddHHmmssSSS",Locale.KOREA); + + public static final Map header(String confName) { + Configuration conf = Configuration.get(confName); + try { + return Map.of( + "api_key", conf.getApiKey(), + "gpki_yn", conf.isGpki() ? "Y" : "N", + "mock_yn", conf.isMock() ? "Y" : "N", + "cert_server_id", conf.getUserServerId(), + "tx_id", txId(), + "SOAPAction", "", +// "Host", InetAddress.getLocalHost().toString(), + "User-Agent", "java-net-httpclient" + ); + } catch (Exception e) { + throw Assert.runtimeException(e); + } + } + + private static String txId() { + return dataFormat.format(new Date()) + key(8); + } + + private static String key(int length) { + char[] chars = new char[length]; + Random r = new Random(System.currentTimeMillis()); + + for (int i = 0; i < length; i++) { + switch (r.nextInt(3)) { + case 0: chars[i] = (char)(r.nextInt(26) + 65); break; + case 1: chars[i] = (char)(r.nextInt(10) + 48); break; + case 2: chars[i] = (char)(r.nextInt(26) + 97); break; + default: chars[i] = (char)r.nextInt(256); + } + } + return String.valueOf(chars); + } + + public static String peel(String str) { + String prefix = "\"data\": ["; + int pos = str.indexOf(prefix); + if (pos < 0) { + prefix = "\"data\":["; + pos = str.indexOf(prefix); + } + String result = str.substring(pos + prefix.length()); + return result.substring(0, result.lastIndexOf("]")).trim(); + } + } +} \ No newline at end of file diff --git a/src/main/resources/intf-conf/gpki.conf b/src/main/resources/intf-conf/gpki.conf new file mode 100644 index 0000000..c0c18cb --- /dev/null +++ b/src/main/resources/intf-conf/gpki.conf @@ -0,0 +1,25 @@ +{ + "license": "C:\\GPKI\\Lic", /* 이용기관 GPKI API 라이센스 디렉토리 */ + + "charset": "UTF-8", /* 문자셋 */ + + "server": { + "local": "SVR1311000030", /* 이용기관 서버 CN */ + "targets": "SVR1500000015" /* 대상기관 서버인증서 아이디, 여러 개일 경우 컴마(,)로 구분 */ + }, + + "ldapUrl": "ldap://10.1.7.118:389/cn=", /* 대상기관 인증서 다운로드를 위한 행정망 LDAP URL */ + /*"ldapUrl": "ldap://152.99.57.127:389/cn=", 대상기관 인증서 다운로드를 위한 인터넷망 LDAP URL */ + "certDir": "C:\\GPKI\\Certificate\\class1", /* 서버 인증서, 키 저장 디렉토리 */ + + "env": { /* 이용기관 서버 인증서 */ + "certFile": "SVR1311000030_env.cer", + "privateKeyFile": "SVR1311000030_env.key", + "privateKeyPassword": "기후대기3395!" + }, + "sig": { /* 이용기관 서버 전자서명 */ + "certFile": "SVR1311000030_sig.cer", + "privateKeyFile": "SVR1311000030_sig.key", + "privateKeyPassword": "기후대기3395!" + } +} \ No newline at end of file diff --git a/src/main/resources/intf-conf/public-info.conf b/src/main/resources/intf-conf/public-info.conf new file mode 100644 index 0000000..1ed6ba6 --- /dev/null +++ b/src/main/resources/intf-conf/public-info.conf @@ -0,0 +1,12 @@ +{ + "basic-info-ext": { /* 자동차 기본정보(연료 제원 포함) 조회 */ + "apiKey": "59f26bf09ed196bfbd98210388c4c6ea9dd0f77bde3f35526f082647a305325b", /* 행정정보 공동이용 서비스가 발급한 api key */ + /*"apiUrl": "http://10.188.225.25:29001/piss/api/molit/SignguCarBassMatterInqireService", /* 행정정보 운영 url */ + "apiUrl": "http://10.188.225.94:29001/piss/api/molit/SignguCarBassMatterInqireService", /* 행정정보 개발 url */ + "userServerId": "SVR1311000030", /* 이용기관 gpki server id */ + "providerServerId": "SVR1500000015", /* 보유기관 gpki server id */ + + "gpki": true, /* 데이터 암복호화 사용 여부 */ + "mock": false /* 보유기관 가상 데이터 사용 여부 */ + } +} \ No newline at end of file diff --git a/src/main/resources/spring/context-common.xml b/src/main/resources/spring/context-common.xml new file mode 100644 index 0000000..3d075d1 --- /dev/null +++ b/src/main/resources/spring/context-common.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + classpath:message/message-common + classpath:message/authentication-message + classpath:org/egovframe/rte/fdl/property/messages/properties + + + + + 60 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/spring/context-datasource.xml b/src/main/resources/spring/context-datasource.xml new file mode 100644 index 0000000..f4a8c4d --- /dev/null +++ b/src/main/resources/spring/context-datasource.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/sql/mapper/base/test-mapper.xml b/src/main/resources/sql/mapper/base/test-mapper.xml new file mode 100644 index 0000000..d674130 --- /dev/null +++ b/src/main/resources/sql/mapper/base/test-mapper.xml @@ -0,0 +1,13 @@ + + + + +${sql} + +${sql} + +${sql} + +COMMIT + + \ No newline at end of file diff --git a/src/main/resources/sql/mybatis-config.xml b/src/main/resources/sql/mybatis-config.xml new file mode 100644 index 0000000..03ad4e8 --- /dev/null +++ b/src/main/resources/sql/mybatis-config.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/java/cokr/xit/interfaces/publicinfo/ConfigurationTest.java b/src/test/java/cokr/xit/interfaces/publicinfo/ConfigurationTest.java new file mode 100644 index 0000000..5a69e2c --- /dev/null +++ b/src/test/java/cokr/xit/interfaces/publicinfo/ConfigurationTest.java @@ -0,0 +1,121 @@ +package cokr.xit.interfaces.publicinfo; + +import java.util.Map; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import cokr.xit.foundation.data.JSON; +import cokr.xit.foundation.test.TestSupport; + +public class ConfigurationTest extends TestSupport { + @Test + void get() { + Map confs = Configuration.get(); + Assertions.assertNotNull(confs); + confs.keySet().forEach(System.out::println); + + Configuration conf = Configuration.get("apiName"); + Assertions.assertNotNull(conf); + } + + @Test + void header() { + ServiceMessage.Support.header("basic-info-ext") + .forEach((k, v) -> System.out.println(String.format("%s = %s", k, v))); + } + + @Test + void body() { + Object1 obj1_0 = new Object1(); + obj1_0.setId("1_0"); + obj1_0.setName("1 zero"); + Object2 obj2_0 = new Object2(); + obj2_0.setId("2_0"); + obj2_0.setName("2 zero"); + obj2_0.setPhoneNo("222-2222"); + + ServiceMessage msg = new ServiceMessage(); + msg.getData().add(obj1_0); + msg.getData().add(obj2_0); + + JSON json = new JSON(); + String str = json.stringify(msg, true); + System.out.println(str); + + Object parsed = json.parse(str, ServiceMessage.class); + System.out.println(parsed); + } + + public static class Object1 { + private String id; + private String name; + + /**id을(를) 반환한다. + * @return id + */ + public String getId() { + return id; + } + /**id을(를) 설정한다. + * @param id id + */ + public void setId(String id) { + this.id = id; + } + /**name을(를) 반환한다. + * @return name + */ + public String getName() { + return name; + } + /**name을(를) 설정한다. + * @param name name + */ + public void setName(String name) { + this.name = name; + } + } + + public static class Object2 { + private String id; + private String name; + private String phoneNo; + /**id을(를) 반환한다. + * @return id + */ + public String getId() { + return id; + } + /**id을(를) 설정한다. + * @param id id + */ + public void setId(String id) { + this.id = id; + } + /**name을(를) 반환한다. + * @return name + */ + public String getName() { + return name; + } + /**name을(를) 설정한다. + * @param name name + */ + public void setName(String name) { + this.name = name; + } + /**phoneNo을(를) 반환한다. + * @return phoneNo + */ + public String getPhoneNo() { + return phoneNo; + } + /**phoneNo을(를) 설정한다. + * @param phoneNo phoneNo + */ + public void setPhoneNo(String phoneNo) { + this.phoneNo = phoneNo; + } + } +} \ No newline at end of file