commit 6e4576934f143f18214c2d0e1821440d42d49e8e Author: mjkhan21 Date: Wed Aug 30 15:03:55 2023 +0900 최초 커밋 diff --git a/lib/libgpkiapi_jni_1.5.jar b/lib/libgpkiapi_jni_1.5.jar new file mode 100644 index 0000000..5e3bdea Binary files /dev/null and b/lib/libgpkiapi_jni_1.5.jar differ diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..17ddf1a --- /dev/null +++ b/pom.xml @@ -0,0 +1,154 @@ + + 4.0.0 + + cokr.xit.interfaces + xit-disabled-parking + 23.04.01-SNAPSHOT + jar + + xit-disabled-parking + 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.base + xit-foundation + 23.04.01-SNAPSHOT + + + + libgpkiapi_jni_1.5 + libgpkiapi_jni_1.5 + 1.0 + system + ${project.basedir}/lib/libgpkiapi_jni_1.5.jar + + + + 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/disabledparking/service/DisabledParkingService.java b/src/main/java/cokr/xit/interfaces/disabledparking/service/DisabledParkingService.java new file mode 100644 index 0000000..33b622e --- /dev/null +++ b/src/main/java/cokr/xit/interfaces/disabledparking/service/DisabledParkingService.java @@ -0,0 +1,22 @@ +package cokr.xit.interfaces.disabledparking.service; + +import java.util.List; + +import cokr.xit.foundation.data.DataObject; + +/**장애인 표지 조회 서비스 인터페이스 + * @author mjkhan + */ +public interface DisabledParkingService { + /**지정한 차량번호의 장애인 표지 여부를 조회한다. + * @param vehicleNo 차량번호 + * @return 지정한 차량번호의 장애인 표지 여부 + */ + DataObject getParkingInfo(String vehicleNo); + + /**지정한 차량번호들의 장애인 표지 여부를 조회한다. + * @param vehicleNos 차량번호 + * @return 지정한 차량번호의 장애인 표지 여부 + */ + List getParkingList(String... vehicleNos); +} \ No newline at end of file diff --git a/src/main/java/cokr/xit/interfaces/disabledparking/service/bean/DisabledParkingBean.java b/src/main/java/cokr/xit/interfaces/disabledparking/service/bean/DisabledParkingBean.java new file mode 100644 index 0000000..c5aa5a1 --- /dev/null +++ b/src/main/java/cokr/xit/interfaces/disabledparking/service/bean/DisabledParkingBean.java @@ -0,0 +1,278 @@ +package cokr.xit.interfaces.disabledparking.service.bean; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.StringReader; +import java.net.http.HttpResponse; +import java.text.SimpleDateFormat; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.stream.Stream; + +import javax.xml.parsers.DocumentBuilderFactory; + +import org.springframework.core.io.ClassPathResource; +import org.springframework.stereotype.Component; +import org.springframework.util.FileCopyUtils; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; + +import cokr.xit.foundation.AbstractComponent; +import cokr.xit.foundation.data.DataObject; +import cokr.xit.foundation.data.JSON; +import cokr.xit.foundation.web.WebClient; + +/**장애인 표지 조회 Bean. 올바로 동작하려면 + * + * 을 설정해야 한다. + * @author mjkhan + */ +@Component("disabledParkingBean") +public class DisabledParkingBean extends AbstractComponent { + private static final String template; + private static final Config conf = Config.get(); + + static { + ClassPathResource resource = new ClassPathResource(conf.template); + try ( + InputStream input = resource.getInputStream(); + InputStreamReader reader = new InputStreamReader(input); + ) { + template = FileCopyUtils.copyToString(reader); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private GPKI gpki = new GPKI(); + + /**지정한 차량번호의 장애인 표지 여부를 조회한다. + * @param vehicleNo 차량번호 + * @return 지정한 차량번호의 장애인 표지 여부 + */ + public DataObject getParkingInfo(String vehicleNo) { + String xml = template + .replace("{carsNo}", vehicleNo) + .replace("{transactionID}", getTransactionID()); + + String params = parse(xml, conf.getRequest()), + encrypted = gpki.encrypt(conf.getTargetServer(), params); + log().debug("params:\n{}", params); + + xml = xml.replace(params, encrypted); + + String + hresp = request(xml), + parsed = parse(hresp, conf.getResponse()), + decrypted = gpki.decrypt(parsed); + hresp = hresp.replace(parsed, decrypted); + log().debug("response:\n{}", hresp.replace("><", ">\n<")); + + return toMap(hresp); + } + + /**지정한 차량번호들의 장애인 표지 여부를 조회한다. + * @param vehicleNos 차량번호 + * @return 지정한 차량번호의 장애인 표지 여부 + */ + public List getParkingList(String... vehicleNos) { + if (isEmpty(vehicleNos)) + return Collections.emptyList(); + + return Stream.of(vehicleNos) + .distinct() + .map(this::getParkingInfo) + .toList(); + } + + private String request(String xml) { + HttpResponse hresp = new WebClient().post(req -> + req.uri(conf.serviceUrl) + .contentType(WebClient.Request.ContentType.XML) + .header("Connection", "close") + .bodyData(xml) + + ); + return hresp.body(); + } + + private static String getTransactionID() { + return new SimpleDateFormat("yyyyMMddHHmmssSSS", Locale.KOREA).format(new Date()) + + Double.toString(Math.random()).substring(2, 6) + + Double.toString(Math.random()).substring(2, 6); + } + + private static String parse(String str, Map range) { + return str + .split(range.get("start"))[1] + .split(range.get("end"))[0]; + } + + private static DataObject toMap(String xml) { + try ( + StringReader reader = new StringReader(xml); + ) { + Document doc = DocumentBuilderFactory + .newInstance() + .newDocumentBuilder() + .parse(new InputSource(reader)); + + DataObject result = new DataObject(); + + NodeList nodes = doc.getElementsByTagName("getDisabledParkingYnResponse"); + for (int i = 0, length = nodes.getLength(); i < length; ++i) { + Node node = nodes.item(i); + if (node.getNodeType() != Node.ELEMENT_NODE) continue; + + NodeList children = node.getChildNodes(); + for (int j = 0, count = children.getLength(); j < count; ++j) { + Node child = children.item(j); + if (child.getNodeType() != Node.ELEMENT_NODE) continue; + + Element element = (Element)child; + result.set(element.getTagName(), element.getTextContent()); + } + } + + return result; + } catch (Exception e) { + throw runtimeException(e); + } + } + + /**장애인 표지 조회 설정. + * 설정 항목은 intf-conf/disabled-parking.conf 파일의 JSON 포맷으로 된 다음 내용을 적재한다. + *
 {
+	 *     "template": "장애인 표지 조회를 위한 요청 xml 템플릿의 클래스패스 상의 경로",
+	 *     "serviceUrl": "장애인 표지 조회 서비스 url",
+	 *     "targetServer": "장애인 표지 조회 서버 아이디(인증키)",
+	 *     "request": {
+	 *         "start": "요청 xml의 파라미터 시작 태그",
+	 *         "end": "요청 xml의 파라미터 끝 태그"
+	 *     },
+	 *     "response": {
+	 *         "start": "응답 xml의 데이터 시작 태그",
+	 *         "end": "응답 xml의 데이터 끝 태그"
+	 *     }
+	 * }
+ * 요청 xml 템플릿(template/disabled-parking-request.xml)으로는 다음 내용을 설정한다. + *
  • commonHeader + *
    • serviceName - 서비스 이름
    • + *
    • useSystemCode - 이용 시스템 코드
    • + *
    • certServerId - 이용기관 GPKI 서버인증서 아이디
    • + *
    • transactionUniqueId - {transactionID}, 프로그램이 생성하는 트랜잭션 아이디, 수정금지
    • + *
    • userDeptCode - 이용자 부서코드
    • + *
    • userName - 이용자 이름
    • + *
    + *
  • + *
  • Body / getDisabledParkingYn + *
    • ReqOrgCd - 요청기관 코드
    • + *
    • ReqBizCd - 요청업무 코드
    • + *
    • CARS_NO - {carsNo}, 이용자가 프로그램으로 전달하는 차량번호, 수정금지
    • + *
    + *
  • + *
+ * @author mjkhan + */ + public static class Config extends AbstractComponent { + private static Config conf; + + public static Config get() { + if (conf == null) + try { + ClassPathResource res = new ClassPathResource("intf-conf/disabled-parking.conf"); + conf = new JSON().parse(res.getInputStream(), Config.class); + } catch (Exception e) { + throw runtimeException(e); + } + return conf; + } + + private String + template, + serviceUrl, + targetServer; + private Map + request, + response; + + /**요청 xml의 클래스패스 상의 경로를 반환한다. + * @return 요청 xml의 클래스패스 상의 경로 + */ + public String getTemplate() { + return template; + } + + /**요청 xml의 클래스패스 상의 경로를 설정한다. + * @param template 요청 xml의 클래스패스 상의 경로 + */ + public void setTemplate(String template) { + this.template = template; + } + + /**장애인 표지 조회 서비스 url을 반환한다. + * @return 장애인 표지 조회 서비스 url + */ + public String getServiceUrl() { + return serviceUrl; + } + + /**장애인 표지 조회 서비스 url을 설정한다. + * @param serviceUrl 장애인 표지 조회 서비스 url + */ + public void setServiceUrl(String serviceUrl) { + this.serviceUrl = serviceUrl; + } + + /**장애인 조회 서버 아이디(키)를 반환한다. + * @return 장애인 조회 서버 아이디(키) + */ + public String getTargetServer() { + return targetServer; + } + + /**장애인 조회 서버 아이디(키)를 설정한다. + * @param targetServer 장애인 조회 서버 아이디(키) + */ + public void setTargetServer(String targetServer) { + this.targetServer = targetServer; + } + + /**요청 xml의 파라미터의 시작과 끝 태그를 반환한다. + * @return 요청 xml의 파라미터의 시작과 끝 태그 + */ + public Map getRequest() { + return request; + } + + /**요청 xml의 파라미터의 시작과 끝 태그를 설정한다. + * @param request 요청 xml의 파라미터의 시작과 끝 태그 + */ + public void setRequest(Map request) { + this.request = request; + } + + /**요청 xml의 결과값의 시작과 끝 태그를 반환한다. + * @return 요청 xml의 결과값의 시작과 끝 태그 + */ + public Map getResponse() { + return response; + } + + /**요청 xml의 결과값의 시작과 끝 태그를 설정한다. + * @param response 요청 xml의 결과값의 시작과 끝 태그 + */ + public void setResponse(Map response) { + this.response = response; + } + } +} \ No newline at end of file diff --git a/src/main/java/cokr/xit/interfaces/disabledparking/service/bean/DisabledParkingServiceBean.java b/src/main/java/cokr/xit/interfaces/disabledparking/service/bean/DisabledParkingServiceBean.java new file mode 100644 index 0000000..72cd5a0 --- /dev/null +++ b/src/main/java/cokr/xit/interfaces/disabledparking/service/bean/DisabledParkingServiceBean.java @@ -0,0 +1,30 @@ +package cokr.xit.interfaces.disabledparking.service.bean; + +import java.util.List; + +import javax.annotation.Resource; + +import org.springframework.stereotype.Service; + +import cokr.xit.foundation.component.AbstractServiceBean; +import cokr.xit.foundation.data.DataObject; +import cokr.xit.interfaces.disabledparking.service.DisabledParkingService; + +/**장애인 표지 조회 서비스 구현체 + * @author mjkhan + */ +@Service("disabledParkingService") +public class DisabledParkingServiceBean extends AbstractServiceBean implements DisabledParkingService { + @Resource(name = "disabledParkingBean") + private DisabledParkingBean bean; + + @Override + public DataObject getParkingInfo(String vehicleNo) { + return bean.getParkingInfo(vehicleNo); + } + + @Override + public List getParkingList(String... vehicleNos) { + return bean.getParkingList(vehicleNos); + } +} \ No newline at end of file diff --git a/src/main/java/cokr/xit/interfaces/disabledparking/service/bean/GPKI.java b/src/main/java/cokr/xit/interfaces/disabledparking/service/bean/GPKI.java new file mode 100644 index 0000000..3674357 --- /dev/null +++ b/src/main/java/cokr/xit/interfaces/disabledparking/service/bean/GPKI.java @@ -0,0 +1,419 @@ +package cokr.xit.interfaces.disabledparking.service.bean; + +import java.io.File; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.IntSupplier; +import java.util.function.Supplier; +import java.util.stream.Stream; + +import org.springframework.core.io.ClassPathResource; + +import com.gpki.gpkiapi_jni; +import com.gpki.gpkiapi.GpkiApi; +import com.gpki.gpkiapi.cert.X509Certificate; +import com.gpki.gpkiapi.exception.GpkiApiException; +import com.gpki.gpkiapi.storage.Disk; + +import cokr.xit.foundation.AbstractComponent; +import cokr.xit.foundation.data.JSON; + +/**GPKI(행정전자서명) API 유틸리티. 올바로 동작하려면 {@link Config intf-conf/gpki.conf}를 설정해야 한다. + * @author mjkhan + */ +public class GPKI extends AbstractComponent { + private static final Config conf = Config.get(); + private static final HashMap serverCerts = new HashMap<>(); + private static byte[] + envCert, + envKey, + sigCert, + sigKey; + + private gpkiapi_jni gpki; + + static { + init(); + } + + /**주어진 문자열을 지정한 아이디의 서버인증서로 암호화하여 반환한다. + * @param serverID 서버인증서 아이디 + * @param data 문자열 + * @return 지정한 아이디의 서버인증서로 암호화한 문자열 + */ + public String encrypt(String serverID, String data) { + if (isEmpty(data)) return ""; + + try { + start(); + + byte[] encrypted = encrypt(serverID, data.getBytes(conf.getCharset())); + return encode(sign(encrypted)); + } catch (Exception e) { + throw runtimeException(e); + } finally { + finish(); + } + } + + /**주어진 암호화된 문자열을 복호화하여 반환한다. + * @param encrypted 암호화된 문자열 + * @return 복호화한 문자열 + */ + public String decrypt(String encrypted) { + if (isEmpty(encrypted)) return ""; + + try { + start(); + + byte[] validated = validate(decode(encrypted)); + return new String(decrypt(validated), conf.getCharset()); + } catch (Exception e) { + throw runtimeException(e); + } finally { + finish(); + } + } + + private static void call(gpkiapi_jni gpki, IntSupplier task, String msg) throws Exception { + int result = task.getAsInt(); + if (result == 0) return; + + String message = isEmpty(msg) ? "" : msg + ": "; + message += gpki != null ? gpki.sDetailErrorString : "errorCode = " + result; + + throw new Exception(message); + } + + private static void init() { + try { + GpkiApi.init(conf.license); + gpkiapi_jni gpki = getGpki(); + call(gpki, () -> gpki.API_GetInfo(), null); + + for (String serverID: conf.getTargetServers()) + load(gpki, serverID); + + envCert = Disk.readCert(conf.getCertFile("env")).getCert(); + envKey = Disk.readPriKey(conf.getPrivateKeyFile("env"), conf.getPrivateKeyPassword("env")).getKey(); + + sigCert = Disk.readCert(conf.getCertFile("sig")).getCert(); + sigKey = Disk.readPriKey(conf.getPrivateKeyFile("sig"), conf.getPrivateKeyPassword("sig")).getKey(); + + finish(gpki); + } catch (Exception e) { + throw runtimeException(e); + } + } + + private static gpkiapi_jni getGpki() throws Exception{ + gpkiapi_jni gpki = new gpkiapi_jni(); + call(gpki, () -> gpki.API_Init(conf.license), null); + return gpki; + } + + private static void finish(gpkiapi_jni gpki) { + if (gpki == null) return; + + try { + call(gpki, () -> gpki.API_Finish(), null); + } catch (Exception e) { + throw runtimeException(e); + } + } + + private static void load(gpkiapi_jni gpki, String serverID) throws Exception { + X509Certificate cert = null; + String certFile = conf.certDir + File.separator + serverID + ".cer"; + try { + cert = Disk.readCert(certFile); + } catch (GpkiApiException e) { + cert = null; + } + + if (cert == null && !isEmpty(conf.ldapUrl)) { + String uri = serverID.charAt(3) > '9' ? + ",ou=Group of Server,o=Public of Korea,c=KR" : + ",ou=Group of Server,o=Government of Korea,c=KR"; + + call(gpki, + () -> gpki.LDAP_GetAnyDataByURL("userCertificate;binary", conf.ldapUrl + serverID + uri), + null + ); + + cert = new X509Certificate(gpki.baReturnArray); + Disk.writeCert(certFile, cert); + } + + if (cert != null) + serverCerts.put(serverID, cert); + } + + private static X509Certificate getCert(String serverID) { + X509Certificate cert = serverCerts.get(serverID); + if (cert == null) + throw new RuntimeException(String.format("%s not found for %s", X509Certificate.class.getSimpleName(), serverID)); + return cert; + } + + private T call(IntSupplier work, Supplier result, String message) { + boolean finish = start(); + + try { + call(gpki, work, message); + return result.get(); + } catch (Exception e) { + throw runtimeException(e); + } finally { + if (finish) + finish(); + } + } + + private byte[] encrypt(String serverID, byte[] data) { + return call( + () -> { + X509Certificate cert = getCert(serverID); + return gpki.CMS_MakeEnvelopedData(cert.getCert(), data, gpkiapi_jni.SYM_ALG_NEAT_CBC); + }, + () -> gpki.baReturnArray, + "Encryption failed" + ); + } + + private byte[] decrypt(byte[] encrypted) { + return call( + () -> gpki.CMS_ProcessEnvelopedData(envCert, envKey, encrypted), + () -> gpki.baReturnArray, + "Decryption failed" + ); + } + + private byte[] sign(byte[] data) { + return call( + () -> gpki.CMS_MakeSignedData(sigCert, sigKey, data, null), + () -> gpki.baReturnArray, + "Fail to sign message" + ); + } + + private byte[] validate(byte[] signed) { + return call( + () -> gpki.CMS_ProcessSignedData(signed), + () -> gpki.baData, + "Validation failed" + ); + } + + private String encode(byte[] data) { + return call( + () -> gpki.BASE64_Encode(data), + () -> gpki.sReturnString, + "Encoding failed" + ); + } + + private byte[] decode(String encoded) { + return call( + () -> gpki.BASE64_Decode(encoded), + () -> gpki.baReturnArray, + "Decoding failed" + ); + } + + private boolean start() { + if (gpki != null) return false; + + try { + gpki = getGpki(); + return true; + } catch (Exception e) { + throw runtimeException(e); + } + } + + private void finish() { + if (gpki == null) return; + + finish(gpki); + gpki = null; + } + + /**GPKI 설정파일(intf-conf/gpki.conf)을 로드한다. + * 설정 항목은 JSON 포맷으로 된 다음 내용을 적재한다. + *
 {
+	 *     "license": "이용기관 GPKI API 라이센스 디렉토리",
+	 *     "charset": "문자셋",
+	 *     "server": {
+	 *         "local": "이용기관 서버 CN",
+	 *         "targets": "대상기관 서버인증서 아이디, 여러 개일 경우 컴마(,)로 구분"
+	 *     },
+	 *     "ldapUrl": "대상기관 인증서 다운로드를 위한 LDAP URL",
+	 *     "certDir": "서버 인증서, 키 저장 디렉토리",
+	 *     "env": {
+	 *         "certFile": "이용기관 서버 인증서 파일",
+	 *         "privateKeyFile": "이용기관 서버 인증서 키 파일",
+	 *         "privateKeyPassword": "이용기관 서버 인증서 비밀번호"
+	 *     },
+	 *     "sig": {
+	 *         "certFile": "이용기관 서버 전자서명 파일",
+	 *         "privateKeyFile": "이용기관 서버 전자서명 키 파일",
+	 *         "privateKeyPassword": "이용기관 서버 전자서명 비밀번호"
+	 *     }
+	 * }
+ * @author mjkhan + */ + public static class Config extends AbstractComponent { + private static Config conf; + + public static Config get() { + if (conf == null) + try { + ClassPathResource res = new ClassPathResource("intf-conf/gpki.conf"); + conf = new JSON().parse(res.getInputStream(), Config.class); + } catch (Exception e) { + throw runtimeException(e); + } + return conf; + } + + private String + charset, + license, + ldapUrl, + certDir; + private Map + server, + env, + sig; + + /**charset을(를) 반환한다. + * @return charset + */ + public String getCharset() { + return charset; + } + + /**charset을(를) 설정한다. + * @param charset charset + */ + public void setCharset(String charset) { + this.charset = charset; + } + + /**license을(를) 반환한다. + * @return license + */ + public String getLicense() { + return license; + } + + /**license을(를) 설정한다. + * @param license license + */ + public void setLicense(String license) { + this.license = license; + } + + /**ldapUrl을(를) 반환한다. + * @return ldapUrl + */ + public String getLdapUrl() { + return ldapUrl; + } + + /**ldapUrl을(를) 설정한다. + * @param ldapUrl ldapUrl + */ + public void setLdapUrl(String ldapUrl) { + this.ldapUrl = ldapUrl; + } + + /**certDir을(를) 반환한다. + * @return certDir + */ + public String getCertDir() { + return certDir; + } + + /**certDir을(를) 설정한다. + * @param certDir certDir + */ + public void setCertDir(String certDir) { + this.certDir = certDir; + } + + /**server을(를) 반환한다. + * @return server + */ + public Map getServer() { + return ifEmpty(server, Collections::emptyMap); + } + + public String getLocalServer() { + return getServer().get("local"); + } + + public List getTargetServers() { + String targets = ifEmpty(getServer().get("targets"), ""); + return Stream.of(targets.split(",")).map(String::trim).toList(); + } + + /**server을(를) 설정한다. + * @param server server + */ + public void setServer(Map server) { + this.server = server; + } + + /**env을(를) 반환한다. + * @return env + */ + public Map getEnv() { + return ifEmpty(env, Collections::emptyMap); + } + + /**env을(를) 설정한다. + * @param env env + */ + public void setEnv(Map env) { + this.env = env; + } + + /**sig을(를) 반환한다. + * @return sig + */ + public Map getSig() { + return ifEmpty(sig, Collections::emptyMap); + } + + /**sig을(를) 설정한다. + * @param sig sig + */ + public void setSig(Map sig) { + this.sig = sig; + } + + private Map getCredentials(String type) { + switch (type) { + case "env": return getEnv(); + case "sig": return getSig(); + default: throw new IllegalArgumentException(type); + } + } + + public String getCertFile(String type) { + return certDir + File.separator + getCredentials(type).get("certFile"); + } + + public String getPrivateKeyFile(String type) { + return certDir + File.separator + getCredentials(type).get("privateKeyFile"); + } + + public String getPrivateKeyPassword(String type) { + return getCredentials(type).get("privateKeyPassword"); + } + } +} \ No newline at end of file diff --git a/src/main/java/cokr/xit/interfaces/disabledparking/web/DisabledParkingController.java b/src/main/java/cokr/xit/interfaces/disabledparking/web/DisabledParkingController.java new file mode 100644 index 0000000..9378711 --- /dev/null +++ b/src/main/java/cokr/xit/interfaces/disabledparking/web/DisabledParkingController.java @@ -0,0 +1,47 @@ +package cokr.xit.interfaces.disabledparking.web; + +import javax.annotation.Resource; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.servlet.ModelAndView; + +import cokr.xit.foundation.web.AbstractController; +import cokr.xit.interfaces.disabledparking.service.DisabledParkingService; + +/**장애인 표지 조회 서비스 컨트롤러 + * @author mjkhan + */ +@Controller("disabledParkingApi") +@RequestMapping("/api/disabledParking") +public class DisabledParkingController extends AbstractController { + @Resource(name = "disabledParkingService") + private DisabledParkingService service; + + /**지정한 차량번호의 장애인 표지 여부를 조회한다. + * @param vehicleNo 차량번호 + * @return "jsonView" + *
 {
+	 *     "parkingInfo": "장애인 표지 정보"
+	 * }
+ */ + @GetMapping(name = "장애인 표지 조회", value = "/parkingInfo.do") + public ModelAndView getParkingInfo(String vehicleNo) { + return new ModelAndView("jsonView") + .addObject("parkingInfo", service.getParkingInfo(vehicleNo)); + } + + /**지정한 차량번호들의 장애인 표지 여부를 조회한다. + * @param vehicleNos 차량번호 + * @return "jsonView" + *
 {
+	 *     "parkingList": "장애인 표지 목록"
+	 * }
+ */ + @GetMapping(name = "장애인 표지 목록 조회", value = "/parkingList.do") + public ModelAndView getParkingList(String... vehicleNos) { + return new ModelAndView("jsonView") + .addObject("parkingList", service.getParkingList(vehicleNos)); + } +} \ No newline at end of file diff --git a/src/main/resources/.gitignore b/src/main/resources/.gitignore new file mode 100644 index 0000000..7d32a6c --- /dev/null +++ b/src/main/resources/.gitignore @@ -0,0 +1,3 @@ +/message/ +/spring/ +/sql/ diff --git a/src/main/resources/intf-conf/disabled-parking.conf b/src/main/resources/intf-conf/disabled-parking.conf new file mode 100644 index 0000000..362c9a7 --- /dev/null +++ b/src/main/resources/intf-conf/disabled-parking.conf @@ -0,0 +1,16 @@ +{ + "template": "template/disabled-parking-request.xml", /* 장애인 표지 조회를 위한 요청 xml 템플릿의 클래스패스 상의 경로 */ + + "serviceUrl": "http://hub.share.go.kr/rid/ynservice/swsdn/DisabledParkingYnService", /* 장애인 표지 조회 서비스 url */ + "targetServer": "SVR1311000030", /* 장애인 표지 조회 서버인증서 아이디 */ + + "request": { /* 요청 xml의 파라미터 시작과 끝 태그 */ + "start": "", + "end": "" + }, + + "response": { /* 응답 xml의 데이터 시작과 끝 태그 */ + "start": "", + "end": "" + } +} \ 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..96e26f3 --- /dev/null +++ b/src/main/resources/intf-conf/gpki.conf @@ -0,0 +1,24 @@ +{ + "license": "C:\\GPKI\\Lic", /* 이용기관 GPKI API 라이센스 디렉토리 */ + + "charset": "UTF-8", /* 문자셋 */ + + "server": { + "local": "SVR3910262001", /* 이용기관 서버 CN */ + "targets": "SVR1311000030" /* 대상기관 서버인증서 아이디, 여러 개일 경우 컴마(,)로 구분 */ + }, + + "ldapUrl": "ldap://152.99.57.127:389/cn=", /* 대상기관 인증서 다운로드를 위한 LDAP URL */ + "certDir": "C:\\GPKI\\Certificate\\class1", /* 서버 인증서, 키 저장 디렉토리 */ + + "env": { /* 이용기관 서버 인증서 */ + "certFile": "SVR3910262001_env.cer", + "privateKeyFile": "SVR3910262001_env.key", + "privateKeyPassword": "wkddodls3322!" + }, + "sig": { /* 이용기관 서버 전자서명 */ + "certFile": "SVR3910262001_sig.cer", + "privateKeyFile": "SVR3910262001_sig.key", + "privateKeyPassword": "wkddodls3322!" + } +} \ No newline at end of file diff --git a/src/main/resources/template/disabled-parking-request.xml b/src/main/resources/template/disabled-parking-request.xml new file mode 100644 index 0000000..b3548d1 --- /dev/null +++ b/src/main/resources/template/disabled-parking-request.xml @@ -0,0 +1,20 @@ + + +
+ + DisabledParkingYnService + 3910404CMC + SVR1311000030 + {transactionID} + + + +
+ + + 3910000 + ERPTC901SSI638W22183 + {carsNo} + + +
\ No newline at end of file diff --git a/src/test/java/cokr/xit/interfaces/disabledparking/service/DisabledParkingServiceTest.java b/src/test/java/cokr/xit/interfaces/disabledparking/service/DisabledParkingServiceTest.java new file mode 100644 index 0000000..fd48e4d --- /dev/null +++ b/src/test/java/cokr/xit/interfaces/disabledparking/service/DisabledParkingServiceTest.java @@ -0,0 +1,57 @@ +package cokr.xit.interfaces.disabledparking.service; + +import java.util.Map; + +import javax.annotation.Resource; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import cokr.xit.foundation.test.TestSupport; +import cokr.xit.interfaces.disabledparking.service.bean.DisabledParkingBean; +import cokr.xit.interfaces.disabledparking.service.bean.GPKI; + +public class DisabledParkingServiceTest extends TestSupport { + @Resource(name = "disabledParkingService") + private DisabledParkingService service; + + @Test + void gpkConfig() { + GPKI.Config conf = GPKI.Config.get(); + System.out.println("charset: " + conf.getCharset()); + System.out.println("licence: " + conf.getLicense()); + System.out.println("ldap url: " + conf.getLdapUrl()); + System.out.println("cert dir: " + conf.getCertDir()); + + System.out.println("local server: " + conf.getLocalServer()); + System.out.println("target servers: " + conf.getTargetServers()); + + for (String type: new String[] {"env", "sig"}) { + System.out.println(type + " certFile: " + conf.getCertFile(type)); + System.out.println(type + " privateKeyFile: " + conf.getPrivateKeyFile(type)); + System.out.println(type + " privateKeyPassword: " + conf.getPrivateKeyPassword(type)); + } + } + + @Test + void disabledParkingConfig() { + DisabledParkingBean.Config conf = DisabledParkingBean.Config.get(); + System.out.println("serviceUrl: " + conf.getServiceUrl()); + System.out.println("targetServer: " + conf.getTargetServer()); + System.out.println("request: " + conf.getRequest()); + System.out.println("response: " + conf.getResponse()); + } + + @Test + void getParkingYn() { + Map.of( + "xx가xxxx", "N", + "47너7721", "N", + "56저2472", "Y" + ) + .forEach((k, v) -> { + Map result = service.getParkingInfo(k); + Assertions.assertEquals(v, result.get("PARKING_PSBL_YN")); + }); + } +} \ No newline at end of file