최초 커밋
commit
6e4576934f
Binary file not shown.
@ -0,0 +1,154 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>cokr.xit.interfaces</groupId>
|
||||
<artifactId>xit-disabled-parking</artifactId>
|
||||
<version>23.04.01-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>xit-disabled-parking</name>
|
||||
<url>http://maven.apache.org</url>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
|
||||
<java.version>17</java.version>
|
||||
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||
<maven.compiler.target>${java.version}</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>mvn2s</id>
|
||||
<url>https://repo1.maven.org/maven2/</url>
|
||||
<releases>
|
||||
<enabled>true</enabled>
|
||||
</releases>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>egovframe</id>
|
||||
<url>https://maven.egovframe.go.kr/maven/</url>
|
||||
<releases>
|
||||
<enabled>true</enabled>
|
||||
</releases>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>maven-public</id>
|
||||
<url>https://nas.xit.co.kr:8888/repository/maven-public/</url>
|
||||
</repository>
|
||||
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cokr.xit.base</groupId>
|
||||
<artifactId>xit-foundation</artifactId>
|
||||
<version>23.04.01-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>libgpkiapi_jni_1.5</groupId>
|
||||
<artifactId>libgpkiapi_jni_1.5</artifactId>
|
||||
<version>1.0</version>
|
||||
<scope>system</scope>
|
||||
<systemPath>${project.basedir}/lib/libgpkiapi_jni_1.5.jar</systemPath>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mariadb.jdbc</groupId>
|
||||
<artifactId>mariadb-java-client</artifactId>
|
||||
<version>2.7.2</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<defaultGoal>install</defaultGoal>
|
||||
<directory>${basedir}/target</directory>
|
||||
<finalName>${artifactId}-${version}</finalName>
|
||||
|
||||
<resources>
|
||||
<resource><directory>${basedir}/src/main/resources</directory></resource>
|
||||
</resources>
|
||||
<testResources>
|
||||
<testResource><directory>${basedir}/src/test/resources</directory></testResource>
|
||||
<testResource><directory>${basedir}/src/main/resources</directory></testResource>
|
||||
</testResources>
|
||||
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>3.3.0</version>
|
||||
<configuration>
|
||||
<includes>
|
||||
<include>**/*.class</include>
|
||||
</includes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<!-- EMMA -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>3.0.0</version>
|
||||
<configuration>
|
||||
<skipTests>true</skipTests>
|
||||
<reportFormat>xml</reportFormat>
|
||||
<excludes>
|
||||
<exclude>**/Abstract*.java</exclude>
|
||||
<exclude>**/*Suite.java</exclude>
|
||||
</excludes>
|
||||
<includes>
|
||||
<include>**/*Test.java</include>
|
||||
</includes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>emma-maven-plugin</artifactId>
|
||||
<inherited>true</inherited>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>2.2</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-sources</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<!-- Javadoc -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>2.9.1</version>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<!-- Nexus deploy -->
|
||||
<distributionManagement>
|
||||
<snapshotRepository>
|
||||
<id>maven-snapshot</id>
|
||||
<url>https://nas.xit.co.kr:8888/repository/maven-snapshots/</url>
|
||||
</snapshotRepository>
|
||||
|
||||
<repository>
|
||||
<id>maven-release</id>
|
||||
<url>https://nas.xit.co.kr:8888/repository/maven-releases/</url>
|
||||
</repository>
|
||||
</distributionManagement>
|
||||
<!-- Nexus deploy -->
|
||||
|
||||
</project>
|
@ -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<DataObject> getParkingList(String... vehicleNos);
|
||||
}
|
@ -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. 올바로 동작하려면
|
||||
* <ul><li>{@link Config intf-conf/disabled-parking.conf}</li>
|
||||
* <li>{@link GPKI}</li>
|
||||
* <li>{@link Config 요청 xml 템플릿}</li>
|
||||
* </ul>
|
||||
* 을 설정해야 한다.
|
||||
* @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<DataObject> getParkingList(String... vehicleNos) {
|
||||
if (isEmpty(vehicleNos))
|
||||
return Collections.emptyList();
|
||||
|
||||
return Stream.of(vehicleNos)
|
||||
.distinct()
|
||||
.map(this::getParkingInfo)
|
||||
.toList();
|
||||
}
|
||||
|
||||
private String request(String xml) {
|
||||
HttpResponse<String> 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<String, String> 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 포맷으로 된 다음 내용을 적재한다.
|
||||
* <pre><code> {
|
||||
* "template": "장애인 표지 조회를 위한 요청 xml 템플릿의 클래스패스 상의 경로",
|
||||
* "serviceUrl": "장애인 표지 조회 서비스 url",
|
||||
* "targetServer": "장애인 표지 조회 서버 아이디(인증키)",
|
||||
* "request": {
|
||||
* "start": "요청 xml의 파라미터 시작 태그",
|
||||
* "end": "요청 xml의 파라미터 끝 태그"
|
||||
* },
|
||||
* "response": {
|
||||
* "start": "응답 xml의 데이터 시작 태그",
|
||||
* "end": "응답 xml의 데이터 끝 태그"
|
||||
* }
|
||||
* }</code></pre>
|
||||
* 요청 xml 템플릿(template/disabled-parking-request.xml)으로는 다음 내용을 설정한다.
|
||||
* <ul><li>commonHeader
|
||||
* <ul><li>serviceName - 서비스 이름</li>
|
||||
* <li>useSystemCode - 이용 시스템 코드</li>
|
||||
* <li>certServerId - 이용기관 GPKI 서버인증서 아이디</li>
|
||||
* <li>transactionUniqueId - {transactionID}, 프로그램이 생성하는 트랜잭션 아이디, 수정금지</li>
|
||||
* <li>userDeptCode - 이용자 부서코드</li>
|
||||
* <li>userName - 이용자 이름</li>
|
||||
* </ul>
|
||||
* </li>
|
||||
* <li>Body / getDisabledParkingYn
|
||||
* <ul><li>ReqOrgCd - 요청기관 코드</li>
|
||||
* <li>ReqBizCd - 요청업무 코드</li>
|
||||
* <li>CARS_NO - {carsNo}, 이용자가 프로그램으로 전달하는 차량번호, 수정금지</li>
|
||||
* </ul>
|
||||
* </li>
|
||||
* </ul>
|
||||
* @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<String, String>
|
||||
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<String, String> getRequest() {
|
||||
return request;
|
||||
}
|
||||
|
||||
/**요청 xml의 파라미터의 시작과 끝 태그를 설정한다.
|
||||
* @param request 요청 xml의 파라미터의 시작과 끝 태그
|
||||
*/
|
||||
public void setRequest(Map<String, String> request) {
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
/**요청 xml의 결과값의 시작과 끝 태그를 반환한다.
|
||||
* @return 요청 xml의 결과값의 시작과 끝 태그
|
||||
*/
|
||||
public Map<String, String> getResponse() {
|
||||
return response;
|
||||
}
|
||||
|
||||
/**요청 xml의 결과값의 시작과 끝 태그를 설정한다.
|
||||
* @param response 요청 xml의 결과값의 시작과 끝 태그
|
||||
*/
|
||||
public void setResponse(Map<String, String> response) {
|
||||
this.response = response;
|
||||
}
|
||||
}
|
||||
}
|
@ -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<DataObject> getParkingList(String... vehicleNos) {
|
||||
return bean.getParkingList(vehicleNos);
|
||||
}
|
||||
}
|
@ -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<String, X509Certificate> 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> T call(IntSupplier work, Supplier<T> 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 포맷으로 된 다음 내용을 적재한다.
|
||||
* <pre><code> {
|
||||
* "license": "이용기관 GPKI API 라이센스 디렉토리",
|
||||
* "charset": "문자셋",
|
||||
* "server": {
|
||||
* "local": "이용기관 서버 CN",
|
||||
* "targets": "대상기관 서버인증서 아이디, 여러 개일 경우 컴마(,)로 구분"
|
||||
* },
|
||||
* "ldapUrl": "대상기관 인증서 다운로드를 위한 LDAP URL",
|
||||
* "certDir": "서버 인증서, 키 저장 디렉토리",
|
||||
* "env": {
|
||||
* "certFile": "이용기관 서버 인증서 파일",
|
||||
* "privateKeyFile": "이용기관 서버 인증서 키 파일",
|
||||
* "privateKeyPassword": "이용기관 서버 인증서 비밀번호"
|
||||
* },
|
||||
* "sig": {
|
||||
* "certFile": "이용기관 서버 전자서명 파일",
|
||||
* "privateKeyFile": "이용기관 서버 전자서명 키 파일",
|
||||
* "privateKeyPassword": "이용기관 서버 전자서명 비밀번호"
|
||||
* }
|
||||
* }</code></pre>
|
||||
* @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<String, String>
|
||||
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<String, String> getServer() {
|
||||
return ifEmpty(server, Collections::emptyMap);
|
||||
}
|
||||
|
||||
public String getLocalServer() {
|
||||
return getServer().get("local");
|
||||
}
|
||||
|
||||
public List<String> getTargetServers() {
|
||||
String targets = ifEmpty(getServer().get("targets"), "");
|
||||
return Stream.of(targets.split(",")).map(String::trim).toList();
|
||||
}
|
||||
|
||||
/**server을(를) 설정한다.
|
||||
* @param server server
|
||||
*/
|
||||
public void setServer(Map<String, String> server) {
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
/**env을(를) 반환한다.
|
||||
* @return env
|
||||
*/
|
||||
public Map<String, String> getEnv() {
|
||||
return ifEmpty(env, Collections::emptyMap);
|
||||
}
|
||||
|
||||
/**env을(를) 설정한다.
|
||||
* @param env env
|
||||
*/
|
||||
public void setEnv(Map<String, String> env) {
|
||||
this.env = env;
|
||||
}
|
||||
|
||||
/**sig을(를) 반환한다.
|
||||
* @return sig
|
||||
*/
|
||||
public Map<String, String> getSig() {
|
||||
return ifEmpty(sig, Collections::emptyMap);
|
||||
}
|
||||
|
||||
/**sig을(를) 설정한다.
|
||||
* @param sig sig
|
||||
*/
|
||||
public void setSig(Map<String, String> sig) {
|
||||
this.sig = sig;
|
||||
}
|
||||
|
||||
private Map<String, String> 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");
|
||||
}
|
||||
}
|
||||
}
|
@ -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"
|
||||
* <pre><code> {
|
||||
* "parkingInfo": "장애인 표지 정보"
|
||||
* }</code></pre>
|
||||
*/
|
||||
@GetMapping(name = "장애인 표지 조회", value = "/parkingInfo.do")
|
||||
public ModelAndView getParkingInfo(String vehicleNo) {
|
||||
return new ModelAndView("jsonView")
|
||||
.addObject("parkingInfo", service.getParkingInfo(vehicleNo));
|
||||
}
|
||||
|
||||
/**지정한 차량번호들의 장애인 표지 여부를 조회한다.
|
||||
* @param vehicleNos 차량번호
|
||||
* @return "jsonView"
|
||||
* <pre><code> {
|
||||
* "parkingList": "장애인 표지 목록"
|
||||
* }</code></pre>
|
||||
*/
|
||||
@GetMapping(name = "장애인 표지 목록 조회", value = "/parkingList.do")
|
||||
public ModelAndView getParkingList(String... vehicleNos) {
|
||||
return new ModelAndView("jsonView")
|
||||
.addObject("parkingList", service.getParkingList(vehicleNos));
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
/message/
|
||||
/spring/
|
||||
/sql/
|
@ -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": "<getDisabledParkingYn xmlns=\"http://ccais.mopas.go.kr/dh/rid/services/swsdn/DisabledParkingYn/types\">",
|
||||
"end": "</getDisabledParkingYn>"
|
||||
},
|
||||
|
||||
"response": { /* 응답 xml의 데이터 시작과 끝 태그 */
|
||||
"start": "<getDisabledParkingYnResponse xmlns=\"http://ccais.mopas.go.kr/dh/rid/services/swsdn/DisabledParkingYn/types\">",
|
||||
"end": "</getDisabledParkingYnResponse>"
|
||||
}
|
||||
}
|
@ -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!"
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">
|
||||
<Header>
|
||||
<commonHeader xmlns="http://ccais.mopas.go.kr/dh/rid/services/swsdn/DisabledParkingYn/types">
|
||||
<serviceName>DisabledParkingYnService</serviceName>
|
||||
<useSystemCode>3910404CMC</useSystemCode>
|
||||
<certServerId>SVR1311000030</certServerId>
|
||||
<transactionUniqueId>{transactionID}</transactionUniqueId>
|
||||
<userDeptCode></userDeptCode>
|
||||
<userName></userName>
|
||||
</commonHeader>
|
||||
</Header>
|
||||
<Body>
|
||||
<getDisabledParkingYn xmlns="http://ccais.mopas.go.kr/dh/rid/services/swsdn/DisabledParkingYn/types">
|
||||
<ReqOrgCd>3910000</ReqOrgCd>
|
||||
<ReqBizCd>ERPTC901SSI638W22183</ReqBizCd>
|
||||
<CARS_NO>{carsNo}</CARS_NO>
|
||||
</getDisabledParkingYn>
|
||||
</Body>
|
||||
</Envelope>
|
@ -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<String, Object> result = service.getParkingInfo(k);
|
||||
Assertions.assertEquals(v, result.get("PARKING_PSBL_YN"));
|
||||
});
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue