mirror of
https://github.com/Weasley-J/chinaport-data-signature.git
synced 2024-04-21 12:41:53 +00:00
[feature] 优化模块命名
This commit is contained in:
@@ -77,31 +77,30 @@
|
||||
|
||||

|
||||
|
||||
- 加签失败邮件通知告警
|
||||
- 加签失败邮件告警
|
||||
|
||||
> [此功能需手动开启](https://github.com/Weasley-J/chinaport-data-signature/blob/main/chinaport-data-signature-app/src/main/resources/application-dev.yml#L25-L43),开关属性: `spring.mail.enable`, 取值: `on/off`
|
||||
> [此功能需手动开启](https://github.com/Weasley-J/chinaport-data-signature/blob/main/chinaport-data-signature-webapp/src/main/resources/application-dev.yml#L25-L43),开关属性: `spring.mail.enable`, 取值: `on/off`,也可以告警消息接入企业微信、钉钉、飞书、Discord等企业沟通软件。
|
||||
|
||||
<figure>
|
||||
<img src="https://weasley.oss-cn-shanghai.aliyuncs.com/Photos/iShot_2023-08-10_13.56.34.png" alt="邮件通知效果">
|
||||
<figcaption>加签失败邮件通知</figcaption>
|
||||
</figure>
|
||||
|
||||
|
||||
|
||||
|
||||
## 1 支持的电子口岸
|
||||
|
||||
> [核心加签接口](https://github.com/Weasley-J/chinaport-data-signature/blob/main/%E4%B8%AD%E5%9B%BD%E7%94%B5%E5%AD%90%E5%8F%A3%E5%B2%B8%E6%8A%A5%E6%96%87%E5%8A%A0%E7%AD%BE%E6%8E%A5%E5%8F%A3%E6%96%87%E6%A1%A3.md#%E6%B5%B7%E5%85%B3%E6%95%B0%E6%8D%AE%E5%8A%A0%E7%AD%BE)理论上支持中国内地的大部分**地方电子口岸**
|
||||
|
||||
| 电子口岸 | 是否支持 | 是否验证 |
|
||||
| ------------ | -------- |-----|
|
||||
| 海口电子口岸 | ✅ | ✅ |
|
||||
| 广州电子口岸 | ✅ | ✅ |
|
||||
| 郑州电子口岸 | ✅ | 待验证 |
|
||||
| 上海电子口岸 | ✅ | 待验证 |
|
||||
| 浙江电子口岸 | ✅ | 待验证 |
|
||||
| ... | | |
|
||||
|
||||
> [核心加签接口](https://github.com/Weasley-J/chinaport-data-signature/blob/main/%E4%B8%AD%E5%9B%BD%E7%94%B5%E5%AD%90%E5%8F%A3%E5%B2%B8%E6%8A%A5%E6%96%87%E5%8A%A0%E7%AD%BE%E6%8E%A5%E5%8F%A3%E6%96%87%E6%A1%A3.md#%E6%B5%B7%E5%85%B3%E6%95%B0%E6%8D%AE%E5%8A%A0%E7%AD%BE)
|
||||
> 理论上支持中国内地的大部分**地方电子口岸**
|
||||
|
||||
| 电子口岸 | 是否支持 | 是否验证 |
|
||||
|--------|------|------|
|
||||
| 海口电子口岸 | ✅ | ✅ |
|
||||
| 广州电子口岸 | ✅ | ✅ |
|
||||
| 郑州电子口岸 | ✅ | 待验证 |
|
||||
| 上海电子口岸 | ✅ | 待验证 |
|
||||
| 杭州电子口岸 | ✅ | 待验证 |
|
||||
| ... | | |
|
||||
|
||||
## 2 项目代码质量
|
||||
|
||||
@@ -123,23 +122,23 @@
|
||||
> 本应用是标准的`springboot`应用,支持`springboot`
|
||||
> 的所有配置参数,推荐通过在[线`yaml`转`properties`](https://www.toyaml.com/index.html)将配置元数据复制到命令行参数中按需使用,
|
||||
|
||||
- [推荐元数据配置示例](https://github.com/Weasley-J/chinaport-data-signature/blob/main/chinaport-data-signature-app/src/main/resources/application-dev.yml#L43-L71)
|
||||
- [推荐元数据配置示例](https://github.com/Weasley-J/chinaport-data-signature/blob/main/chinaport-data-signature-webapp/src/main/resources/application-dev.yml#L43-L71)
|
||||
|
||||
- [加签异常邮件通知配置示例](https://github.com/Weasley-J/chinaport-data-signature/blob/main/chinaport-data-signature-app/src/main/resources/application-dev.yml#L23-L41)
|
||||
- [加签异常邮件通知配置示例](https://github.com/Weasley-J/chinaport-data-signature/blob/main/chinaport-data-signature-webapp/src/main/resources/application-dev.yml#L23-L41)
|
||||
|
||||
| 配置参数 | 是否必须(Y/N) | 解释 |
|
||||
| ------------------------------------------------ | --------------- | ------------------------------------------------------------ |
|
||||
| eport.signature.algorithm | N | [XML签名的算法类型](https://github.com/Weasley-J/chinaport-data-signature/blob/main/chinaport-data-signature-app/src/main/java/cn/alphahub/eport/signature/config/SignatureAlgorithmProperties.java#L20),不指定时使用程序自动推断的算法, 推荐使用程序自动推断的算法 |
|
||||
| eport.signature.algorithm | N | [XML签名的算法类型](https://github.com/Weasley-J/chinaport-data-signature/blob/main/chinaport-data-signature-webapp/src/main/java/cn/alphahub/eport/signature/config/SignatureAlgorithmProperties.java#L20),不指定时使用程序自动推断的算法, 推荐使用程序自动推断的算法 |
|
||||
| eport.signature.ukey.ws-url | Y | `u-key`所连接的`Windows`电脑的`socket`链接`url`,如: `ws://127.0.0.1:61232`,下载`release`直接运行的修改全局配置的`UKEY_HOST`即可 |
|
||||
| eport.signature.ukey.password | N | u-key密码的密码,默认: `88888888`, 如果密码改过,需要指定下,下载`release`直接运行的修改全局配置的`UKEY_PASSWORD`即可 |
|
||||
| eport.signature.ukey.health.endpoint.client-name | N | Windows上重启的ukey可执行文件全限定文件名称,不指定将自动查找(将程序安装到Windows平台上会生效,Linux、Uni环境无效) |
|
||||
| eport.signature.auth.enable | N | [是否启用token鉴权](https://github.com/Weasley-J/chinaport-data-signature/blob/main/chinaport-data-signature-app/src/main/java/cn/alphahub/eport/signature/config/AuthenticationProperties.java#L17),取值:<u>on/off</u>,生产环境建议开启 |
|
||||
| eport.signature.auth.token | N | 客户端请求鉴权token, 默认值: DefaultAuthToken, [请求头](https://github.com/Weasley-J/chinaport-data-signature/blob/main/chinaport-data-signature-app/src/main/java/cn/alphahub/eport/signature/config/AuthenticationProperties.java#L24),<u>生产环境不建议使用默认值</u> |
|
||||
| eport.signature.report.ceb-message.cop-code | Y | [电子口岸XML报文](https://github.com/Weasley-J/chinaport-data-signature/blob/main/chinaport-data-signature-app/src/main/java/cn/alphahub/eport/signature/config/ChinaEportProperties.java#L20)传输企业代码 |
|
||||
| eport.signature.auth.enable | N | [是否启用token鉴权](https://github.com/Weasley-J/chinaport-data-signature/blob/main/chinaport-data-signature-webapp/src/main/java/cn/alphahub/eport/signature/config/AuthenticationProperties.java#L17),取值:<u>on/off</u>,生产环境建议开启 |
|
||||
| eport.signature.auth.token | N | 客户端请求鉴权token, 默认值: DefaultAuthToken, [请求头](https://github.com/Weasley-J/chinaport-data-signature/blob/main/chinaport-data-signature-webapp/src/main/java/cn/alphahub/eport/signature/config/AuthenticationProperties.java#L24),<u>生产环境不建议使用默认值</u> |
|
||||
| eport.signature.report.ceb-message.cop-code | Y | [电子口岸XML报文](https://github.com/Weasley-J/chinaport-data-signature/blob/main/chinaport-data-signature-webapp/src/main/java/cn/alphahub/eport/signature/config/ChinaEportProperties.java#L20)传输企业代码 |
|
||||
| eport.signature.report.ceb-message.cop-name | Y | 传输企业名称,报文传输的企业名称 |
|
||||
| eport.signature.report.ceb-message.dxp-id | Y | 文传输编号,向中国电子口岸数据中心申请数据交换平台的用户编号 |
|
||||
| eport.signature.report.ceb-message.server | N | [海关服务器地址](https://github.com/Weasley-J/chinaport-data-signature/blob/main/chinaport-data-signature-app/src/main/java/cn/alphahub/eport/signature/config/ChinaEportProperties.java#L43),<u>缺省则采用Client中的密文作文默认Server URL</u> |
|
||||
| eport.signature.report.customs179.ebp-code | Y | [海关 179 数据上报](https://github.com/Weasley-J/chinaport-data-signature/blob/main/chinaport-data-signature-app/src/main/java/cn/alphahub/eport/signature/config/Customs179Properties.java#L24)的电商平台代码 |
|
||||
| eport.signature.report.ceb-message.server | N | [海关服务器地址](https://github.com/Weasley-J/chinaport-data-signature/blob/main/chinaport-data-signature-webapp/src/main/java/cn/alphahub/eport/signature/config/ChinaEportProperties.java#L43),<u>缺省则采用Client中的密文作文默认Server URL</u> |
|
||||
| eport.signature.report.customs179.ebp-code | Y | [海关 179 数据上报](https://github.com/Weasley-J/chinaport-data-signature/blob/main/chinaport-data-signature-webapp/src/main/java/cn/alphahub/eport/signature/config/Customs179Properties.java#L24)的电商平台代码 |
|
||||
| eport.signature.report.customs179.server | N | 数据上报服务器URL地址,链接格式: https://域名联系海关/ceb2grab/grab/realTimeDataUpload,<u>没有配置的话采用项目内置的URL密文地址</u> |
|
||||
|
||||
## 5 项目启动脚本
|
||||
@@ -156,7 +155,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Environment args
|
||||
APP="chinaport-data-signature-app"
|
||||
APP="chinaport-data-signature-webapp"
|
||||
UKEY_HOST="127.0.0.1"
|
||||
UKEY_PASSWORD="88888888"
|
||||
JVM_ARGS="-Xms1g -Xmx1g"
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
FROM azul/zulu-openjdk:17-jre
|
||||
MAINTAINER weasley
|
||||
WORKDIR /app
|
||||
COPY target/chinaport-data-signature-*.jar /app/chinaport-data-signature.jar
|
||||
EXPOSE 8080
|
||||
ENV TZ=PRC
|
||||
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ >/etc/timezone
|
||||
|
||||
ENV JAVA_OPTS=""
|
||||
ENV SPRING_ARGS=""
|
||||
ENTRYPOINT ["sh", "-c", "java ${JVM_OPTIONS} -jar chinaport-data-signature.jar ${SPRING_ARGS}"]
|
||||
@@ -1,8 +0,0 @@
|
||||
#!/bin/bash
|
||||
TAG="1.1.1"
|
||||
docker stop chinaport-data-signature && docker rm -f chinaport-data-signature
|
||||
docker rmi -f weasleyj/chinaport-data-signature:${TAG}-arm64
|
||||
docker build -t weasleyj/chinaport-data-signature:${TAG}-arm64 . -f ./Dockerfile
|
||||
docker login -u weasleyj
|
||||
docker push weasleyj/chinaport-data-signature:${TAG}-arm64
|
||||
exit 0
|
||||
@@ -1,8 +0,0 @@
|
||||
#!/bin/bash
|
||||
TAG="latest"
|
||||
docker stop chinaport-data-signature && docker rm -f chinaport-data-signature
|
||||
docker rmi -f weasleyj/chinaport-data-signature:${TAG}
|
||||
docker build -t weasleyj/chinaport-data-signature:${TAG} . -f ./Dockerfile
|
||||
docker login -u weasleyj
|
||||
docker push weasleyj/chinaport-data-signature:${TAG}
|
||||
exit 0
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
package cn.alphahub.eport.signature;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.web.socket.config.annotation.EnableWebSocket;
|
||||
|
||||
/**
|
||||
* 中国电子口岸(海口海关)报文加签推送应用
|
||||
*
|
||||
* @author Weasley J
|
||||
* @date 2022-02-13
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@EnableWebSocket
|
||||
@SpringBootApplication
|
||||
public class ChinaEportSignatureApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ChinaEportSignatureApplication.class, args);
|
||||
}
|
||||
}
|
||||
-41
@@ -1,41 +0,0 @@
|
||||
package cn.alphahub.eport.signature.base.exception;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
/**
|
||||
* The eport web client exception
|
||||
*
|
||||
* @author weasley
|
||||
* @version 1.1.0
|
||||
*/
|
||||
@Getter
|
||||
public class EportWebClientException extends RuntimeException {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
/**
|
||||
* 异常消息
|
||||
*/
|
||||
private String msg;
|
||||
/**
|
||||
* 错误码, 默认: 500
|
||||
*/
|
||||
private int code = 500;
|
||||
|
||||
public EportWebClientException(String msg) {
|
||||
super(msg);
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public EportWebClientException(String msg, Throwable e) {
|
||||
super(msg, e);
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public EportWebClientException(String msg, int code, Throwable e) {
|
||||
super(msg, e);
|
||||
this.msg = msg;
|
||||
this.code = code;
|
||||
}
|
||||
}
|
||||
-49
@@ -1,49 +0,0 @@
|
||||
package cn.alphahub.eport.signature.base.exception;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
/**
|
||||
* 自定义异常
|
||||
*
|
||||
* @author liuwenjing
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class SignException extends RuntimeException {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 异常消息
|
||||
*/
|
||||
private String msg;
|
||||
|
||||
/**
|
||||
* 错误码, 默认500
|
||||
*/
|
||||
private int code = 500;
|
||||
|
||||
public SignException(String msg) {
|
||||
super(msg);
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public SignException(String msg, Throwable e) {
|
||||
super(msg, e);
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public SignException(String msg, int code, Throwable e) {
|
||||
super(msg, e);
|
||||
this.msg = msg;
|
||||
this.code = code;
|
||||
}
|
||||
}
|
||||
-44
@@ -1,44 +0,0 @@
|
||||
package cn.alphahub.eport.signature.config;
|
||||
|
||||
import cn.alphahub.eport.signature.core.ChinaEportReportClient;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
/**
|
||||
* 电子口岸XML报文传输的企业元数据配置
|
||||
*
|
||||
* @author weasley
|
||||
* @version 1.1.0
|
||||
*/
|
||||
@Data
|
||||
@Validated
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = ChinaEportProperties.PREFIX)
|
||||
public class ChinaEportProperties {
|
||||
public static final String PREFIX = "eport.signature.report.ceb-message";
|
||||
/**
|
||||
* 传输企业代码,报文传输的企业代码(需要与接入客户端的企业身份一致)
|
||||
*/
|
||||
@NotBlank(message = "配置文件中的报文传输的企业代码不能为空")
|
||||
private String copCode;
|
||||
/**
|
||||
* 传输企业名称,报文传输的企业名称
|
||||
*/
|
||||
@NotBlank(message = "报文传输的企业名称不能为空")
|
||||
private String copName;
|
||||
/**
|
||||
* 报文传输编号,向中国电子口岸数据中心申请数据交换平台的用户编号
|
||||
*/
|
||||
@NotBlank(message = "向中国电子口岸数据中心申请数据交换平台的用户编号不能为空")
|
||||
private String dxpId;
|
||||
/**
|
||||
* 海关服务器地址,缺省则采用Client中的密文作文默认Server URL
|
||||
*
|
||||
* @apiNote CEMMessage XML数据上报服务器地址,格式: http://ip:port
|
||||
* @see ChinaEportReportClient#EPORT_CEBMESSAGE_SERVER_ENCODE
|
||||
*/
|
||||
private String server;
|
||||
}
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
package cn.alphahub.eport.signature.config;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
/**
|
||||
* 海关 179 数据上报配置元数据
|
||||
*
|
||||
* @author weasley
|
||||
* @version 1.1.0
|
||||
* @apiNote 如果接口没有传过来, 就取
|
||||
*/
|
||||
@Data
|
||||
@Validated
|
||||
@ConfigurationProperties(prefix = Customs179Properties.PREFIX)
|
||||
public class Customs179Properties {
|
||||
public static final String PREFIX = "eport.signature.report.customs179";
|
||||
/**
|
||||
* 电商平台代码
|
||||
*
|
||||
* @apiNote 电商平台海关注册登记编号或统一社会信用代码。境内企业需据实填写海关注册登记编号或统一社会信用代码,境外平台企业如无法提供电商平台代码可填“无”。
|
||||
*/
|
||||
@NotBlank(message = "电商平台代码不能为空;如果你想对接CEBMessage进出口单, 先随便填一个,后面改成正确的")
|
||||
private String ebpCode;
|
||||
/**
|
||||
* 数据上报服务器URL地址,链接格式: https://域名联系海关获取/ceb2grab/grab/realTimeDataUpload
|
||||
*
|
||||
* @apiNote 没有配置的话采用项目内置的URL地址
|
||||
*/
|
||||
private String server;
|
||||
}
|
||||
-45
@@ -1,45 +0,0 @@
|
||||
package cn.alphahub.eport.signature.config;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* XML Signature Algorithm
|
||||
*
|
||||
* @author weasley
|
||||
* @version 1.0.0
|
||||
* @apiNote 签名算法
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum SignatureAlgorithm {
|
||||
RSA_SHA1("rsa-sha1", "SHA1WITHRSA", "http://www.w3.org/2000/09/xmldsig#rsa-sha1"),
|
||||
SM2_SM3("sm2-sm3", "SM3WITHSM2", "http://www.w3.org/2000/09/xmldsig#sm2-sm3"),
|
||||
;
|
||||
/**
|
||||
* 算法类型
|
||||
*/
|
||||
private final String algorithmType;
|
||||
/**
|
||||
* 算法名称
|
||||
*/
|
||||
private final String algorithmName;
|
||||
/**
|
||||
* XML代码段中 ds:SignatureMethod 的算法值
|
||||
* <p>
|
||||
* XML Signature Method:
|
||||
* <pre>
|
||||
* {@code <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#${algorithmType}"/>}
|
||||
* </pre>
|
||||
*/
|
||||
private final String signatureMethod;
|
||||
|
||||
public static SignatureAlgorithm getSignatureAlgorithmSigAlgName(String sigAlgName) {
|
||||
return Arrays.stream(values())
|
||||
.filter(original -> Objects.equals(original.getAlgorithmName(), sigAlgName))
|
||||
.findFirst().orElse(null);
|
||||
}
|
||||
}
|
||||
-158
@@ -1,158 +0,0 @@
|
||||
package cn.alphahub.eport.signature.config;
|
||||
|
||||
import cn.alphahub.eport.signature.support.CommandClient;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.apache.logging.log4j.util.Strings;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.alphahub.eport.signature.config.ICommand.EPORT_ACCESS_CONTROL;
|
||||
import static cn.alphahub.eport.signature.config.ICommand.EPORT_WS_SERVER;
|
||||
import static cn.alphahub.eport.signature.config.UkeyProperties.PREFIX;
|
||||
|
||||
/**
|
||||
* An interface to build command
|
||||
*/
|
||||
@FunctionalInterface
|
||||
interface ICommand extends Serializable {
|
||||
/**
|
||||
* WebSocketServer.exe
|
||||
*/
|
||||
String EPORT_WS_SERVER = "CnEport.Pub.WebSocketServer.exe";
|
||||
/**
|
||||
* SetAccessControl.exe
|
||||
*/
|
||||
String EPORT_ACCESS_CONTROL = "SetAccessControl.exe";
|
||||
|
||||
/**
|
||||
* To build command
|
||||
*
|
||||
* @param exeClient e.g: C:\中国电子口岸客户端控件\SetAccessControl.exe
|
||||
* @return command script
|
||||
*/
|
||||
String build(String exeClient);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ukey自我重启相关
|
||||
* <p>
|
||||
* Windows上的可执行文件: C:\中国电子口岸客户端控件\SetAccessControl.exe
|
||||
*
|
||||
* @author weasley
|
||||
* @version 1.0.0
|
||||
* @apiNote 可执行文件 SetAccessControl.exe 的安装位置以实际为准
|
||||
*/
|
||||
@Data
|
||||
@Slf4j
|
||||
@ConfigurationProperties(prefix = PREFIX + ".health.endpoint")
|
||||
public class UkeyAccessClientProperties {
|
||||
|
||||
/**
|
||||
* Windows上重启的ukey可执行文件全限定文件名称
|
||||
*
|
||||
* @apiNote 建议指定 clientName,可以避免全盘查找 SetAccessControl.exe, 提高性能
|
||||
* @apiNote 全限定文件名称,包含文件后缀, 建议不要安装到中文目录里面,如: C:\中国电子口岸客户端控件\SetAccessControl.exe
|
||||
*/
|
||||
private String clientName;
|
||||
|
||||
/**
|
||||
* 查找中国电子口岸客户端控件重启客户端的Windows可执行文件的具体位置
|
||||
*
|
||||
* @return SetAccessControl.exe 文件的绝对路径
|
||||
* @apiNote 以下命令行只适用于Windows平台,其他平台报错直接忽略
|
||||
*/
|
||||
public String findClient() {
|
||||
if (!SystemUtils.IS_OS_WINDOWS) return null;
|
||||
CommandClient sharedInstance = CommandClient.getSharedInstance();
|
||||
|
||||
List<String> accessControlStdOutList;
|
||||
List<String> eportWsServerStdOutList;
|
||||
|
||||
sharedInstance.execute("where " + EPORT_ACCESS_CONTROL);
|
||||
accessControlStdOutList = new ArrayList<>(sharedInstance.getStdOutList());
|
||||
if (CollectionUtils.isEmpty(accessControlStdOutList)) return null;
|
||||
|
||||
sharedInstance.execute("where " + EPORT_WS_SERVER);
|
||||
eportWsServerStdOutList = new ArrayList<>(sharedInstance.getStdOutList());
|
||||
if (CollectionUtils.isEmpty(eportWsServerStdOutList)) return null;
|
||||
|
||||
String accessControlStdOut = FilenameUtils.getFullPathNoEndSeparator(accessControlStdOutList.get(0));
|
||||
String eportWsServerStdOut = FilenameUtils.getFullPathNoEndSeparator(eportWsServerStdOutList.get(0));
|
||||
if (accessControlStdOut.equals(eportWsServerStdOut)) {
|
||||
log.info("找到u-key Windows客户端执行文件: {} {}", EPORT_WS_SERVER, EPORT_ACCESS_CONTROL);
|
||||
return accessControlStdOutList.get(0);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行指令
|
||||
* <pre>
|
||||
* -h 或 -help: 帮助
|
||||
* -restart: 重启控件
|
||||
* -start: 启动控件
|
||||
* -stop: 停止控件
|
||||
* -repair: 修复证书
|
||||
* </pre>
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum Command implements ICommand {
|
||||
/**
|
||||
* 重启控件
|
||||
*/
|
||||
RESTART("-restart", "重启控件"),
|
||||
/**
|
||||
* 启动控件
|
||||
*/
|
||||
START("-start", "启动控件"),
|
||||
/**
|
||||
* 停止控件
|
||||
*/
|
||||
STOP("-stop", "停止控件"),
|
||||
/**
|
||||
* 修复证书
|
||||
*/
|
||||
REPAIR("-repair", "修复证书"),
|
||||
/**
|
||||
* 缺省符,无业务意义
|
||||
*/
|
||||
DEFAULT("", "缺省符");
|
||||
|
||||
/**
|
||||
* 指令
|
||||
*/
|
||||
private final String cmd;
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
private final String desc;
|
||||
|
||||
public String getCmd() {
|
||||
if (RESTART.cmd.equals(cmd)) {
|
||||
return cmd + " -i";
|
||||
}
|
||||
return cmd;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String build(String exeClient) {
|
||||
if (null == exeClient) return Strings.EMPTY;
|
||||
if (StringUtils.isBlank(this.getCmd())) {
|
||||
return "cmd /c \"" + exeClient + "\"";
|
||||
}
|
||||
return "cmd /c \"" + exeClient + " " + this.getCmd() + "\"";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-300
@@ -1,300 +0,0 @@
|
||||
package cn.alphahub.eport.signature.config;
|
||||
|
||||
import cn.alphahub.dtt.plus.util.JacksonUtil;
|
||||
import cn.alphahub.dtt.plus.util.SpringUtil;
|
||||
import cn.alphahub.eport.signature.core.CertificateHandler;
|
||||
import cn.alphahub.eport.signature.core.SignHandler;
|
||||
import cn.alphahub.eport.signature.core.WebSocketClientHandler;
|
||||
import cn.alphahub.eport.signature.entity.SignRequest;
|
||||
import cn.alphahub.eport.signature.entity.SpcValidTime;
|
||||
import cn.alphahub.eport.signature.entity.UkeyRequest;
|
||||
import cn.alphahub.eport.signature.entity.UkeyResponse;
|
||||
import cn.alphahub.eport.signature.entity.WebSocketWrapper;
|
||||
import cn.alphahub.eport.signature.support.CertificateParser;
|
||||
import cn.hutool.core.lang.TypeReference;
|
||||
import lombok.Data;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.web.socket.TextMessage;
|
||||
import org.springframework.web.socket.WebSocketSession;
|
||||
import org.springframework.web.socket.client.WebSocketConnectionManager;
|
||||
import org.springframework.web.socket.client.standard.StandardWebSocketClient;
|
||||
import org.springframework.web.socket.handler.TextWebSocketHandler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.concurrent.locks.LockSupport;
|
||||
|
||||
import static cn.alphahub.eport.signature.core.CertificateHandler.SING_DATA_METHOD;
|
||||
import static cn.hutool.json.JSONUtil.toBean;
|
||||
import static cn.hutool.json.JSONUtil.toJsonStr;
|
||||
|
||||
/**
|
||||
* 初始化配置
|
||||
*
|
||||
* @author lwj
|
||||
* @version 1.0
|
||||
* @date 2022-01-11 15:29
|
||||
*/
|
||||
@Data
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@EnableConfigurationProperties({UkeyProperties.class, EmailProperties.class, SignatureAlgorithmProperties.class, Customs179Properties.class})
|
||||
public class UkeyInitialConfig implements ApplicationRunner {
|
||||
/**
|
||||
* u-key默认密码8个8不要修改
|
||||
*/
|
||||
private static final String DEFAULT_PASSWORD = "88888888";
|
||||
|
||||
/**
|
||||
* 取海关签名证书PEM, X509Certificate证书(未经HASH算法散列过)
|
||||
*
|
||||
* @return 取海关签名证书PEM, X509Certificate证书是加签证书,真正的X509Certificate证书
|
||||
*/
|
||||
public static String getX509CertificateParameter() {
|
||||
UkeyRequest ukeyRequest = new UkeyRequest();
|
||||
ukeyRequest.set_method("cus-sec_SpcGetSignCertAsPEM");
|
||||
ukeyRequest.set_id(1);
|
||||
ukeyRequest.setArgs(new HashMap<>());
|
||||
return JacksonUtil.toJson(ukeyRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用卡计算摘要,返回PEM格式信息, 这个不调也罢,已经硬核计算出来了
|
||||
*
|
||||
* @param sourceXml 不含ds:Signature节点的<ceb:CEBXxxMessage></ceb:CEBXxxMessage>源xml原文数据
|
||||
* @param uniqueId uniqueId 唯一id, 用来区分是哪一次发送的消息,int32,最大32位大于0
|
||||
* @return DigestValue的值
|
||||
*/
|
||||
public static String getDigestValueParameter(String sourceXml, Integer uniqueId) {
|
||||
Map<String, Object> args = new LinkedHashMap<>(2);
|
||||
args.put("szInfo", sourceXml);
|
||||
args.put("passwd", ObjectUtils.defaultIfNull(SpringUtil.getBean(UkeyProperties.class).getPassword(), DEFAULT_PASSWORD));
|
||||
UkeyRequest ukeyRequest = new UkeyRequest();
|
||||
ukeyRequest.set_method("sec_SpcSHA1DigestAsPEM");
|
||||
ukeyRequest.set_id(uniqueId);
|
||||
ukeyRequest.setArgs(args);
|
||||
return JacksonUtil.toJson(ukeyRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* 签名, 不对原文计算摘要, 请您自行计算好摘要传入
|
||||
*
|
||||
* @param request 原文入参
|
||||
* @return SignatureValue的值, 返回的数组,包含您的证书编号,可作为KeyName的值
|
||||
*/
|
||||
public static String getSignDataAsPEM(SignRequest request) {
|
||||
String initData = SignHandler.getInitData(request);
|
||||
log.warn("发送给ukey的真正请求入参: {}\n原始报文: {}", initData, request.getData());
|
||||
@SuppressWarnings({"all"}) UkeyRequest ukeyRequest = new UkeyRequest("cus-sec_SpcSignDataAsPEM", new HashMap<>() {{
|
||||
put("inData", initData);
|
||||
put("passwd", ObjectUtils.defaultIfNull(SpringUtil.getBean(UkeyProperties.class).getPassword(), DEFAULT_PASSWORD));
|
||||
}});
|
||||
ukeyRequest.set_id(request.getId());
|
||||
return JacksonUtil.toJson(ukeyRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用卡计算摘要,返回PEM格式信息
|
||||
*
|
||||
* @return ukey响应数据,示例: B959CD298E3BA6DAE360FB8CDB87B68B66D8BD221474046232910DD5FDA54354
|
||||
*/
|
||||
public static String getSHA1DigestAsPEMParams(SignRequest request) {
|
||||
Map<String, Object> args = new LinkedHashMap<>();
|
||||
String initData = SignHandler.getInitData(request);
|
||||
log.warn("使用卡计算摘要,返回PEM格式信息: \n{}原始报文:\n{}", initData, request.getData());
|
||||
args.put("szInfo", initData);
|
||||
args.put("passwd", ObjectUtils.defaultIfNull(SpringUtil.getBean(UkeyProperties.class).getPassword(), DEFAULT_PASSWORD));
|
||||
UkeyRequest ukeyRequest = new UkeyRequest();
|
||||
ukeyRequest.set_method("cus-sec_SpcSHA1DigestAsPEM");
|
||||
ukeyRequest.set_id(request.getId());
|
||||
ukeyRequest.setArgs(args);
|
||||
return JacksonUtil.toJson(ukeyRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* To park thread
|
||||
*/
|
||||
private static void parkThread(AtomicReference<Thread> reference, WebSocketConnectionManager socketConnectionManager) {
|
||||
socketConnectionManager.start();
|
||||
try {
|
||||
LockSupport.parkNanos(reference.get(), 1000 * 1000 * 1000 * 3L);
|
||||
} catch (Exception e) {
|
||||
log.error("线程自动unpark异常 {}", e.getLocalizedMessage(), e);
|
||||
} finally {
|
||||
socketConnectionManager.stop();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback used to run the bean.
|
||||
*
|
||||
* @param args incoming application arguments
|
||||
*/
|
||||
@Override
|
||||
public void run(ApplicationArguments args) throws IOException {
|
||||
org.apache.xml.security.Init.init();
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("XMl output format with C14n Initial Succeed.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* IOC中返回一个WebSocket包装类的实例对象
|
||||
*
|
||||
* @return WebSocket包装类
|
||||
*/
|
||||
@Bean
|
||||
@Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
|
||||
public WebSocketWrapper webSocketWrapper() {
|
||||
return new WebSocketWrapper();
|
||||
}
|
||||
|
||||
/**
|
||||
* 基于标准 Java WebSocket API 的 WebSocketClient
|
||||
*
|
||||
* @return An instance of StandardWebSocketClient
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean({StandardWebSocketClient.class})
|
||||
public StandardWebSocketClient standardWebSocketClient() {
|
||||
return new StandardWebSocketClient();
|
||||
}
|
||||
|
||||
/**
|
||||
* 注入一个X509Certificate证书判断的Bean
|
||||
*
|
||||
* @return X509Certificate证书判断
|
||||
*/
|
||||
@Bean
|
||||
@SneakyThrows
|
||||
public CertificateHandler certificateHandler(UkeyProperties ukeyProperties, StandardWebSocketClient standardWebSocketClient) {
|
||||
CertificateHandler certificateHandler = new CertificateHandler();
|
||||
|
||||
Map<String, String> certMap = new ConcurrentHashMap<>();
|
||||
AtomicReference<Thread> reference = new AtomicReference<>();
|
||||
reference.set(Thread.currentThread());
|
||||
|
||||
// 获取u-key内的证书相关
|
||||
WebSocketConnectionManager certManager = new WebSocketConnectionManager(standardWebSocketClient, new TextWebSocketHandler() {
|
||||
final UkeyRequest ukeyRequest = new UkeyRequest("cus-sec_SpcGetSignCertAsPEM", new HashMap<>());
|
||||
|
||||
@Override
|
||||
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
|
||||
session.sendMessage(new TextMessage(JacksonUtil.toJson(ukeyRequest)));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleTextMessage(WebSocketSession session, TextMessage message) {
|
||||
UkeyResponse response = toBean(message.getPayload(), new TypeReference<>() {
|
||||
}, true);
|
||||
try {
|
||||
if (Objects.equals(response.get_id(), 1)) {
|
||||
UkeyResponse.Args responseArgs = response.get_args();
|
||||
if (responseArgs.getResult().equals(true) && CollectionUtils.isNotEmpty(responseArgs.getData())) {
|
||||
certMap.put(SING_DATA_METHOD, responseArgs.getData().get(0));
|
||||
log.warn("已从电子口岸u-key中获取到x509Certificate证书: {}", JacksonUtil.toJson(certMap));
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("唤醒线程异常 {}", e.getLocalizedMessage(), e);
|
||||
} finally {
|
||||
LockSupport.unpark(reference.get());
|
||||
}
|
||||
}
|
||||
}, ukeyProperties.getWsUrl());
|
||||
parkThread(reference, certManager);
|
||||
|
||||
// 获取u-key签发日期, 查看海关证书有效期
|
||||
WebSocketConnectionManager ukeyValidTimeManager = new WebSocketConnectionManager(standardWebSocketClient, new TextWebSocketHandler() {
|
||||
@Override
|
||||
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
|
||||
session.sendMessage(new TextMessage(toJsonStr(new UkeyRequest("cus-sec_SpcGetValidTimeFromCert", 1, new LinkedHashMap<>()))));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleTextMessage(WebSocketSession session, TextMessage message) {
|
||||
UkeyResponse response = toBean(message.getPayload(), new TypeReference<>() {
|
||||
}, true);
|
||||
try {
|
||||
if (Objects.equals(response.get_id(), 1)) {
|
||||
UkeyResponse.Args responseArgs = response.get_args();
|
||||
if (responseArgs.getResult().equals(true) && CollectionUtils.isNotEmpty(responseArgs.getData())) {
|
||||
log.warn("从电子口岸u-key中获取到u-key有效期区间: {}", responseArgs.getData());
|
||||
SpcValidTime validTime = toBean(responseArgs.getData().get(0), new TypeReference<>() {
|
||||
}, true);
|
||||
certificateHandler.setUkeyValidTimeBegin(validTime.getSzStartTime());
|
||||
certificateHandler.setUkeyValidTimeEnd(validTime.getSzEndTime());
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("唤醒线程异常 {}", e.getLocalizedMessage(), e);
|
||||
} finally {
|
||||
LockSupport.unpark(reference.get());
|
||||
}
|
||||
}
|
||||
}, ukeyProperties.getWsUrl());
|
||||
parkThread(reference, ukeyValidTimeManager);
|
||||
|
||||
//自动推断ukey使用的签名算法
|
||||
WebSocketConnectionManager certSignatureAlgorithmManager = new WebSocketConnectionManager(standardWebSocketClient, new TextWebSocketHandler() {
|
||||
@Override
|
||||
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
|
||||
log.info("开始推断ukey使用的签名算法:");
|
||||
session.sendMessage(new TextMessage(toJsonStr(new UkeyRequest("cus-sec_SpcGetSignCertAsPEM", new LinkedHashMap<>()))));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleTextMessage(WebSocketSession session, TextMessage message) {
|
||||
UkeyResponse response = toBean(message.getPayload(), new TypeReference<>() {
|
||||
}, true);
|
||||
try {
|
||||
if (Objects.equals(response.get_id(), 1)) {
|
||||
UkeyResponse.Args responseArgs = response.get_args();
|
||||
if (responseArgs.getResult().equals(true) && CollectionUtils.isNotEmpty(responseArgs.getData())) {
|
||||
String x509Certificate = CertificateHandler.buildX509CertificateWithHeader(responseArgs.getData().get(0));
|
||||
X509Certificate certificateByCertText = CertificateParser.parseCertificateByCertText(x509Certificate);
|
||||
if (null != SignatureAlgorithm.getSignatureAlgorithmSigAlgName(certificateByCertText.getSigAlgName())) {
|
||||
certificateHandler.setAlgorithm(SignatureAlgorithm.getSignatureAlgorithmSigAlgName(certificateByCertText.getSigAlgName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("唤醒线程异常 {}", e.getLocalizedMessage(), e);
|
||||
} finally {
|
||||
LockSupport.unpark(reference.get());
|
||||
}
|
||||
}
|
||||
}, ukeyProperties.getWsUrl());
|
||||
parkThread(reference, certSignatureAlgorithmManager);
|
||||
|
||||
certificateHandler.setX509Map(certMap);
|
||||
return certificateHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return An instance of WebSocketClientHandler
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean({WebSocketClientHandler.class})
|
||||
public WebSocketClientHandler webSocketClientHandler(UkeyProperties ukeyProperties, WebSocketWrapper webSocketWrapper, CertificateHandler certificateHandler) {
|
||||
return new WebSocketClientHandler(ukeyProperties, webSocketWrapper, certificateHandler);
|
||||
}
|
||||
|
||||
}
|
||||
-29
@@ -1,29 +0,0 @@
|
||||
package cn.alphahub.eport.signature.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import static cn.alphahub.eport.signature.config.UkeyProperties.PREFIX;
|
||||
|
||||
/**
|
||||
* 电子口岸u-key的配置参数
|
||||
*
|
||||
* @author weasley
|
||||
* @version 1.0
|
||||
* @date 2022/2/13
|
||||
*/
|
||||
@Data
|
||||
@Validated
|
||||
@ConfigurationProperties(prefix = PREFIX)
|
||||
public class UkeyProperties {
|
||||
public static final String PREFIX = "eport.signature.ukey";
|
||||
/**
|
||||
* socket链接url
|
||||
*/
|
||||
private String wsUrl = "ws://127.0.0.1:61232";
|
||||
/**
|
||||
* u-key密码
|
||||
*/
|
||||
private String password = "88888888";
|
||||
}
|
||||
-197
@@ -1,197 +0,0 @@
|
||||
package cn.alphahub.eport.signature.config;
|
||||
|
||||
import cn.alphahub.eport.signature.base.exception.EportWebClientException;
|
||||
import cn.alphahub.eport.signature.core.web.EportCebMessageHttpClient;
|
||||
import cn.alphahub.eport.signature.core.web.EportCustoms179HttpClient;
|
||||
import cn.alphahub.eport.signature.core.web.EportReportResultHttpClient;
|
||||
import cn.alphahub.eport.signature.core.web.WebfluxDemoHttpClient;
|
||||
import cn.alphahub.eport.signature.support.OriginalPropertyNamingStrategy;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.tomcat.util.codec.binary.Base64;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatusCode;
|
||||
import org.springframework.http.codec.json.Jackson2JsonEncoder;
|
||||
import org.springframework.web.reactive.function.client.ClientRequest;
|
||||
import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
|
||||
import org.springframework.web.reactive.function.client.ExchangeStrategies;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
import org.springframework.web.reactive.function.client.support.WebClientAdapter;
|
||||
import org.springframework.web.service.invoker.HttpServiceProxyFactory;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static cn.alphahub.eport.signature.core.ChinaEportReportClient.EPORT_CEBMESSAGE_SERVER_ENCODE;
|
||||
import static cn.alphahub.eport.signature.core.ChinaEportReportClient.REPORT_PROD_ENV_179_URL_ENCODE;
|
||||
|
||||
/**
|
||||
* The WebClient Configuration
|
||||
*
|
||||
* @author weasley
|
||||
* @version 1.0.0
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@EnableConfigurationProperties({Customs179Properties.class, ChinaEportProperties.class})
|
||||
public class WebClientConfiguration {
|
||||
public static final ThreadLocal<Consumer<HttpHeaders>> HTTP_HEADERS = new ThreadLocal<>();
|
||||
|
||||
/**
|
||||
* @return A proxy bean of EportCebMessageHttpClient
|
||||
*/
|
||||
@Bean
|
||||
public EportCebMessageHttpClient eportCebMessageHttpClient(ObjectMapper objectMapper, ChinaEportProperties chinaEportProperties) {
|
||||
WebClient webClient = WebClient.builder()
|
||||
.exchangeStrategies(customExchangeStrategies(objectMapper))
|
||||
.filter(((request, next) -> next.exchange(Objects.requireNonNull(injectHeader(request).block()))))
|
||||
.filters(exchangeFilters -> {
|
||||
exchangeFilters.add(logRequest());
|
||||
exchangeFilters.add(logResponse());
|
||||
})
|
||||
.defaultStatusHandler(HttpStatusCode::isError, resp -> Mono.just(new EportWebClientException("Web Client 调用发生异常!")))
|
||||
.baseUrl(StringUtils.defaultIfBlank(chinaEportProperties.getServer(), new String(Base64.decodeBase64(EPORT_CEBMESSAGE_SERVER_ENCODE))))
|
||||
.build();
|
||||
HttpServiceProxyFactory httpServiceProxyFactory = HttpServiceProxyFactory.builder(WebClientAdapter.forClient(webClient))
|
||||
.build();
|
||||
return httpServiceProxyFactory.createClient(EportCebMessageHttpClient.class);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return A proxy bean of EportCustoms179HttpClient
|
||||
*/
|
||||
@Bean
|
||||
public EportCustoms179HttpClient eportCustoms179HttpClient(ObjectMapper objectMapper, Customs179Properties customs179Properties) {
|
||||
WebClient webClient = WebClient.builder()
|
||||
.exchangeStrategies(ExchangeStrategies.builder().codecs(configurer -> {
|
||||
configurer.defaultCodecs().jackson2JsonEncoder(new Jackson2JsonEncoder(enhanceSourceMapper(objectMapper)));
|
||||
}).build())
|
||||
.filters(exchangeFilters -> {
|
||||
exchangeFilters.add(((request, next) -> next.exchange(Objects.requireNonNull(injectHeader(request).block()))));
|
||||
exchangeFilters.add(logRequest());
|
||||
exchangeFilters.add(logResponse());
|
||||
})
|
||||
.defaultStatusHandler(HttpStatusCode::isError, resp -> Mono.just(new EportWebClientException("Web Client 调用发生异常!")))
|
||||
.baseUrl(StringUtils.defaultIfBlank(customs179Properties.getServer(), new String(Base64.decodeBase64(REPORT_PROD_ENV_179_URL_ENCODE))))
|
||||
.build();
|
||||
HttpServiceProxyFactory httpServiceProxyFactory = HttpServiceProxyFactory.builder(WebClientAdapter.forClient(webClient)).build();
|
||||
return httpServiceProxyFactory.createClient(EportCustoms179HttpClient.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A proxy bean of EportReportResultHttpClient
|
||||
*/
|
||||
@Bean
|
||||
public EportReportResultHttpClient eportReportResultHttpClient(ChinaEportProperties chinaEportProperties) {
|
||||
String baseUrl = StringUtils.defaultIfBlank(chinaEportProperties.getServer(), new String(Base64.decodeBase64(EPORT_CEBMESSAGE_SERVER_ENCODE)));
|
||||
WebClient webClient = WebClient.builder()
|
||||
.filter(logRequest())
|
||||
.filter(logResponse())
|
||||
.defaultStatusHandler(HttpStatusCode::isError, resp -> Mono.just(new EportWebClientException("Web Client 调用发生异常!")))
|
||||
.baseUrl(baseUrl)
|
||||
.build();
|
||||
return HttpServiceProxyFactory.builder(WebClientAdapter.forClient(webClient)).build()
|
||||
.createClient(EportReportResultHttpClient.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A proxy bean of WebfluxDemoHttpClient
|
||||
*/
|
||||
@Bean
|
||||
public WebfluxDemoHttpClient webfluxDemoHttpClient(ObjectMapper objectMapper) {
|
||||
WebClient webClient = WebClient.builder()
|
||||
.exchangeStrategies(customExchangeStrategies(objectMapper))
|
||||
.filters(exchangeFilters -> {
|
||||
exchangeFilters.add(((request, next) -> next.exchange(Objects.requireNonNull(injectHeader(request).block()))));
|
||||
exchangeFilters.add(logRequest());
|
||||
exchangeFilters.add(logResponse());
|
||||
})
|
||||
.defaultStatusHandler(HttpStatusCode::isError, resp -> Mono.just(new EportWebClientException("Web Client 调用发生异常!")))
|
||||
.baseUrl("http://127.0.0.1:8080")
|
||||
.build();
|
||||
WebClientAdapter clientAdapter = WebClientAdapter.forClient(webClient);
|
||||
HttpServiceProxyFactory httpServiceProxyFactory = HttpServiceProxyFactory.builder(clientAdapter).build();
|
||||
return httpServiceProxyFactory.createClient(WebfluxDemoHttpClient.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* To inject headers
|
||||
*/
|
||||
public Mono<ClientRequest> injectHeader(final ClientRequest clientRequest) {
|
||||
if (null != HTTP_HEADERS.get()) {
|
||||
Mono<ClientRequest> clientRequestMono = Mono.just(ClientRequest.from(clientRequest)
|
||||
.headers(HTTP_HEADERS.get())
|
||||
.build());
|
||||
clearHttpHeaders();
|
||||
return clientRequestMono;
|
||||
}
|
||||
return Mono.just(ClientRequest.from(clientRequest)
|
||||
.build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear Http Headers
|
||||
*/
|
||||
public void clearHttpHeaders() {
|
||||
if (null != HTTP_HEADERS.get()) {
|
||||
HTTP_HEADERS.remove();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom exchange strategies
|
||||
*/
|
||||
public ExchangeStrategies customExchangeStrategies(ObjectMapper objectMapper) {
|
||||
return ExchangeStrategies.builder()
|
||||
.codecs(configurer -> {
|
||||
configurer.defaultCodecs().jackson2JsonEncoder(new Jackson2JsonEncoder(enhanceSourceMapper(objectMapper)));
|
||||
}).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* To enhance mapper
|
||||
*/
|
||||
private ObjectMapper enhanceSourceMapper(ObjectMapper objectMapper) {
|
||||
ObjectMapper enhanceMapper = objectMapper.copy();
|
||||
enhanceMapper.setPropertyNamingStrategy(new OriginalPropertyNamingStrategy());
|
||||
return enhanceMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* log web request
|
||||
*/
|
||||
private ExchangeFilterFunction logRequest() {
|
||||
return ExchangeFilterFunction.ofRequestProcessor(clientRequest -> {
|
||||
if (log.isInfoEnabled()) {
|
||||
log.info("Request URL: " + clientRequest.url());
|
||||
log.info("Request Method: " + clientRequest.method());
|
||||
log.info("Request Headers: " + clientRequest.headers());
|
||||
if (clientRequest.method() == HttpMethod.POST || clientRequest.method() == HttpMethod.PUT) {
|
||||
log.info("Request Body: " + clientRequest.body());
|
||||
}
|
||||
}
|
||||
return Mono.just(clientRequest);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* log web response
|
||||
*/
|
||||
private ExchangeFilterFunction logResponse() {
|
||||
return ExchangeFilterFunction.ofResponseProcessor(clientResponse -> {
|
||||
if (log.isInfoEnabled()) {
|
||||
log.info("Response status: {}", clientResponse.statusCode());
|
||||
log.info("Response headers: {}", clientResponse.headers().asHttpHeaders());
|
||||
}
|
||||
return Mono.just(clientResponse);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
-80
@@ -1,80 +0,0 @@
|
||||
package cn.alphahub.eport.signature.controller.rpc;
|
||||
|
||||
import cn.alphahub.eport.signature.base.domain.Result;
|
||||
import cn.alphahub.eport.signature.config.ChinaEportProperties;
|
||||
import cn.alphahub.eport.signature.core.web.EportReportResultHttpClient;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
/**
|
||||
* 查询申报回执结果
|
||||
*
|
||||
* @author weasley
|
||||
* @version 1.1.0
|
||||
* @apiNote 目前仅实现:CEB311Message、CEB621Message 申报结果查询
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/rpc/eport/result")
|
||||
public class EportReportResultController {
|
||||
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
|
||||
@Autowired
|
||||
private ChinaEportProperties chinaEportProperties;
|
||||
@Autowired
|
||||
private EportReportResultHttpClient eportReportResultHttpClient;
|
||||
|
||||
/**
|
||||
* 查询311进口单申报结果
|
||||
*
|
||||
* @param qryId 查询时间,日期时间格式:yyyyMMddHHmmss
|
||||
* @response {
|
||||
* "message": "操作成功",
|
||||
* "success": true,
|
||||
* "timestamp": "2023-08-09 15:18:25",
|
||||
* "code": 200,
|
||||
* "data": "[{org_id=1, instm=1691397836000, re_order_guid=NWIVQA-WEASLEY-20230807164347-GCVZ4S, returnstatus=2, ebpcode=46016602EV, returntime2=20230807164356, ebccode=46016602EV, trans_dxpid=DXPENT0000530815, returninfo=新增申报成功[NWIVQA-WEASLEY-20230807164347-GCVZ4S], returntime=1691397830000, orderno=JO_D8079556739228400022}]"
|
||||
* }
|
||||
* @apiNote 查询此时间点之后的数据,必须在当前时间点之前,默认查询当前时间前15秒的申报数据,当前时间:20230806144417,则 qryId=20230806144401,不传的情况适用于刚刚申报成功查询回执结果,海关的处理时间间隔约为15秒
|
||||
* @since 1.1.1
|
||||
*/
|
||||
@GetMapping("/ceb312msg")
|
||||
public Result<String> getCeb312msgResult(@RequestParam(required = false) @DateTimeFormat(pattern = "yyyyMMddHHmmss") LocalDateTime qryId) {
|
||||
qryId = ObjectUtils.defaultIfNull(qryId, LocalDateTime.now().minusSeconds(15));
|
||||
log.info("开始查询311申报回执结果, qryId {}", FORMATTER.format(qryId));
|
||||
Mono<String> msgResult = eportReportResultHttpClient.getCeb312msgResult(chinaEportProperties.getDxpId(), FORMATTER.format(qryId));
|
||||
return Result.ok(msgResult.block());
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询621进口单申报结果
|
||||
*
|
||||
* @param qryId 查询时间,日期时间格式:yyyyMMddHHmmss
|
||||
* @response {
|
||||
* "message": "操作成功",
|
||||
* "success": true,
|
||||
* "timestamp": "2023-08-09 15:26:53",
|
||||
* "code": 200,
|
||||
* "data": "[{org_id=1, instm=1691397837000, agentcode=46016602EV, re_elist_guid_cus=9422f96b-ccbc-4a69-a899-6d9dd583d501, returnstatus=2, ebpcode=46016602EV, preno=B20230807570793612, returntime2=20230807164357, ebccode=46016602EV, createtime=20230807164351, trans_dxpid=DXPENT0000530815, returninfo=清单新增申报成功[3CQHDG-WEASLEY-20230807164348-7KYRQN][电商企业编码:46016602EV订单编号:JO_D8079556739228400022], returntime=1691397830000, re_elist_guid=3CQHDG-WEASLEY-20230807164348-7KYRQN, customscode=6409}, {org_id=1, instm=1691397847000, agentcode=46016602EV, re_elist_guid_cus=9422f96b-ccbc-4a69-a899-6d9dd583d501, returnstatus=120, ebpcode=46016602EV, preno=B20230807570793612, returntime2=20230807164407, ebccode=46016602EV, createtime=20230807164355, trans_dxpid=DXPENT0000530815, returninfo=[Code:1800;Desc:逻辑校验通过], returntime=1691397831000, re_elist_guid=4e7a2dab-064b-410b-9ce6-e322c590ae3d, customscode=6409, invtno=64092023I000722460}, {org_id=1, instm=1691397847000, agentcode=46016602EV, re_elist_guid_cus=9422f96b-ccbc-4a69-a899-6d9dd583d501, returnstatus=800, ebpcode=46016602EV, preno=B20230807570793612, returntime2=20230807164407, ebccode=46016602EV, createtime=20230807164355, trans_dxpid=DXPENT0000530815, returninfo=[Code:2600;Desc:放行], returntime=1691397832000, re_elist_guid=4e7a2dab-064b-410b-9ce6-e322c590ae3d, customscode=6409, invtno=64092023I000722460}]"
|
||||
* }
|
||||
* @apiNote 查询此时间点之后的数据,必须在当前时间点之前,默认查询当前时间前15秒的申报数据,当前时间:20230806144417,则 qryId=20230806144401,不传的情况适用于刚刚申报成功查询回执结果,海关的处理时间间隔约为15秒
|
||||
* @since 1.1.1
|
||||
*/
|
||||
@GetMapping("/ceb622msg")
|
||||
public Result<String> getCe622msgResult(@RequestParam(required = false) @DateTimeFormat(pattern = "yyyyMMddHHmmss") LocalDateTime qryId) {
|
||||
qryId = ObjectUtils.defaultIfNull(qryId, LocalDateTime.now().minusSeconds(15));
|
||||
log.info("开始查询621进口单申报回执结果, qryId {}", FORMATTER.format(qryId));
|
||||
Mono<String> msgResult = eportReportResultHttpClient.getCe622msgResult(chinaEportProperties.getDxpId(), FORMATTER.format(qryId));
|
||||
return Result.ok(msgResult.block());
|
||||
}
|
||||
|
||||
}
|
||||
-130
@@ -1,130 +0,0 @@
|
||||
package cn.alphahub.eport.signature.core;
|
||||
|
||||
import cn.alphahub.eport.signature.config.SignatureAlgorithm;
|
||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.Accessors;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Map;
|
||||
|
||||
import static cn.alphahub.eport.signature.config.SignatureAlgorithm.RSA_SHA1;
|
||||
|
||||
/**
|
||||
* X509Certificate证书
|
||||
*
|
||||
* @author weasley
|
||||
* @version 1.0
|
||||
* @date 2022/2/13
|
||||
*/
|
||||
@Data
|
||||
@Slf4j
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Accessors(chain = true)
|
||||
public class CertificateHandler implements Serializable {
|
||||
/**
|
||||
* 从u-key读取含发送推送 X509Certificate 证书对应的加签方法
|
||||
*/
|
||||
public static final String SING_DATA_METHOD = "cus-sec_SpcSignDataAsPEM";
|
||||
/**
|
||||
* 暂定2022-07-01为第一个时间分界
|
||||
*/
|
||||
public static final LocalDateTime DATE_TIME_202207 = LocalDateTimeUtil.parse("2022-07-01", "yyyy-MM-dd");
|
||||
|
||||
/**
|
||||
* u-key的.cer证书是否存在
|
||||
*/
|
||||
private Boolean certExists = false;
|
||||
/**
|
||||
* XML 签名算法, RSA_SHA1 兜底
|
||||
*/
|
||||
private SignatureAlgorithm algorithm = RSA_SHA1;
|
||||
/**
|
||||
* 判断用那一个证书的集合
|
||||
* <ul>
|
||||
* <li>key -> 从u-key读取<ds:SignatureValue>的方法名</li>
|
||||
* <li>value -> 从u-key读取<ds:SignatureValue>的方法名对应的X509Certificate证书</li>
|
||||
* </ul>
|
||||
*/
|
||||
@Getter
|
||||
private Map<String, String> x509Map;
|
||||
/**
|
||||
* 海关u-key证书有效期开始
|
||||
*/
|
||||
private LocalDateTime ukeyValidTimeBegin;
|
||||
/**
|
||||
* 海关u-key证书有效期结束
|
||||
*/
|
||||
private LocalDateTime ukeyValidTimeEnd;
|
||||
|
||||
/**
|
||||
* 组装X509Certificate证书
|
||||
* <p>
|
||||
* 组装64位长度一行的pem分割的pem
|
||||
*
|
||||
* @param certPomFromUkey 从ukey里面读取的pem
|
||||
* @return 64位长度一行的pem分割的pem
|
||||
* @apiNote 不带Header
|
||||
*/
|
||||
public static String buildX509CertificateWithoutHeader(String certPomFromUkey) {
|
||||
certPomFromUkey = certPomFromUkey.trim();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int count = 0;
|
||||
for (char c : certPomFromUkey.toCharArray()) {
|
||||
sb.append(c);
|
||||
count++;
|
||||
if (count == 64) {
|
||||
sb.append("\n");
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
return sb.toString().trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* 组装X509Certificate证书
|
||||
* <p>
|
||||
* 组装64位长度一行的pem分割的pem
|
||||
*
|
||||
* @param certPomFromUkey 从ukey里面读取的pem
|
||||
* @return 64位长度一行的pem分割的pem
|
||||
* @apiNote 带Header
|
||||
*/
|
||||
public static String buildX509CertificateWithHeader(String certPomFromUkey) {
|
||||
return "-----BEGIN CERTIFICATE-----\n"
|
||||
.concat(buildX509CertificateWithoutHeader(certPomFromUkey)).concat("\n")
|
||||
.concat("-----END CERTIFICATE-----");
|
||||
}
|
||||
|
||||
public LocalDateTime getUkeyValidTimeBegin() {
|
||||
if (ukeyValidTimeBegin == null) {
|
||||
return LocalDateTime.now();
|
||||
}
|
||||
return ukeyValidTimeBegin;
|
||||
}
|
||||
|
||||
public void setX509Map(Map<String, String> x509Map) {
|
||||
this.x509Map = x509Map;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取509Certificate证书
|
||||
*
|
||||
* @param method 发送给电子口岸u-key的入参的[_method]字段的值
|
||||
* @return 509Certificate证书
|
||||
*/
|
||||
public String getX509Certificate(String method) {
|
||||
return switch (method) {
|
||||
case SING_DATA_METHOD -> this.getX509Map().get(SING_DATA_METHOD);
|
||||
default -> throw new IllegalStateException("Unexpected value: " + method);
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
-476
@@ -1,476 +0,0 @@
|
||||
package cn.alphahub.eport.signature.core;
|
||||
|
||||
import cn.alphahub.eport.signature.base.exception.SignException;
|
||||
import cn.alphahub.eport.signature.config.ChinaEportProperties;
|
||||
import cn.alphahub.eport.signature.config.Customs179Properties;
|
||||
import cn.alphahub.eport.signature.config.UkeyProperties;
|
||||
import cn.alphahub.eport.signature.entity.Capture179DataRequest;
|
||||
import cn.alphahub.eport.signature.entity.Capture179DataResponse;
|
||||
import cn.alphahub.eport.signature.entity.SignRequest;
|
||||
import cn.alphahub.eport.signature.entity.SignResult;
|
||||
import cn.alphahub.eport.signature.entity.ThirdAbstractResponse;
|
||||
import cn.alphahub.eport.signature.entity.UkeyRequest;
|
||||
import cn.alphahub.eport.signature.entity.UkeyResponse.Args;
|
||||
import cn.alphahub.eport.signature.entity.UploadCEBMessageRequest;
|
||||
import cn.hutool.core.codec.Base64;
|
||||
import cn.hutool.core.lang.TypeReference;
|
||||
import cn.hutool.http.ContentType;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import io.github.weasleyj.china.eport.sign.AbstractCebMessage;
|
||||
import io.github.weasleyj.china.eport.sign.IMessageType;
|
||||
import io.github.weasleyj.china.eport.sign.model.cebmessage.BaseTransfer;
|
||||
import io.github.weasleyj.china.eport.sign.model.cebmessage.CEB311Message;
|
||||
import io.github.weasleyj.china.eport.sign.model.cebmessage.CEB621Message;
|
||||
import io.github.weasleyj.china.eport.sign.model.cebmessage.export.CEB303Message;
|
||||
import io.github.weasleyj.china.eport.sign.model.customs179.Customs179Request;
|
||||
import io.github.weasleyj.china.eport.sign.model.request.Message;
|
||||
import io.github.weasleyj.china.eport.sign.model.request.MessageBody;
|
||||
import io.github.weasleyj.china.eport.sign.model.request.MessageHead;
|
||||
import io.github.weasleyj.china.eport.sign.model.request.MessageRequest;
|
||||
import io.github.weasleyj.china.eport.sign.model.signature.CanonicalizationMethod;
|
||||
import io.github.weasleyj.china.eport.sign.model.signature.DigestMethod;
|
||||
import io.github.weasleyj.china.eport.sign.model.signature.KeyInfo;
|
||||
import io.github.weasleyj.china.eport.sign.model.signature.Reference;
|
||||
import io.github.weasleyj.china.eport.sign.model.signature.Signature;
|
||||
import io.github.weasleyj.china.eport.sign.model.signature.SignatureMethod;
|
||||
import io.github.weasleyj.china.eport.sign.model.signature.SignedInfo;
|
||||
import io.github.weasleyj.china.eport.sign.model.signature.Transforms;
|
||||
import io.github.weasleyj.china.eport.sign.model.signature.X509Data;
|
||||
import io.github.weasleyj.china.eport.sign.util.GUIDUtil;
|
||||
import io.github.weasleyj.china.eport.sign.util.JAXBUtil;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import static cn.alphahub.dtt.plus.util.JacksonUtil.readValue;
|
||||
import static cn.alphahub.dtt.plus.util.JacksonUtil.toJson;
|
||||
import static cn.alphahub.eport.signature.core.CertificateHandler.SING_DATA_METHOD;
|
||||
|
||||
/**
|
||||
* 电子口岸上报客户端
|
||||
*
|
||||
* @author weasley
|
||||
* @since 1.1.0
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@AllArgsConstructor
|
||||
public class ChinaEportReportClient {
|
||||
/**
|
||||
* 海关服务器地址,格式: http://ip:port
|
||||
*
|
||||
* @apiNote base64加密下,不适合直接公布到公网
|
||||
*/
|
||||
public static final String EPORT_CEBMESSAGE_SERVER_ENCODE = "aHR0cDovLzM2LjEwMS4yMDguMjMwOjgwOTA=";
|
||||
/**
|
||||
* 海关 179 数据抓取测试环境 URL (base64加密,不适合直接公布到外网)
|
||||
*/
|
||||
public static final String REPORT_TEST_ENV_179_URL_ENCODE = "aHR0cHM6Ly9zd2FwcHRlc3Quc2luZ2xld2luZG93LmNuL2NlYjJncmFiL2dyYWIvcmVhbFRpbWVEYXRhVXBsb2Fk";
|
||||
/**
|
||||
* 海关 179 数据抓取生产环境 URL (base64加密,不适合直接公布到外网)
|
||||
*/
|
||||
public static final String REPORT_PROD_ENV_179_URL_ENCODE = "aHR0cHM6Ly9jdXN0b21zLmNoaW5hcG9ydC5nb3YuY24vY2ViMmdyYWIvZ3JhYi9yZWFsVGltZURhdGFVcGxvYWQ=";
|
||||
private final SignHandler signHandler;
|
||||
private final UkeyProperties ukeyProperties;
|
||||
private final Customs179Properties customs179Properties;
|
||||
private final ChinaEportProperties chinaEportProperties;
|
||||
|
||||
/**
|
||||
* 构建 ds:Signature 节点
|
||||
*
|
||||
* @param signResult 加签结果
|
||||
* @return ds:Signature 节点
|
||||
*/
|
||||
public static String buildSignatureNode(SignResult signResult) {
|
||||
KeyInfo keyInfo = KeyInfo.builder()
|
||||
.keyName(signResult.getCertNo())
|
||||
.x509Data(new X509Data(signResult.getX509Certificate()))
|
||||
.build();
|
||||
|
||||
Reference reference = Reference.builder()
|
||||
.digestMethod(new DigestMethod())
|
||||
.uri("")
|
||||
.transforms(new Transforms())
|
||||
.digestValue(signResult.getDigestValue())
|
||||
.build();
|
||||
|
||||
SignedInfo signedInfo = SignedInfo.builder()
|
||||
.CanonicalizationMethod(new CanonicalizationMethod())
|
||||
.SignatureMethod(new SignatureMethod().setAlgorithm(SignatureHandler.getSignatureMethodAlgorithm()))
|
||||
.reference(reference)
|
||||
.build();
|
||||
|
||||
Signature signature = Signature.builder()
|
||||
.keyInfo(keyInfo)
|
||||
.signatureValue(signResult.getSignatureValue())
|
||||
.signedInfo(signedInfo)
|
||||
.build();
|
||||
|
||||
return JAXBUtil.toXml(signature);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建 ds:SignedInfo 节点
|
||||
*
|
||||
* @param signResult 加签结果
|
||||
* @return ds:SignedInfo 节点
|
||||
*/
|
||||
public static String buildSignedInfoNode(SignResult signResult) {
|
||||
Reference reference = Reference.builder()
|
||||
.digestMethod(new DigestMethod())
|
||||
.uri("")
|
||||
.transforms(new Transforms())
|
||||
.digestValue(signResult.getDigestValue())
|
||||
.build();
|
||||
SignedInfo signedInfo = SignedInfo.builder()
|
||||
.CanonicalizationMethod(new CanonicalizationMethod())
|
||||
.SignatureMethod(new SignatureMethod().setAlgorithm(SignatureHandler.getSignatureMethodAlgorithm()))
|
||||
.reference(reference)
|
||||
.build();
|
||||
return JAXBUtil.toXml(signedInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* CebXxxMessage数据上报海关
|
||||
*
|
||||
* @param cebMessage 原始xml对象
|
||||
* @param messageType CEB311Message|CEB621Message|...
|
||||
* @return 推送结果, OK: 表示成功, 报关结果需要自己查询回执
|
||||
*/
|
||||
public ThirdAbstractResponse<MessageRequest, String, String> report(AbstractCebMessage cebMessage, IMessageType messageType) {
|
||||
log.info("数据上报海关xml入参: {}, {}", JSONUtil.toJsonStr(cebMessage), messageType.getType());
|
||||
MessageRequest messageRequest = buildMessageRequest(cebMessage, messageType);
|
||||
String requestServer = Base64.decodeStr(EPORT_CEBMESSAGE_SERVER_ENCODE);
|
||||
requestServer = StringUtils.defaultIfBlank(chinaEportProperties.getServer(), requestServer);
|
||||
String requestBody = JSONUtil.toJsonStr(messageRequest);
|
||||
log.info("数据上报海关请求入参 {}\nRequest Server: {}", requestBody, requestServer);
|
||||
HttpResponse httpResponse = HttpUtil.createPost(requestServer + "/cebcmsg")
|
||||
.contentType(ContentType.JSON.getValue())
|
||||
.body(requestBody)
|
||||
.execute();
|
||||
String responseBody = httpResponse.body();
|
||||
log.info("数据上报Http响应结果: {}", responseBody);
|
||||
if (!"OK".equals(responseBody)) {
|
||||
log.error("上报海关请求异常, 请求入参: {}\n海关Http响应: {}", requestBody, responseBody);
|
||||
}
|
||||
ThirdAbstractResponse<MessageRequest, String, String> thirdResponse = ThirdAbstractResponse.getInstance();
|
||||
thirdResponse.setPayload(messageRequest);
|
||||
thirdResponse.setOriginal(responseBody);
|
||||
thirdResponse.setExpected("OK");
|
||||
return thirdResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* 海关 179 数据抓取
|
||||
* <p>
|
||||
* <ul>
|
||||
* <li>
|
||||
* 期望的成功数据返回示例
|
||||
* <pre>
|
||||
* {"code":"10000","message":"","serviceTime":1567050097628}
|
||||
* </pre>
|
||||
* </li>
|
||||
* </ul>
|
||||
*/
|
||||
public ThirdAbstractResponse<Map<String, Object>, String, Capture179DataResponse> capture179Data(Capture179DataRequest request) {
|
||||
log.info("海关 179 数据抓取 {}", JSONUtil.toJsonStr(request));
|
||||
Customs179Request customs179Request = new Customs179Request();
|
||||
customs179Request.setSessionID(request.getSessionID());
|
||||
customs179Request.setPayExchangeInfoHead(request.getPayExchangeInfoHead());
|
||||
customs179Request.setPayExchangeInfoLists(request.getPayExchangeInfoLists());
|
||||
customs179Request.setServiceTime(request.getServiceTime());
|
||||
|
||||
String guid = GUIDUtil.getGuid();
|
||||
customs179Request.setSessionID(guid);
|
||||
customs179Request.getPayExchangeInfoHead().setGuid(guid);
|
||||
customs179Request.getPayExchangeInfoHead().setEbpCode(customs179Properties.getEbpCode());
|
||||
|
||||
String testEnvServerUrl = Base64.decodeStr(REPORT_TEST_ENV_179_URL_ENCODE);
|
||||
String prodEnvServerUrl = Base64.decodeStr(REPORT_PROD_ENV_179_URL_ENCODE);
|
||||
String requestUrl = StringUtils.defaultIfBlank(customs179Properties.getServer(), prodEnvServerUrl);
|
||||
|
||||
String capture179DataUkeyRequest = "\"sessionID\":\"" + customs179Request.getSessionID() + "\"||" +
|
||||
"\"payExchangeInfoHead\":\"" + toJson(customs179Request.getPayExchangeInfoHead()) + "\"||" +
|
||||
"\"payExchangeInfoLists\":\"" + toJson(customs179Request.getPayExchangeInfoLists()) + "\"||" +
|
||||
"\"serviceTime\":" + "\"" + customs179Request.getServiceTime() + "\"";
|
||||
|
||||
@SuppressWarnings("all") UkeyRequest ukeyRequest = new UkeyRequest(SING_DATA_METHOD, new HashMap<>() {{
|
||||
put("inData", capture179DataUkeyRequest);
|
||||
put("passwd", ukeyProperties.getPassword());
|
||||
}});
|
||||
String signParams = toJson(ukeyRequest);
|
||||
SignRequest signRequest = new SignRequest(capture179DataUkeyRequest);
|
||||
SignResult signResult = signHandler.sign(signRequest, signParams);
|
||||
|
||||
if (Boolean.FALSE.equals(signResult.getSuccess())) {
|
||||
throw new SignException("海关 179 数据抓取加签失败, ukey加签入参: " + signParams + ", 原始入参: " + toJson(customs179Request));
|
||||
}
|
||||
customs179Request.setCertNo(signResult.getCertNo());
|
||||
customs179Request.setSignValue(signResult.getSignatureValue());
|
||||
|
||||
Map<String, Object> formData = new LinkedHashMap<>();
|
||||
formData.put("payExInfoStr", toJson(customs179Request));
|
||||
log.info("海关179数据抓取URL: {}\n开始上报,请求数据:{}", requestUrl, toJson(formData));
|
||||
|
||||
HttpResponse httpResponse = HttpUtil.createPost(requestUrl)
|
||||
.form(formData)
|
||||
.header("Content-Type", "application/x-www-form-urlencoded", true)
|
||||
.timeout(5 * 1000)
|
||||
.execute();
|
||||
log.info("海关 179 数据抓取返回结果 {}", httpResponse.body());
|
||||
Capture179DataResponse expected = readValue("""
|
||||
{"code":"10000","message":"上传成功","total":0,"serviceTime":null}
|
||||
""", Capture179DataResponse.class);
|
||||
expected.setServiceTime(Long.parseLong(customs179Request.getServiceTime()));
|
||||
ThirdAbstractResponse<Map<String, Object>, String, Capture179DataResponse> thirdResponse = ThirdAbstractResponse.getInstance();
|
||||
thirdResponse.setPayload(formData);
|
||||
thirdResponse.setOriginal(httpResponse.body());
|
||||
thirdResponse.setExpected(expected);
|
||||
return thirdResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* 推断CebMessage的具体的XML入参解析成Java对象
|
||||
*
|
||||
* @implNote 已实现: CEB311Message,CEB621Message,CEB303Message
|
||||
*/
|
||||
@SuppressWarnings("all")
|
||||
public AbstractCebMessage buildCebMessage(UploadCEBMessageRequest request) {
|
||||
String guid = GUIDUtil.getGuid();
|
||||
String cebMessage = String.valueOf(request.getCebMessage());
|
||||
switch (request.getDataType()) {
|
||||
case XML -> {
|
||||
return switch (request.getMessageType()) {
|
||||
// 进口单XML报文解析
|
||||
case CEB311Message -> {
|
||||
CEB311Message ceb311Message = Objects.requireNonNull(JAXBUtil.toBean(cebMessage, CEB311Message.class));
|
||||
ceb311Message.setGuid(guid);
|
||||
ceb311Message.getOrder().getOrderHead().setGuid(guid);
|
||||
buildBaseTransfer(ceb311Message.getBaseTransfer());
|
||||
yield ceb311Message;
|
||||
}
|
||||
case CEB312Message -> null;
|
||||
case CEB411Message -> null;
|
||||
case CEB412Message -> null;
|
||||
case CEB511Message -> null;
|
||||
case CEB512Message -> null;
|
||||
case CEB513Message -> null;
|
||||
case CEB514Message -> null;
|
||||
case CEB621Message -> {
|
||||
CEB621Message ceb621Message = Objects.requireNonNull(JAXBUtil.toBean(cebMessage, CEB621Message.class));
|
||||
ceb621Message.setGuid(guid);
|
||||
ceb621Message.getInventory().getInventoryHead().setGuid(guid);
|
||||
buildBaseTransfer(ceb621Message.getBaseTransfer());
|
||||
yield ceb621Message;
|
||||
}
|
||||
case CEB622Message -> null;
|
||||
case CEB623Message -> null;
|
||||
case CEB624Message -> null;
|
||||
case CEB625Message -> null;
|
||||
case CEB626Message -> null;
|
||||
case CEB711Message -> null;
|
||||
case CEB712Message -> null;
|
||||
case CEB213Message -> null;
|
||||
case CEB214Message -> null;
|
||||
case CEB215Message -> null;
|
||||
case CEB216Message -> null;
|
||||
case CEB303Message -> {
|
||||
CEB303Message ceb303Message = Objects.requireNonNull(JAXBUtil.toBean(cebMessage, CEB303Message.class));
|
||||
ceb303Message.setGuid(guid);
|
||||
ceb303Message.getOrder().getOrderHead().setGuid(guid);
|
||||
buildBaseTransfer(ceb303Message.getBaseTransfer());
|
||||
yield ceb303Message;
|
||||
}
|
||||
// 出口单XML报文
|
||||
case CEB304Message -> null;
|
||||
case CEB403Message -> null;
|
||||
case CEB404Message -> null;
|
||||
case CEB505Message -> null;
|
||||
case CEB506Message -> null;
|
||||
case CEB507Message -> null;
|
||||
case CEB508Message -> null;
|
||||
case CEB509Message -> null;
|
||||
case CEB510Message -> null;
|
||||
case CEB603Message -> null;
|
||||
case CEB604Message -> null;
|
||||
case CEB605Message -> null;
|
||||
case CEB606Message -> null;
|
||||
case CEB607Message -> null;
|
||||
case CEB608Message -> null;
|
||||
case CEB701Message -> null;
|
||||
case CEB702Message -> null;
|
||||
case CEB792Message -> null;
|
||||
};
|
||||
}
|
||||
case JSON -> {
|
||||
return switch (request.getMessageType()) {
|
||||
case CEB311Message -> {
|
||||
CEB311Message ceb311Message = JSONUtil.toBean(cebMessage, new TypeReference<>() {
|
||||
}, false);
|
||||
ceb311Message.setGuid(guid);
|
||||
ceb311Message.getOrder().getOrderHead().setGuid(guid);
|
||||
buildBaseTransfer(ceb311Message.getBaseTransfer());
|
||||
yield ceb311Message;
|
||||
}
|
||||
// 进口单JSON报文解析
|
||||
case CEB312Message -> null;
|
||||
case CEB411Message -> null;
|
||||
case CEB412Message -> null;
|
||||
case CEB511Message -> null;
|
||||
case CEB512Message -> null;
|
||||
case CEB513Message -> null;
|
||||
case CEB514Message -> null;
|
||||
case CEB621Message -> {
|
||||
CEB621Message ceb621Message = JSONUtil.toBean(cebMessage, new TypeReference<>() {
|
||||
}, false);
|
||||
ceb621Message.setGuid(guid);
|
||||
ceb621Message.getInventory().getInventoryHead().setGuid(guid);
|
||||
buildBaseTransfer(ceb621Message.getBaseTransfer());
|
||||
yield ceb621Message;
|
||||
}
|
||||
case CEB622Message -> null;
|
||||
case CEB623Message -> null;
|
||||
case CEB624Message -> null;
|
||||
case CEB625Message -> null;
|
||||
case CEB626Message -> null;
|
||||
case CEB711Message -> null;
|
||||
case CEB712Message -> null;
|
||||
case CEB213Message -> null;
|
||||
case CEB214Message -> null;
|
||||
case CEB215Message -> null;
|
||||
case CEB216Message -> null;
|
||||
// 出口单JSON报文解析
|
||||
case CEB303Message -> {
|
||||
CEB303Message ceb303Message = JSONUtil.toBean(cebMessage, new TypeReference<>() {
|
||||
}, false);
|
||||
ceb303Message.setGuid(guid);
|
||||
ceb303Message.getOrder().getOrderHead().setGuid(guid);
|
||||
buildBaseTransfer(ceb303Message.getBaseTransfer());
|
||||
yield ceb303Message;
|
||||
}
|
||||
case CEB304Message -> null;
|
||||
case CEB403Message -> null;
|
||||
case CEB404Message -> null;
|
||||
case CEB505Message -> null;
|
||||
case CEB506Message -> null;
|
||||
case CEB507Message -> null;
|
||||
case CEB508Message -> null;
|
||||
case CEB509Message -> null;
|
||||
case CEB510Message -> null;
|
||||
case CEB603Message -> null;
|
||||
case CEB604Message -> null;
|
||||
case CEB605Message -> null;
|
||||
case CEB606Message -> null;
|
||||
case CEB607Message -> null;
|
||||
case CEB608Message -> null;
|
||||
case CEB701Message -> null;
|
||||
case CEB702Message -> null;
|
||||
case CEB792Message -> null;
|
||||
};
|
||||
}
|
||||
default -> throw new IllegalStateException("Unexpected value: " + toJson(request));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build BaseTransfer XML Node
|
||||
*/
|
||||
@SuppressWarnings("all")
|
||||
public BaseTransfer buildBaseTransfer(BaseTransfer baseTransfer) {
|
||||
baseTransfer.setCopCode(StringUtils.defaultIfBlank(baseTransfer.getCopCode(), chinaEportProperties.getCopCode()));
|
||||
baseTransfer.setCopName(StringUtils.defaultIfBlank(baseTransfer.getCopName(), chinaEportProperties.getCopName()));
|
||||
baseTransfer.setDxpId(StringUtils.defaultIfBlank(baseTransfer.getDxpId(), chinaEportProperties.getDxpId()));
|
||||
baseTransfer.setDxpMode(StringUtils.defaultIfBlank(baseTransfer.getDxpMode(), "DXP"));
|
||||
return baseTransfer;
|
||||
}
|
||||
|
||||
/**
|
||||
* 组装最终请求数据,完成末三段加密
|
||||
*/
|
||||
public MessageRequest buildMessageRequest(AbstractCebMessage cebMessage, IMessageType messageType) {
|
||||
if (cebMessage == null) {
|
||||
return new MessageRequest();
|
||||
}
|
||||
String sourceXml = JAXBUtil.toXml(cebMessage);
|
||||
MessageHead messageHead = new MessageHead();
|
||||
messageHead.setMessageType(messageType.getType() + ".xml");
|
||||
messageHead.setSendTime(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
|
||||
sourceXml = sourceXml.replaceAll("xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\"", "");
|
||||
|
||||
SignRequest ukeyRequest = new SignRequest();
|
||||
ukeyRequest.setId(1);
|
||||
ukeyRequest.setData(sourceXml);
|
||||
|
||||
// 请求签名加密,2次组装数据, 请求加密(包含第1,2段加密)
|
||||
String payload = signHandler.getSignDataParameter(ukeyRequest);
|
||||
SignResult signResult = signHandler.sign(ukeyRequest, payload);
|
||||
// 线程uppark时间可能会失败,增强方法兜底
|
||||
if (Boolean.FALSE.equals(signResult.getSuccess())) {
|
||||
this.enhanceSignResultIfDefective(signResult, sourceXml);
|
||||
}
|
||||
if (Boolean.FALSE.equals(signResult.getSuccess())) {
|
||||
throw new SignException("CebXxxMessage XML数据加签失败, ukey加签入参: " + payload + ", 原始入参: " + toJson(ukeyRequest));
|
||||
}
|
||||
String xmlBody = buildXml(signResult, sourceXml, messageType);
|
||||
// 第3次组装数据, 将加签后的 XM 进行 base64 加密
|
||||
String encodedXml = Base64.encode(xmlBody);
|
||||
MessageBody messageBody = MessageBody.builder().data(encodedXml).build();
|
||||
Message message = Message.builder().MessageBody(messageBody).MessageHead(messageHead).build();
|
||||
return new MessageRequest(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 增强 SignResult, 如果有缺陷: 如: 验签失败等场景
|
||||
*
|
||||
* @param signResult 签名结果
|
||||
* @param sourceXml 原始XML
|
||||
* @return 签名结果
|
||||
* @apiNote 用于 SignHandler#sign 结果出现验签失败时的兜底手段
|
||||
*/
|
||||
@SuppressWarnings("all")
|
||||
private SignResult enhanceSignResultIfDefective(SignResult signResult, String sourceXml) {
|
||||
String signatureXml = SignHandler.getInitData(new SignRequest(sourceXml));
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("inData", signatureXml);
|
||||
params.put("passwd", ukeyProperties.getPassword());
|
||||
Args args1 = signHandler.getUkeyResponseArgs(new UkeyRequest("cus-sec_SpcSignDataAsPEM", params));
|
||||
Args args2 = signHandler.getUkeyResponseArgs(new UkeyRequest("cus-sec_SpcGetSignCertAsPEM", new HashMap<>()));
|
||||
signResult.setCertNo(args1.getData().get(1));
|
||||
signResult.setSignatureValue(args1.getData().get(0));
|
||||
signResult.setX509Certificate(args2.getData().get(0));
|
||||
if (signResult.getSuccess().equals(false)) {
|
||||
signResult.setSuccess(true);
|
||||
}
|
||||
return signResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* 组装Signature XML节点
|
||||
*
|
||||
* @param sourceXml 原始XML
|
||||
*/
|
||||
private String buildXml(SignResult signResult, String sourceXml, IMessageType messageType) {
|
||||
String signatureNode = buildSignatureNode(signResult);
|
||||
String xmlStart = "</ceb:" + messageType.getType() + ">";
|
||||
int index = sourceXml.indexOf(xmlStart);
|
||||
StringBuilder builder = new StringBuilder(sourceXml);
|
||||
builder.insert(index, signatureNode.concat("\n"));
|
||||
String finalXml = builder.toString();
|
||||
if (finalXml.startsWith("<?xml ")) {
|
||||
finalXml = finalXml.replace("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", "");
|
||||
}
|
||||
log.info("上报海关的XML报文:\n{}", finalXml);
|
||||
return finalXml;
|
||||
}
|
||||
|
||||
}
|
||||
-173
@@ -1,173 +0,0 @@
|
||||
package cn.alphahub.eport.signature.core;
|
||||
|
||||
import cn.alphahub.eport.signature.config.UkeyInitialConfig;
|
||||
import cn.alphahub.eport.signature.config.UkeyProperties;
|
||||
import cn.alphahub.eport.signature.entity.SignRequest;
|
||||
import cn.alphahub.eport.signature.entity.SignResult;
|
||||
import cn.alphahub.eport.signature.entity.UkeyRequest;
|
||||
import cn.alphahub.eport.signature.entity.UkeyResponse;
|
||||
import cn.alphahub.eport.signature.entity.UkeyResponse.Args;
|
||||
import cn.alphahub.eport.signature.entity.UkeyResponseArgsWrapper;
|
||||
import cn.alphahub.eport.signature.entity.WebSocketWrapper;
|
||||
import cn.alphahub.eport.signature.support.XMLValidator;
|
||||
import cn.hutool.core.lang.TypeReference;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.socket.TextMessage;
|
||||
import org.springframework.web.socket.WebSocketSession;
|
||||
import org.springframework.web.socket.client.WebSocketConnectionManager;
|
||||
import org.springframework.web.socket.client.standard.StandardWebSocketClient;
|
||||
import org.springframework.web.socket.handler.TextWebSocketHandler;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.concurrent.locks.LockSupport;
|
||||
|
||||
|
||||
/**
|
||||
* <p>电子口岸加签业务核心类</p>
|
||||
* <a href='http://tool.qdhuaxun.cn/?signdoc'>华讯云业务帮助文档,签名的不要看误导人,华讯的业务文档可以参考</a>
|
||||
*
|
||||
* @author weasley
|
||||
* @version 1.0
|
||||
* @date 2022/2/12
|
||||
*/
|
||||
@Data
|
||||
@Slf4j
|
||||
@Service
|
||||
@Validated
|
||||
@Accessors(chain = true)
|
||||
public class SignHandler {
|
||||
@Autowired
|
||||
private UkeyProperties ukeyProperties;
|
||||
@Autowired
|
||||
private WebSocketClientHandler webSocketClientHandler;
|
||||
@Autowired
|
||||
private StandardWebSocketClient standardWebSocketClient;
|
||||
|
||||
/**
|
||||
* 获取ukey的socket入参的inData字段
|
||||
*
|
||||
* @param request 加签数据请求入参
|
||||
* @return ukey的socket入参的inData字段
|
||||
*/
|
||||
public static String getInitData(@Valid SignRequest request) {
|
||||
if (isSignXml(request)) {
|
||||
return SignatureHandler.getSignatureNodeBeforeSend(request);
|
||||
}
|
||||
return request.getData();
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否总署xml
|
||||
*
|
||||
* @param request 加签数据请求入参
|
||||
* @return 总署xml返回true
|
||||
*/
|
||||
public static boolean isSignXml(@Valid SignRequest request) {
|
||||
if (StringUtils.isBlank(request.getData())) {
|
||||
throw new IllegalArgumentException("加签数据请求入参能为空!");
|
||||
}
|
||||
boolean isSignXmlString = XMLValidator.isValidXML(request.getData()) || StringUtils.startsWith(request.getData(), "<ceb:CEB");
|
||||
boolean isSign179String = StringUtils.startsWith(request.getData(), "\"sessionID\"");
|
||||
if (isSignXmlString && !isSign179String) {
|
||||
return true;
|
||||
}
|
||||
if (isSign179String && !isSignXmlString) {
|
||||
return false;
|
||||
}
|
||||
throw new IllegalArgumentException("加签数据请求入参不合法,请检查参数:" + request.getData());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取u-key签名参数
|
||||
*
|
||||
* @return 发送u-key的签名的入参
|
||||
* @since 2022-11-27
|
||||
*/
|
||||
public String getSignDataParameter(@Valid SignRequest request) {
|
||||
return UkeyInitialConfig.getSignDataAsPEM(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* WebSocket发送消息给电子口岸u-key
|
||||
*
|
||||
* @param request 加签请求参数
|
||||
* @param payload websocket发送的数据载荷
|
||||
*/
|
||||
public SignResult sign(@Valid SignRequest request, @NotBlank(message = "websocket发送的数据载荷不能为空") String payload) {
|
||||
log.info("收到u-key加签数据: {}", payload);
|
||||
|
||||
WebSocketWrapper wrapper = webSocketClientHandler.getWebSocketWrapper();
|
||||
wrapper.setPayload(payload);
|
||||
wrapper.setRequest(request);
|
||||
wrapper.setSignResult(new SignResult());
|
||||
wrapper.setThreadReference(new AtomicReference<>(Thread.currentThread()));
|
||||
if (SignHandler.isSignXml(request)) {
|
||||
wrapper.getSignResult().setDigestValue(SignatureHandler.getDigestValueOfCEBXxxMessage(request.getData()));
|
||||
wrapper.getSignResult().setSignatureNode(SignatureHandler.getSignatureNodeBeforeSend(request));
|
||||
}
|
||||
|
||||
WebSocketConnectionManager manager = new WebSocketConnectionManager(standardWebSocketClient, webSocketClientHandler, ukeyProperties.getWsUrl());
|
||||
manager.start();
|
||||
|
||||
try {
|
||||
LockSupport.parkNanos(wrapper.getThreadReference().get(), 1000 * 1000 * 1000 * 5L);
|
||||
} catch (Exception e) {
|
||||
log.error("线程自动unpark异常 {}", e.getLocalizedMessage(), e);
|
||||
wrapper.getSignResult().setSuccess(false);
|
||||
} finally {
|
||||
manager.stop();
|
||||
}
|
||||
|
||||
return wrapper.getSignResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取u-key加签返回内层对象(我们要的数据在这里面)
|
||||
*
|
||||
* @apiNote 发送任意数据给ukey,实时响应结果
|
||||
*/
|
||||
public Args getUkeyResponseArgs(UkeyRequest ukeyRequest) {
|
||||
AtomicReference<UkeyResponseArgsWrapper> reference = new AtomicReference<>();
|
||||
reference.set(new UkeyResponseArgsWrapper(Thread.currentThread(), new Args()));
|
||||
WebSocketConnectionManager webSocketConnectionManager = new WebSocketConnectionManager(standardWebSocketClient, new TextWebSocketHandler() {
|
||||
@Override
|
||||
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
|
||||
session.sendMessage(new TextMessage(JSONUtil.toJsonStr(ukeyRequest)));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleTextMessage(WebSocketSession session, TextMessage message) {
|
||||
UkeyResponse response = JSONUtil.toBean(message.getPayload(), new TypeReference<>() {
|
||||
}, true);
|
||||
try {
|
||||
if (Objects.equals(response.get_id(), ukeyRequest.get_id())) {
|
||||
log.warn("从电子口岸ukey中获取获取到数据: {}", response.get_args());
|
||||
reference.get().setResponseArgs(response.get_args());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("唤醒线程异常 {}", e.getLocalizedMessage(), e);
|
||||
}
|
||||
}
|
||||
}, ukeyProperties.getWsUrl());
|
||||
webSocketConnectionManager.start();
|
||||
try {
|
||||
LockSupport.parkNanos(reference.get().getThread(), 1000 * 1000 * 1000 * 1L);
|
||||
} catch (Exception e) {
|
||||
log.error("线程自动unpark异常 {}", e.getLocalizedMessage(), e);
|
||||
} finally {
|
||||
webSocketConnectionManager.stop();
|
||||
}
|
||||
return reference.get().getResponseArgs();
|
||||
}
|
||||
|
||||
}
|
||||
-28
@@ -1,28 +0,0 @@
|
||||
package cn.alphahub.eport.signature.entity;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 海关 179 数据抓取
|
||||
* <p>
|
||||
* 成功数据格式:
|
||||
* <pre>
|
||||
* {
|
||||
* "code": "10000",
|
||||
* "message": "",
|
||||
* "serviceTime": 1567050097628
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @author weasley
|
||||
* @version 1.1.0
|
||||
*/
|
||||
@Data
|
||||
public class Capture179DataResponse implements Serializable {
|
||||
private String code;
|
||||
private long total;
|
||||
private String message;
|
||||
private long serviceTime;
|
||||
}
|
||||
-43
@@ -1,43 +0,0 @@
|
||||
package cn.alphahub.eport.signature.util;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* 获取客户端的真实 IP 地址
|
||||
*
|
||||
* @author weasley
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public final class ClientIPUtils {
|
||||
static final String UNKNOWN = "unknown";
|
||||
|
||||
private ClientIPUtils() {
|
||||
}
|
||||
|
||||
public static String getClientIP(HttpServletRequest request) {
|
||||
String ipAddress = request.getHeader("X-Forwarded-For");
|
||||
|
||||
if (null == ipAddress || ipAddress.isEmpty() || UNKNOWN.equalsIgnoreCase(ipAddress)) {
|
||||
ipAddress = request.getHeader("Proxy-Client-IP");
|
||||
}
|
||||
if (null == ipAddress || ipAddress.isEmpty() || UNKNOWN.equalsIgnoreCase(ipAddress)) {
|
||||
ipAddress = request.getHeader("WL-Proxy-Client-IP");
|
||||
}
|
||||
if (null == ipAddress || ipAddress.isEmpty() || UNKNOWN.equalsIgnoreCase(ipAddress)) {
|
||||
ipAddress = request.getHeader("HTTP_CLIENT_IP");
|
||||
}
|
||||
if (null == ipAddress || ipAddress.isEmpty() || UNKNOWN.equalsIgnoreCase(ipAddress)) {
|
||||
ipAddress = request.getHeader("HTTP_X_FORWARDED_FOR");
|
||||
}
|
||||
if (null == ipAddress || ipAddress.isEmpty() || UNKNOWN.equalsIgnoreCase(ipAddress)) {
|
||||
ipAddress = request.getRemoteAddr();
|
||||
}
|
||||
|
||||
// 如果客户端经过了多级反向代理,那么 X-Forwarded-For 中的值是以逗号分隔的 IP 地址列表,取第一个非 unknown 的 IP 地址
|
||||
if (ipAddress != null && ipAddress.contains(",")) {
|
||||
ipAddress = ipAddress.split(",")[0].trim();
|
||||
}
|
||||
|
||||
return ipAddress;
|
||||
}
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
logging:
|
||||
level:
|
||||
cn.alphahub.eport.signature: debug
|
||||
org.apache.http: error
|
||||
org.springframework.core: warn
|
||||
org.springframework.beans: warn
|
||||
org.springframework.context: warn
|
||||
org.springframework.web: warn
|
||||
org.springframework.web.servlet: info
|
||||
org.springframework.cloud.openfeign: debug
|
||||
org.springframework.web.servlet.DispatcherServlet: debug
|
||||
org.springframework.context.support.PropertySourcesPlaceholderConfigurer: info
|
||||
org.springframework.security.core.userdetails: debug
|
||||
org.springframework.jdbc.datasource.DataSourceTransactionManager: debug
|
||||
jdbc.sqltiming: debug
|
||||
com.alibaba.cloud.nacos.client: error
|
||||
com.alibaba.nacos: error
|
||||
org.springframework.web.reactive.function.client.ExchangeFunctions: trace
|
||||
reactor.netty.http.client.HttpClient: DEBUG
|
||||
|
||||
file:
|
||||
path: ./logs/
|
||||
name: ${logging.file.path}/${spring.application.name}.log
|
||||
charset:
|
||||
file: UTF-8
|
||||
logback:
|
||||
rollingpolicy:
|
||||
max-file-size: 5MB
|
||||
clean-history-on-start: true
|
||||
|
||||
spring:
|
||||
jackson:
|
||||
default-property-inclusion: non_null
|
||||
|
||||
#邮件通知配置,默认:不启用
|
||||
mail:
|
||||
enable: false
|
||||
#主收件人邮箱
|
||||
to: abc@qq.com
|
||||
#抄送箱邮(非必填),抄送多个收件人箱邮用","隔开
|
||||
cc: abc@outlook.com,cbd@qq.com
|
||||
#以下发送邮件的客服端参数,使用哪个邮箱发出通知消息
|
||||
host: smtp.189.cn #邮件服务商主机
|
||||
port: 465 #端口号
|
||||
username: xxx@189.cn
|
||||
password: your_password
|
||||
protocol: smtp" #协议
|
||||
properties:
|
||||
mail:
|
||||
smtp:
|
||||
ssl:
|
||||
enable: true
|
||||
debug: false #是否开始debug模式,终端会打印日志
|
||||
|
||||
mvc:
|
||||
log-request-details: on
|
||||
|
||||
#中国电子口岸u-key的配置参数
|
||||
eport:
|
||||
signature:
|
||||
algorithm: sm2_sm3
|
||||
ukey:
|
||||
ws-url: ws://127.0.0.1:61232
|
||||
password: 88888888
|
||||
health:
|
||||
endpoint:
|
||||
# Windows上重启的ukey可执行文件全限定文件名称
|
||||
client-name: C:\中国电子口岸客户端控件\SetAccessControl.exe
|
||||
# 客户端请求鉴权token, 生产环境不建议使用默认token
|
||||
auth:
|
||||
enable: on
|
||||
token: DefaultAuthToken
|
||||
# 报文传输企业的元数据配置(数据上报)
|
||||
report:
|
||||
# CEBMessage XML加签配置
|
||||
ceb-message:
|
||||
cop-code: 传输企业代码
|
||||
cop-name: 海南省荣誉进出口贸易有限公司
|
||||
dxp-id: 向中国电子口岸数据中心申请数据交换平台的用户编号
|
||||
# 数据上报服务器地址,格式: http://ip:port, 缺省将采用默认server
|
||||
server:
|
||||
# 海关 179 数据抓取配置
|
||||
customs179:
|
||||
ebp-code: 电商平台代码
|
||||
# server可为空,配置文件中指定时,将被优先使用
|
||||
server: https://海关服务器域名不方便公开/ceb2grab/grab/realTimeDataUpload
|
||||
@@ -1,10 +0,0 @@
|
||||
${AnsiColor.BRIGHT_YELLOW}
|
||||
________ _ ______ __ _____ _ __
|
||||
/ ____/ /_ (_)___ ____ _ / ____/___ ____ _____/ /_ / ___/(_)___ _____ ____ _/ /___ __________
|
||||
/ / / __ \/ / __ \/ __ `/ / __/ / __ \/ __ \/ ___/ __/ \__ \/ / __ `/ __ \/ __ `/ __/ / / / ___/ _ \
|
||||
/ /___/ / / / / / / / /_/ / / /___/ /_/ / /_/ / / / /_ ___/ / / /_/ / / / / /_/ / /_/ /_/ / / / __/
|
||||
\____/_/ /_/_/_/ /_/\__,_/ /_____/ .___/\____/_/ \__/ /____/_/\__, /_/ /_/\__,_/\__/\__,_/_/ \___/
|
||||
/_/ /____/
|
||||
|
||||
${AnsiColor.BRIGHT_GREEN}${application.title} ${application.formatted-version}
|
||||
Powered by Spring Boot ${spring-boot.formatted-version}${AnsiStyle.NORMAL}
|
||||
File diff suppressed because one or more lines are too long
@@ -1,259 +0,0 @@
|
||||
let api = [];
|
||||
const apiDocListSize = 1
|
||||
api.push({
|
||||
name: 'default',
|
||||
order: '1',
|
||||
list: []
|
||||
})
|
||||
api[0].list.push({
|
||||
alias: 'EportTestController',
|
||||
order: '1',
|
||||
link: '电子口岸报文加签(测试)',
|
||||
desc: '电子口岸报文加签(测试)',
|
||||
list: []
|
||||
})
|
||||
api[0].list[0].list.push({
|
||||
order: '1',
|
||||
deprecated: 'false',
|
||||
url: 'http://localhost:8080/rpc/eport/test/cebmessage/signature',
|
||||
desc: '海关总署XML数据加签',
|
||||
});
|
||||
api[0].list[0].list.push({
|
||||
order: '2',
|
||||
deprecated: 'false',
|
||||
url: 'http://localhost:8080/rpc/eport/test/179/signature',
|
||||
desc: '海关179数据抓取加签',
|
||||
});
|
||||
api[0].list[0].list.push({
|
||||
order: '3',
|
||||
deprecated: 'false',
|
||||
url: 'http://localhost:8080/rpc/eport/test/endpoint/exec',
|
||||
desc: '执行脚本命令',
|
||||
});
|
||||
api[0].list.push({
|
||||
alias: 'EportReportResultController',
|
||||
order: '2',
|
||||
link: '查询申报回执结果',
|
||||
desc: '查询申报回执结果',
|
||||
list: []
|
||||
})
|
||||
api[0].list[1].list.push({
|
||||
order: '1',
|
||||
deprecated: 'false',
|
||||
url: 'http://localhost:8080/rpc/eport/result/ceb312msg',
|
||||
desc: '查询311进口单申报结果',
|
||||
});
|
||||
api[0].list[1].list.push({
|
||||
order: '2',
|
||||
deprecated: 'false',
|
||||
url: 'http://localhost:8080/rpc/eport/result/ceb622msg',
|
||||
desc: '查询621进口单申报结果',
|
||||
});
|
||||
api[0].list.push({
|
||||
alias: 'UkeyHealthController',
|
||||
order: '3',
|
||||
link: 'ukey健康状态恢复',
|
||||
desc: 'Ukey健康状态恢复',
|
||||
list: []
|
||||
})
|
||||
api[0].list[2].list.push({
|
||||
order: '1',
|
||||
deprecated: 'false',
|
||||
url: 'http://localhost:8080/rpc/eport/ukey/health/endpoint/{command}',
|
||||
desc: 'ukey健康指令',
|
||||
});
|
||||
api[0].list.push({
|
||||
alias: 'EportUploadController',
|
||||
order: '4',
|
||||
link: '电子口岸报文推送',
|
||||
desc: '电子口岸报文推送',
|
||||
list: []
|
||||
})
|
||||
api[0].list[3].list.push({
|
||||
order: '1',
|
||||
deprecated: 'false',
|
||||
url: 'http://localhost:8080/rpc/eport/upload/CEBMessage',
|
||||
desc: '推送CEBMessage报文',
|
||||
});
|
||||
api[0].list[3].list.push({
|
||||
order: '2',
|
||||
deprecated: 'false',
|
||||
url: 'http://localhost:8080/rpc/eport/upload/179/data',
|
||||
desc: '推送海关179号数据',
|
||||
});
|
||||
api[0].list.push({
|
||||
alias: 'EportCertController',
|
||||
order: '5',
|
||||
link: '电子口岸x509证书',
|
||||
desc: '电子口岸X509证书',
|
||||
list: []
|
||||
})
|
||||
api[0].list[4].list.push({
|
||||
order: '1',
|
||||
deprecated: 'false',
|
||||
url: 'http://localhost:8080/rpc/eport/cert/download',
|
||||
desc: '下载证书',
|
||||
});
|
||||
api[0].list.push({
|
||||
alias: 'EportSignController',
|
||||
order: '6',
|
||||
link: '电子口岸报文加签',
|
||||
desc: '电子口岸报文加签',
|
||||
list: []
|
||||
})
|
||||
api[0].list[5].list.push({
|
||||
order: '1',
|
||||
deprecated: 'false',
|
||||
url: 'http://localhost:8080/rpc/eport/signature',
|
||||
desc: '海关数据加签',
|
||||
});
|
||||
document.onkeydown = keyDownSearch;
|
||||
function keyDownSearch(e) {
|
||||
const theEvent = e;
|
||||
const code = theEvent.keyCode || theEvent.which || theEvent.charCode;
|
||||
if (code === 13) {
|
||||
const search = document.getElementById('search');
|
||||
const searchValue = search.value.toLocaleLowerCase();
|
||||
|
||||
let searchGroup = [];
|
||||
for (let i = 0; i < api.length; i++) {
|
||||
|
||||
let apiGroup = api[i];
|
||||
|
||||
let searchArr = [];
|
||||
for (let i = 0; i < apiGroup.list.length; i++) {
|
||||
let apiData = apiGroup.list[i];
|
||||
const desc = apiData.desc;
|
||||
if (desc.toLocaleLowerCase().indexOf(searchValue) > -1) {
|
||||
searchArr.push({
|
||||
order: apiData.order,
|
||||
desc: apiData.desc,
|
||||
link: apiData.link,
|
||||
list: apiData.list
|
||||
});
|
||||
} else {
|
||||
let methodList = apiData.list || [];
|
||||
let methodListTemp = [];
|
||||
for (let j = 0; j < methodList.length; j++) {
|
||||
const methodData = methodList[j];
|
||||
const methodDesc = methodData.desc;
|
||||
if (methodDesc.toLocaleLowerCase().indexOf(searchValue) > -1) {
|
||||
methodListTemp.push(methodData);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (methodListTemp.length > 0) {
|
||||
const data = {
|
||||
order: apiData.order,
|
||||
desc: apiData.desc,
|
||||
link: apiData.link,
|
||||
list: methodListTemp
|
||||
};
|
||||
searchArr.push(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (apiGroup.name.toLocaleLowerCase().indexOf(searchValue) > -1) {
|
||||
searchGroup.push({
|
||||
name: apiGroup.name,
|
||||
order: apiGroup.order,
|
||||
list: searchArr
|
||||
});
|
||||
continue;
|
||||
}
|
||||
if (searchArr.length === 0) {
|
||||
continue;
|
||||
}
|
||||
searchGroup.push({
|
||||
name: apiGroup.name,
|
||||
order: apiGroup.order,
|
||||
list: searchArr
|
||||
});
|
||||
}
|
||||
let html;
|
||||
if (searchValue === '') {
|
||||
const liClass = "";
|
||||
const display = "display: none";
|
||||
html = buildAccordion(api,liClass,display);
|
||||
document.getElementById('accordion').innerHTML = html;
|
||||
} else {
|
||||
const liClass = "open";
|
||||
const display = "display: block";
|
||||
html = buildAccordion(searchGroup,liClass,display);
|
||||
document.getElementById('accordion').innerHTML = html;
|
||||
}
|
||||
const Accordion = function (el, multiple) {
|
||||
this.el = el || {};
|
||||
this.multiple = multiple || false;
|
||||
const links = this.el.find('.dd');
|
||||
links.on('click', {el: this.el, multiple: this.multiple}, this.dropdown);
|
||||
};
|
||||
Accordion.prototype.dropdown = function (e) {
|
||||
const $el = e.data.el;
|
||||
let $this = $(this), $next = $this.next();
|
||||
$next.slideToggle();
|
||||
$this.parent().toggleClass('open');
|
||||
if (!e.data.multiple) {
|
||||
$el.find('.submenu').not($next).slideUp("20").parent().removeClass('open');
|
||||
}
|
||||
};
|
||||
new Accordion($('#accordion'), false);
|
||||
}
|
||||
}
|
||||
|
||||
function buildAccordion(apiGroups, liClass, display) {
|
||||
let html = "";
|
||||
if (apiGroups.length > 0) {
|
||||
if (apiDocListSize === 1) {
|
||||
let apiData = apiGroups[0].list;
|
||||
let order = apiGroups[0].order;
|
||||
for (let j = 0; j < apiData.length; j++) {
|
||||
html += '<li class="'+liClass+'">';
|
||||
html += '<a class="dd" href="#_'+order+'_'+apiData[j].order+'_' + apiData[j].link + '">' + apiData[j].order + '. ' + apiData[j].desc + '</a>';
|
||||
html += '<ul class="sectlevel2" style="'+display+'">';
|
||||
let doc = apiData[j].list;
|
||||
for (let m = 0; m < doc.length; m++) {
|
||||
let spanString;
|
||||
if (doc[m].deprecated === 'true') {
|
||||
spanString='<span class="line-through">';
|
||||
} else {
|
||||
spanString='<span>';
|
||||
}
|
||||
html += '<li><a href="#_'+order+'_' + apiData[j].order + '_' + doc[m].order + '_' + doc[m].desc + '">' + apiData[j].order + '.' + doc[m].order + '. ' + spanString + doc[m].desc + '<span></a> </li>';
|
||||
}
|
||||
html += '</ul>';
|
||||
html += '</li>';
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < apiGroups.length; i++) {
|
||||
let apiGroup = apiGroups[i];
|
||||
html += '<li class="'+liClass+'">';
|
||||
html += '<a class="dd" href="#_'+apiGroup.order+'_' + apiGroup.name + '">' + apiGroup.order + '. ' + apiGroup.name + '</a>';
|
||||
html += '<ul class="sectlevel1">';
|
||||
|
||||
let apiData = apiGroup.list;
|
||||
for (let j = 0; j < apiData.length; j++) {
|
||||
html += '<li class="'+liClass+'">';
|
||||
html += '<a class="dd" href="#_'+apiGroup.order+'_'+ apiData[j].order + '_'+ apiData[j].link + '">' +apiGroup.order+'.'+ apiData[j].order + '. ' + apiData[j].desc + '</a>';
|
||||
html += '<ul class="sectlevel2" style="'+display+'">';
|
||||
let doc = apiData[j].list;
|
||||
for (let m = 0; m < doc.length; m++) {
|
||||
let spanString;
|
||||
if (doc[m].deprecated === 'true') {
|
||||
spanString='<span class="line-through">';
|
||||
} else {
|
||||
spanString='<span>';
|
||||
}
|
||||
html += '<li><a href="#_'+apiGroup.order+'_' + apiData[j].order + '_' + doc[m].order + '_' + doc[m].desc + '">'+apiGroup.order+'.' + apiData[j].order + '.' + doc[m].order + '. ' + spanString + doc[m].desc + '<span></a> </li>';
|
||||
}
|
||||
html += '</ul>';
|
||||
html += '</li>';
|
||||
}
|
||||
|
||||
html += '</ul>';
|
||||
html += '</li>';
|
||||
}
|
||||
}
|
||||
}
|
||||
return html;
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
.hljs{display:block;overflow-x:auto;color:#eaeaea;background:#000;padding:.5em}.hljs-subst{color:#eaeaea}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}.hljs-builtin-name,.hljs-type{color:#eaeaea}.hljs-params{color:#da0000}.hljs-literal,.hljs-name,.hljs-number{color:red;font-weight:bolder}.hljs-comment{color:#969896}.hljs-quote,.hljs-selector-id{color:#0ff}.hljs-template-variable,.hljs-title,.hljs-variable{color:#0ff;font-weight:700}.hljs-keyword,.hljs-selector-class,.hljs-symbol{color:#fff000}.hljs-bullet,.hljs-string{color:#0f0}.hljs-section,.hljs-tag{color:#000fff}.hljs-selector-tag{color:#000fff;font-weight:700}.hljs-attribute,.hljs-built_in,.hljs-link,.hljs-regexp{color:#f0f}.hljs-meta{color:#fff;font-weight:bolder}
|
||||
@@ -1,115 +0,0 @@
|
||||
<ceb:CEB621Message xmlns:ceb="http://www.chinaport.gov.cn/ceb" guid="CEB621_HNZB_FXJK_20220208175054_0034"
|
||||
version="v1.0">
|
||||
<ceb:Inventory>
|
||||
<ceb:InventoryHead>
|
||||
<ceb:guid>CEB621_HNZB_FXJK_20220208175055_0035</ceb:guid>
|
||||
<ceb:appType>1</ceb:appType>
|
||||
<ceb:appTime>20220208175055</ceb:appTime>
|
||||
<ceb:appStatus>2</ceb:appStatus>
|
||||
<ceb:orderNo>48037605991776256</ceb:orderNo>
|
||||
<ceb:ebpCode>46121601BC</ceb:ebpCode>
|
||||
<ceb:ebpName>海南星创互联网医药有限公司</ceb:ebpName>
|
||||
<ceb:ebcCode>46121601BC</ceb:ebcCode>
|
||||
<ceb:ebcName>海南星创互联网医药有限公司</ceb:ebcName>
|
||||
<ceb:logisticsNo>2222222</ceb:logisticsNo>
|
||||
<ceb:logisticsCode>4101986180</ceb:logisticsCode>
|
||||
<ceb:logisticsName>圆通</ceb:logisticsName>
|
||||
<ceb:copNo>4101W68006</ceb:copNo>
|
||||
<ceb:assureCode>4101960AGP</ceb:assureCode>
|
||||
<ceb:ieFlag>I</ceb:ieFlag>
|
||||
<ceb:declTime>20220208</ceb:declTime>
|
||||
<ceb:customsCode>4601</ceb:customsCode>
|
||||
<ceb:portCode>4601</ceb:portCode>
|
||||
<ceb:ieDate>20220208</ceb:ieDate>
|
||||
<ceb:buyerIdType>1</ceb:buyerIdType>
|
||||
<ceb:buyerIdNumber>612326199005306719</ceb:buyerIdNumber>
|
||||
<ceb:buyerName>用户_15150368</ceb:buyerName>
|
||||
<ceb:buyerTelephone>18209182500</ceb:buyerTelephone>
|
||||
<ceb:consigneeAddress>哈布斯堡</ceb:consigneeAddress>
|
||||
<ceb:agentCode>46121601BC</ceb:agentCode>
|
||||
<ceb:agentName>海南星创互联网医药有限公司</ceb:agentName>
|
||||
<ceb:tradeMode>9610</ceb:tradeMode>
|
||||
<ceb:trafMode>W</ceb:trafMode>
|
||||
<ceb:country>142</ceb:country>
|
||||
<ceb:freight>0.00</ceb:freight>
|
||||
<ceb:insuredFee>0</ceb:insuredFee>
|
||||
<ceb:currency>142</ceb:currency>
|
||||
<ceb:packNo>1</ceb:packNo>
|
||||
<ceb:grossWeight>0.07</ceb:grossWeight>
|
||||
<ceb:netWeight>0.06</ceb:netWeight>
|
||||
</ceb:InventoryHead>
|
||||
<ceb:InventoryList>
|
||||
<ceb:gnum>1</ceb:gnum>
|
||||
<ceb:itemRecordNo>0797776159482</ceb:itemRecordNo>
|
||||
<ceb:itemNo>HNGGKJ00000057</ceb:itemNo>
|
||||
<ceb:itemName>通用铁剂胶囊</ceb:itemName>
|
||||
<ceb:gcode>2106909090</ceb:gcode>
|
||||
<ceb:gname>ACTIVE IRON</ceb:gname>
|
||||
<ceb:gmodel>4|3|乳清蛋白(牛奶)65%,胶囊壳(羟苯基甲基纤维素)21%,硫酸亚铁1.2%,酸度调节剂2%,填充剂 10.8%|30粒/盒|无中文名称/ACTIVE IRON</ceb:gmodel>
|
||||
<ceb:country>306</ceb:country>
|
||||
<ceb:currency>142</ceb:currency>
|
||||
<ceb:qty>1</ceb:qty>
|
||||
<ceb:unit>140</ceb:unit>
|
||||
<ceb:qty1>0.06</ceb:qty1>
|
||||
<ceb:unit1>035</ceb:unit1>
|
||||
<ceb:price>0.01</ceb:price>
|
||||
<ceb:totalPrice>0.01</ceb:totalPrice>
|
||||
</ceb:InventoryList>
|
||||
</ceb:Inventory>
|
||||
<ceb:BaseTransfer>
|
||||
<ceb:copCode>46121601BC</ceb:copCode>
|
||||
<ceb:copName>海南星创互联网医药有限公司</ceb:copName>
|
||||
<ceb:dxpMode>DXP</ceb:dxpMode>
|
||||
<ceb:dxpId>DXPENT0000458763</ceb:dxpId>
|
||||
</ceb:BaseTransfer>
|
||||
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
|
||||
<ds:SignedInfo>
|
||||
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
|
||||
<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
|
||||
<ds:Reference URI="">
|
||||
<ds:Transforms>
|
||||
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
|
||||
</ds:Transforms>
|
||||
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
|
||||
<ds:DigestValue>b2FtM/jHvW4fnJpMMPWk/177RwA=</ds:DigestValue>
|
||||
</ds:Reference>
|
||||
</ds:SignedInfo>
|
||||
<ds:SignatureValue>
|
||||
mhWpJI3RsJZGiR8MqI3kcJAJPqgVOQi0IFJzp0CBWLG+Qa7ZnYJX+hi70J+/09diHYkCMDxcKuT5uogbFvi/9rOZnrn+YsL0h0/nGcO31lx/RpqT8i5GoSt/UcZe9COIbnrKwdh+90FLYIW4xS3Rsglv4CDL+e53GGmSdduSTUU=
|
||||
</ds:SignatureValue>
|
||||
<ds:KeyInfo>
|
||||
<ds:KeyName>01691fe9</ds:KeyName>
|
||||
<ds:X509Data>
|
||||
<ds:X509Certificate>
|
||||
bjEVMBMGA1UECh4MTi1W/XU1W1BT41y4MRUwEwYDVQQLHgyLwU5me6F0Bk4tX8Mx
|
||||
wzENMAsGA1UEBx4EUxdOrDAeFw0yMTA1MzEwMDAwMDBaFw0zMTA1MzEwMDAwMDBa
|
||||
MIIE+TCCBGKgAwIBAgIEAWkf6TANBgkqhkiG9w0BAQUFADB8MQswCQYDVQQGEwJj
|
||||
DTALBgNVBAgeBFMXTqwxITAfBgNVBAMeGE4tVv11NVtQThpSoYvBTmZ7oXQGTi1f
|
||||
A4GNADCBiQKBgQDt9T7JOQikKKL7oY7RWbRMp7y2VsrXIbFzSDh5DZQPimduUijZ
|
||||
xgG4Kdr0TwxAQ+s3H85fCU81ROgqAS05/IzOR7eDSEYYT9CRmNYXIRTPVwIDAQAB
|
||||
MBwxDTALBgNVBAMeBH8qdDMxCzAJBgNVBBIeAgAxMIGfMA0GCSqGSIb3DQEBAQUA
|
||||
o4IC5jCCAuIwCwYDVR0PBAQDAgbAMAkGA1UdEwQCMAAwgacGA1UdIwSBnzCBnIAU
|
||||
zZlK6AkZMDYJBM2/IJGI0QjWjBJVZ8hEUkgJb4UOOBCBM+cCGFCNY5LX/mAo5Boe
|
||||
Ch4MTi1W/XU1W1BT41y4MRUwEwYDVQQLHgyLwU5me6F0Bk4tX8MxDTALBgNVBAge
|
||||
Bx4EUxdOrIIBADAdBgNVHQ4EFgQU1eAz50lwZ6gidtkJNPIr7O0jKvUwQgYDVR0g
|
||||
BDswOTA3BgYrgQcBAQIwLTArBggrBgEFBQcCARYfaHR0cDovL2Nwcy5jaGluYXBv
|
||||
+XWjeEULQmjCBFOr68NOPlR4dGChgYCkfjB8MQswCQYDVQQGEwJjbjEVMBMGA1UE
|
||||
BFMXTqwxITAfBgNVBAMeGE4tVv11NVtQThpSoYvBTmZ7oXQGTi1fwzENMAsGA1UE
|
||||
cnQuZ292LmNuL0NQUzByBgNVHR8EazBpMDCgLqAshipsZGFwOi8vbGRhcC5jaGlu
|
||||
YXBvcnQuZ292LmNuOjM4OS8wMDAtMS5jcmwwNaAzoDGGL2h0dHA6Ly9sZGFwLmNo
|
||||
aW5hcG9ydC5nb3YuY246ODA4OC9kemthMDAwLTEuY3JsMG0GCCsGAQUFBwEBBGEw
|
||||
LzAtBggrBgEFBQcwAYYhaHR0cDovL29jc3AuY2hpbmFwb3J0Lmdvdi5jbjo4MDg4
|
||||
XzAuBggrBgEFBQcwAYYiaHR0cDovL29jc3AuY2hpbmFwb3J0Lmdvdi5jbjo4ODAw
|
||||
KwYBBAGpQ2QFAwQEFgIwMTAiBgorBgEEAalDZAUIBBQWEjMxMDEwMjE5ODAwNzI0
|
||||
MCoGCisGAQQBqUNkBQEEHBYauqPEz9DHtLS7pcGqzfjSvdKp09DP3rmry74wEgYK
|
||||
CwQLFglNQTVUTFVBNTMwEgYKKwYBBAGpQ2QFDAQEFgIwMDASBgorBgEEAalDZAIE
|
||||
MzIxNDAdBgorBgEEAalDZAUJBA8WDUpKMEc5MDAxNTkyNzIwGQYKKwYBBAGpQ2QF
|
||||
MxAzM4bXvdlDcE6fODsCvQMFKctlA+LCllFQwl58HaBmcWx4T/ddKF9LBYc8A986
|
||||
BAQWAjE0MBIGCisGAQQBqUNkAgEEBBYCMTIwDQYJKoZIhvcNAQEFBQADgYEAqSuO
|
||||
LlcUw6Mkwxraj2WO+meXdDzRLEO8t3gyZk7tYp5aneV4zGYUsphwyMdLt8N8o4kV
|
||||
gg16bQy43XgA1jRMv8nvhb908IqQQBxwv0SIuXU=
|
||||
</ds:X509Certificate>
|
||||
</ds:X509Data>
|
||||
</ds:KeyInfo>
|
||||
</ds:Signature>
|
||||
</ceb:CEB621Message>
|
||||
-49
File diff suppressed because one or more lines are too long
-87
@@ -1,87 +0,0 @@
|
||||
package cn.alphahub.eport.signature.after202207;
|
||||
|
||||
/**
|
||||
* SignConstant
|
||||
*
|
||||
* @author weasley
|
||||
* @version 1.0.0
|
||||
*/
|
||||
public interface SignConstant {
|
||||
/**
|
||||
* 179入参示例
|
||||
*/
|
||||
String sign179 = """
|
||||
"sessionID":"ad2254-8hewyf32-55616249"||"payExchangeInfoHead":"{"guid":"9D55BA71-22DE-41F4-8B50-C36C83B3B530","initalRequest":"原始请求","initalResponse":"ok","ebpCode":"4404840022","payCode":"312226T001","payTransactionId":"2018121222001354081010726129","totalAmount":100,"currency":"142","verDept":"3","payType":"1","tradingTime":"20181212041803","note":"批量订单,测试订单优化,生成多个so订单"}"||"payExchangeInfoLists":"[{"orderNo":"SO1710301150602574003","goodsInfo":[{"gname":"lhy-gnsku3","itemLink":"http://m.yunjiweidian.com/yunjibuyer/static/vue-buyer/idc/index.html#/detail?itemId=999761&shopId=453"},{"gname":"lhy-gnsku2","itemLink":"http://m.yunjiweidian.com/yunjibuyer/static/vue-buyer/idc/index.html#/detail?itemId=999760&shopId=453"}],"recpAccount":"OSA571908863132601","recpCode":"","recpName":"YUNJIHONGKONGLIMITED"}]"||"serviceTime":"1544519952469"
|
||||
""";
|
||||
|
||||
/**
|
||||
* XML入参示例
|
||||
*/
|
||||
String signXml = """
|
||||
<ceb:CEB621Message xmlns:ceb="http://www.chinaport.gov.cn/ceb" guid="CEB621_HNZB_FXJK_20220208175054_0034"
|
||||
version="v1.0">
|
||||
<ceb:Inventory>
|
||||
<ceb:InventoryHead>
|
||||
<ceb:guid>CEB621_HNZB_FXJK_20220208175055_0035</ceb:guid>
|
||||
<ceb:appType>1</ceb:appType>
|
||||
<ceb:appTime>20220208175055</ceb:appTime>
|
||||
<ceb:appStatus>2</ceb:appStatus>
|
||||
<ceb:orderNo>48037605991776256</ceb:orderNo>
|
||||
<ceb:ebpCode>46121601BC</ceb:ebpCode>
|
||||
<ceb:ebpName>海南星创互联网医药有限公司</ceb:ebpName>
|
||||
<ceb:ebcCode>46121601BC</ceb:ebcCode>
|
||||
<ceb:ebcName>海南星创互联网医药有限公司</ceb:ebcName>
|
||||
<ceb:logisticsNo>2222222</ceb:logisticsNo>
|
||||
<ceb:logisticsCode>4101986180</ceb:logisticsCode>
|
||||
<ceb:logisticsName>圆通</ceb:logisticsName>
|
||||
<ceb:copNo>4101W68006</ceb:copNo>
|
||||
<ceb:assureCode>4101960AGP</ceb:assureCode>
|
||||
<ceb:ieFlag>I</ceb:ieFlag>
|
||||
<ceb:declTime>20220208</ceb:declTime>
|
||||
<ceb:customsCode>4601</ceb:customsCode>
|
||||
<ceb:portCode>4601</ceb:portCode>
|
||||
<ceb:ieDate>20220208</ceb:ieDate>
|
||||
<ceb:buyerIdType>1</ceb:buyerIdType>
|
||||
<ceb:buyerIdNumber>612326199005306719</ceb:buyerIdNumber>
|
||||
<ceb:buyerName>用户_15150368</ceb:buyerName>
|
||||
<ceb:buyerTelephone>18209182500</ceb:buyerTelephone>
|
||||
<ceb:consigneeAddress>哈布斯堡</ceb:consigneeAddress>
|
||||
<ceb:agentCode>46121601BC</ceb:agentCode>
|
||||
<ceb:agentName>海南星创互联网医药有限公司</ceb:agentName>
|
||||
<ceb:tradeMode>9610</ceb:tradeMode>
|
||||
<ceb:trafMode>W</ceb:trafMode>
|
||||
<ceb:country>142</ceb:country>
|
||||
<ceb:freight>0.00</ceb:freight>
|
||||
<ceb:insuredFee>0</ceb:insuredFee>
|
||||
<ceb:currency>142</ceb:currency>
|
||||
<ceb:packNo>1</ceb:packNo>
|
||||
<ceb:grossWeight>0.07</ceb:grossWeight>
|
||||
<ceb:netWeight>0.06</ceb:netWeight>
|
||||
</ceb:InventoryHead>
|
||||
<ceb:InventoryList>
|
||||
<ceb:gnum>1</ceb:gnum>
|
||||
<ceb:itemRecordNo>0797776159482</ceb:itemRecordNo>
|
||||
<ceb:itemNo>HNGGKJ00000057</ceb:itemNo>
|
||||
<ceb:itemName>通用铁剂胶囊</ceb:itemName>
|
||||
<ceb:gcode>2106909090</ceb:gcode>
|
||||
<ceb:gname>ACTIVE IRON</ceb:gname>
|
||||
<ceb:gmodel>4|3|乳清蛋白(牛奶)65%,胶囊壳(羟苯基甲基纤维素)21%,硫酸亚铁1.2%,酸度调节剂2%,填充剂 10.8%|30粒/盒|无中文名称/ACTIVE IRON</ceb:gmodel>
|
||||
<ceb:country>306</ceb:country>
|
||||
<ceb:currency>142</ceb:currency>
|
||||
<ceb:qty>1</ceb:qty>
|
||||
<ceb:unit>140</ceb:unit>
|
||||
<ceb:qty1>0.06</ceb:qty1>
|
||||
<ceb:unit1>035</ceb:unit1>
|
||||
<ceb:price>0.01</ceb:price>
|
||||
<ceb:totalPrice>0.01</ceb:totalPrice>
|
||||
</ceb:InventoryList>
|
||||
</ceb:Inventory>
|
||||
<ceb:BaseTransfer>
|
||||
<ceb:copCode>46121601BC</ceb:copCode>
|
||||
<ceb:copName>海南星创互联网医药有限公司</ceb:copName>
|
||||
<ceb:dxpMode>DXP</ceb:dxpMode>
|
||||
<ceb:dxpId>DXPENT0000458763</ceb:dxpId>
|
||||
</ceb:BaseTransfer>
|
||||
</ceb:CEB621Message>
|
||||
""";
|
||||
}
|
||||
-36
@@ -1,36 +0,0 @@
|
||||
package cn.alphahub.eport.signature.after202207;
|
||||
|
||||
import cn.alphahub.eport.signature.entity.UkeyRequest;
|
||||
import cn.hutool.json.JSONConfig;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 查看海关证书有效期
|
||||
*
|
||||
* @author weasley
|
||||
* @version 1.0.7
|
||||
*/
|
||||
@SpringBootTest
|
||||
public class TestCusSpcGetValidTimeFromCert {
|
||||
@Autowired
|
||||
private SignClient signClient;
|
||||
|
||||
@Test
|
||||
@DisplayName("查看海关证书有效期")
|
||||
void testCusSpcGetValidTimeFromCert() {
|
||||
Map<String, Object> args = new LinkedHashMap<>();
|
||||
//args.put("cert", null);//证书内容,可为空
|
||||
UkeyRequest ukeyRequest = new UkeyRequest("cus-sec_SpcGetValidTimeFromCert", 1, args);
|
||||
JSONConfig jsonConfig = new JSONConfig();
|
||||
String jsonStr = JSONUtil.toJsonStr(ukeyRequest, jsonConfig);
|
||||
System.err.println(jsonStr);
|
||||
signClient.sign(ukeyRequest);
|
||||
}
|
||||
}
|
||||
-161
@@ -1,161 +0,0 @@
|
||||
package cn.alphahub.eport.signature.c14;
|
||||
|
||||
|
||||
import cn.alphahub.eport.signature.core.SignatureHandler;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
/**
|
||||
* Sign CEB311Message With C14n
|
||||
* <p>
|
||||
*
|
||||
* @author lwj
|
||||
* @version 1.0
|
||||
* @date 2022-01-11 14:23
|
||||
*/
|
||||
@SpringBootTest
|
||||
class C14nEncodeTests {
|
||||
|
||||
@Test
|
||||
void testSignCEB311MessageWithC14n() {
|
||||
org.apache.xml.security.Init.init();
|
||||
String sourceXml = """
|
||||
<ceb:CEB311Message xmlns:ceb="http://www.chinaport.gov.cn/ceb" guid="4CDE1CFD-EDED-46B1-946C-B8022E42FC94"
|
||||
version="1.0">
|
||||
<link type="text/css" id="dark-mode" rel="stylesheet" href=""/>
|
||||
<ceb:Order>
|
||||
<ceb:OrderHead>
|
||||
<ceb:guid>4CDE1CFD-EDED-46B1-946C-B8022E42FC94</ceb:guid>
|
||||
<ceb:appType>1</ceb:appType>
|
||||
<ceb:appTime>20160308112701</ceb:appTime>
|
||||
<ceb:appStatus>2</ceb:appStatus>
|
||||
<ceb:orderType>I</ceb:orderType>
|
||||
<ceb:orderNo>order20160321116421002</ceb:orderNo>
|
||||
<ceb:ebpCode>1105910159</ceb:ebpCode>
|
||||
<ceb:ebpName>东方物通科技(北京)有限公司</ceb:ebpName>
|
||||
<ceb:ebcCode>1105910159</ceb:ebcCode>
|
||||
<ceb:ebcName>东方物通科技(北京)有限公司</ceb:ebcName>
|
||||
<ceb:goodsValue>14000</ceb:goodsValue>
|
||||
<ceb:freight>5000</ceb:freight>
|
||||
<ceb:discount>0</ceb:discount>
|
||||
<ceb:taxTotal>50</ceb:taxTotal>
|
||||
<ceb:acturalPaid>19050</ceb:acturalPaid>
|
||||
<ceb:currency>142</ceb:currency>
|
||||
<ceb:buyerRegNo>ID20160001</ceb:buyerRegNo>
|
||||
<ceb:buyerName>aa</ceb:buyerName>
|
||||
<ceb:buyerTelephone>18813025940</ceb:buyerTelephone>
|
||||
<ceb:buyerIdType>1</ceb:buyerIdType>
|
||||
<ceb:buyerIdNumber>130681136250023332</ceb:buyerIdNumber>
|
||||
<ceb:payCode>1105910159</ceb:payCode>
|
||||
<ceb:payName>东方物通科技(北京)有限公司</ceb:payName>
|
||||
<ceb:payTransactionId>20160001634226001</ceb:payTransactionId>
|
||||
<ceb:batchNumbers>20160317</ceb:batchNumbers>
|
||||
<ceb:consignee>焦洪宇</ceb:consignee>
|
||||
<ceb:consigneeTelephone>13522652231</ceb:consigneeTelephone>
|
||||
<ceb:consigneeAddress>北京市海淀区</ceb:consigneeAddress>
|
||||
<ceb:consigneeDistrict>072750</ceb:consigneeDistrict>
|
||||
<ceb:note>test</ceb:note>
|
||||
</ceb:OrderHead>
|
||||
<ceb:OrderList>
|
||||
<ceb:gnum>1</ceb:gnum>
|
||||
<ceb:itemNo>AF001-001</ceb:itemNo>
|
||||
<ceb:itemName>b</ceb:itemName>
|
||||
<ceb:gmodel>33mm</ceb:gmodel>
|
||||
<ceb:itemDescribe>v</ceb:itemDescribe>
|
||||
<ceb:barCode>2345123</ceb:barCode>
|
||||
<ceb:unit>007</ceb:unit>
|
||||
<ceb:qty>100</ceb:qty>
|
||||
<ceb:price>20</ceb:price>
|
||||
<ceb:totalPrice>2000</ceb:totalPrice>
|
||||
<ceb:currency>142</ceb:currency>
|
||||
<ceb:country>116</ceb:country>
|
||||
<ceb:note/>
|
||||
</ceb:OrderList>
|
||||
</ceb:Order>
|
||||
<ceb:BaseTransfer>
|
||||
<ceb:copCode>1101180326</ceb:copCode>
|
||||
<ceb:copName>物流企业</ceb:copName>
|
||||
<ceb:dxpMode>DXP</ceb:dxpMode>
|
||||
<ceb:dxpId>EXP2016522002580001</ceb:dxpId>
|
||||
<ceb:note>test</ceb:note>
|
||||
</ceb:BaseTransfer>
|
||||
</ceb:CEB311Message>
|
||||
""";
|
||||
String messageWithC14n = SignatureHandler.getDigestValueOfCEBXxxMessage(sourceXml);
|
||||
System.err.println(messageWithC14n);
|
||||
assert "vDf4oKPVFtRft5U57TNW85kJT18=".equals(messageWithC14n);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSha1HexCEB621Message() {
|
||||
String xml = """
|
||||
<ceb:CEB621Message xmlns:ceb="http://www.chinaport.gov.cn/ceb" guid="CEB621_HNZB_FXJK_20220208175054_0034"
|
||||
version="v1.0">
|
||||
<ceb:Inventory>
|
||||
<ceb:InventoryHead>
|
||||
<ceb:guid>CEB621_HNZB_FXJK_20220208175055_0035</ceb:guid>
|
||||
<ceb:appType>1</ceb:appType>
|
||||
<ceb:appTime>20220208175055</ceb:appTime>
|
||||
<ceb:appStatus>2</ceb:appStatus>
|
||||
<ceb:orderNo>48037605991776256</ceb:orderNo>
|
||||
<ceb:ebpCode>46121601BC</ceb:ebpCode>
|
||||
<ceb:ebpName>海南星创互联网医药有限公司</ceb:ebpName>
|
||||
<ceb:ebcCode>46121601BC</ceb:ebcCode>
|
||||
<ceb:ebcName>海南星创互联网医药有限公司</ceb:ebcName>
|
||||
<ceb:logisticsNo>2222222</ceb:logisticsNo>
|
||||
<ceb:logisticsCode>4101986180</ceb:logisticsCode>
|
||||
<ceb:logisticsName>圆通</ceb:logisticsName>
|
||||
<ceb:copNo>4101W68006</ceb:copNo>
|
||||
<ceb:assureCode>4101960AGP</ceb:assureCode>
|
||||
<ceb:ieFlag>I</ceb:ieFlag>
|
||||
<ceb:declTime>20220208</ceb:declTime>
|
||||
<ceb:customsCode>4601</ceb:customsCode>
|
||||
<ceb:portCode>4601</ceb:portCode>
|
||||
<ceb:ieDate>20220208</ceb:ieDate>
|
||||
<ceb:buyerIdType>1</ceb:buyerIdType>
|
||||
<ceb:buyerIdNumber>612326199005306719</ceb:buyerIdNumber>
|
||||
<ceb:buyerName>用户_15150368</ceb:buyerName>
|
||||
<ceb:buyerTelephone>18209182500</ceb:buyerTelephone>
|
||||
<ceb:consigneeAddress>哈布斯堡</ceb:consigneeAddress>
|
||||
<ceb:agentCode>46121601BC</ceb:agentCode>
|
||||
<ceb:agentName>海南星创互联网医药有限公司</ceb:agentName>
|
||||
<ceb:tradeMode>9610</ceb:tradeMode>
|
||||
<ceb:trafMode>W</ceb:trafMode>
|
||||
<ceb:country>142</ceb:country>
|
||||
<ceb:freight>0.00</ceb:freight>
|
||||
<ceb:insuredFee>0</ceb:insuredFee>
|
||||
<ceb:currency>142</ceb:currency>
|
||||
<ceb:packNo>1</ceb:packNo>
|
||||
<ceb:grossWeight>0.07</ceb:grossWeight>
|
||||
<ceb:netWeight>0.06</ceb:netWeight>
|
||||
</ceb:InventoryHead>
|
||||
<ceb:InventoryList>
|
||||
<ceb:gnum>1</ceb:gnum>
|
||||
<ceb:itemRecordNo>0797776159482</ceb:itemRecordNo>
|
||||
<ceb:itemNo>HNGGKJ00000057</ceb:itemNo>
|
||||
<ceb:itemName>通用铁剂胶囊</ceb:itemName>
|
||||
<ceb:gcode>2106909090</ceb:gcode>
|
||||
<ceb:gname>ACTIVE IRON</ceb:gname>
|
||||
<ceb:gmodel>4|3|乳清蛋白(牛奶)65%,胶囊壳(羟苯基甲基纤维素)21%,硫酸亚铁1.2%,酸度调节剂2%,填充剂 10.8%|30粒/盒|无中文名称/ACTIVE IRON</ceb:gmodel>
|
||||
<ceb:country>306</ceb:country>
|
||||
<ceb:currency>142</ceb:currency>
|
||||
<ceb:qty>1</ceb:qty>
|
||||
<ceb:unit>140</ceb:unit>
|
||||
<ceb:qty1>0.06</ceb:qty1>
|
||||
<ceb:unit1>035</ceb:unit1>
|
||||
<ceb:price>0.01</ceb:price>
|
||||
<ceb:totalPrice>0.01</ceb:totalPrice>
|
||||
</ceb:InventoryList>
|
||||
</ceb:Inventory>
|
||||
<ceb:BaseTransfer>
|
||||
<ceb:copCode>46121601BC</ceb:copCode>
|
||||
<ceb:copName>海南星创互联网医药有限公司</ceb:copName>
|
||||
<ceb:dxpMode>DXP</ceb:dxpMode>
|
||||
<ceb:dxpId>DXPENT0000458763</ceb:dxpId>
|
||||
</ceb:BaseTransfer>
|
||||
</ceb:CEB621Message>
|
||||
""";
|
||||
System.out.println(xml);
|
||||
}
|
||||
|
||||
}
|
||||
-308
@@ -1,308 +0,0 @@
|
||||
package cn.alphahub.eport.signature.client;
|
||||
|
||||
import cn.alphahub.dtt.plus.util.SpringUtil;
|
||||
import cn.alphahub.eport.signature.config.UkeyInitialConfig;
|
||||
import cn.alphahub.eport.signature.config.UkeyInitialConfigTest;
|
||||
import cn.alphahub.eport.signature.core.CertificateHandler;
|
||||
import cn.alphahub.eport.signature.core.SignHandler;
|
||||
import cn.alphahub.eport.signature.entity.SignRequest;
|
||||
import cn.alphahub.eport.signature.entity.SignResult;
|
||||
import cn.alphahub.eport.signature.entity.UkeyRequest;
|
||||
import cn.alphahub.eport.signature.entity.UkeyResponse.Args;
|
||||
import cn.alphahub.eport.signature.support.CertificateParser;
|
||||
import cn.hutool.core.lang.TypeReference;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* WebSocketClient测试类
|
||||
*
|
||||
* @author weasley
|
||||
* @version 1.0
|
||||
* @date 2022/2/12
|
||||
*/
|
||||
@Slf4j
|
||||
@SpringBootTest
|
||||
class SignClientTest {
|
||||
private static final String PASSWORD = "88888888";
|
||||
|
||||
@Autowired
|
||||
private SignHandler signClient;
|
||||
|
||||
@Test
|
||||
void sendInitDataAsPEMWithHashPTest() {
|
||||
SignRequest request = new SignRequest(UkeyInitialConfigTest.CEB621Message);
|
||||
String parameter = UkeyInitialConfig.getSignDataAsPEM(request);
|
||||
SignResult result = signClient.sign(request, parameter);
|
||||
System.out.println(JSONUtil.toJsonPrettyStr(result));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取版本号
|
||||
*/
|
||||
@Test
|
||||
void ukeyVersion() {
|
||||
String method = "cus-sec_SpcGetCardAttachInfo";
|
||||
UkeyRequest ukeyRequest = new UkeyRequest(method, new LinkedHashMap<>() {{
|
||||
put("passwd", PASSWORD);
|
||||
}});
|
||||
Args args = this.signClient.getUkeyResponseArgs(ukeyRequest);
|
||||
System.out.println(JSONUtil.toJsonPrettyStr(args));
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用卡计算摘要,返回PEM格式信息
|
||||
*/
|
||||
@Test
|
||||
@DisplayName("使用卡计算摘要,返回PEM格式信息")
|
||||
void calculationSummaryUsingCards() {
|
||||
String method = "cus-sec_SpcSHA1Digest";
|
||||
UkeyRequest ukeyRequest = new UkeyRequest(method, new LinkedHashMap<>() {{
|
||||
put("szInfo", """
|
||||
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/><ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sm2-sm3"/><ds:Reference URI=""><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><ds:DigestValue>8JmrLw4jIIsHx+qb7D7U9eKPlWo=</ds:DigestValue></ds:Reference></ds:SignedInfo>
|
||||
""");
|
||||
put("passwd", PASSWORD);
|
||||
}});
|
||||
Args args = this.signClient.getUkeyResponseArgs(ukeyRequest);
|
||||
System.out.println(JSONUtil.toJsonPrettyStr(args));
|
||||
}
|
||||
|
||||
@Test
|
||||
void calculationSummaryUsingCards2() {
|
||||
String method = "cus-sec_SpcSHA1DigestAsPEM";
|
||||
String data = """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ceb:CEB311Message guid="tCEB311_HNZB_HNFX_20230707223752_003" version="1.0" xmlns:ceb="http://www.chinaport.gov.cn/ceb" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<ceb:Order>
|
||||
<ceb:OrderHead>
|
||||
<ceb:guid>tCEB311_HNZB_HNFX_20230707223752_001</ceb:guid>
|
||||
<ceb:appType>1</ceb:appType>
|
||||
<ceb:appTime>20230704181028</ceb:appTime>
|
||||
<ceb:appStatus>2</ceb:appStatus>
|
||||
<ceb:orderType>I</ceb:orderType>
|
||||
<ceb:orderNo>T_C5051511332138160010</ceb:orderNo>
|
||||
<ceb:ebpCode>4601630004</ceb:ebpCode>
|
||||
<ceb:ebpName>海南省荣誉进出口贸易有限公司</ceb:ebpName>
|
||||
<ceb:ebcCode>4601630004</ceb:ebcCode>
|
||||
<ceb:ebcName>海南省荣誉进出口贸易有限公司</ceb:ebcName>
|
||||
<ceb:goodsValue>0.01</ceb:goodsValue>
|
||||
<ceb:freight>0</ceb:freight>
|
||||
<ceb:discount>0</ceb:discount>
|
||||
<ceb:taxTotal>0</ceb:taxTotal>
|
||||
<ceb:acturalPaid>0.01</ceb:acturalPaid>
|
||||
<ceb:currency>142</ceb:currency>
|
||||
<ceb:buyerRegNo>4</ceb:buyerRegNo>
|
||||
<ceb:buyerName>袁晓雨</ceb:buyerName>
|
||||
<ceb:buyerTelephone>13701727375</ceb:buyerTelephone>
|
||||
<ceb:buyerIdType>1</ceb:buyerIdType>
|
||||
<ceb:buyerIdNumber>130435200009241538</ceb:buyerIdNumber>
|
||||
<ceb:consignee>袁晓雨</ceb:consignee>
|
||||
<ceb:consigneeTelephone>13701727375</ceb:consigneeTelephone>
|
||||
<ceb:consigneeAddress>北京北京市东城区</ceb:consigneeAddress>
|
||||
<ceb:note>test</ceb:note>
|
||||
</ceb:OrderHead>
|
||||
<ceb:OrderList>
|
||||
<ceb:gnum>1</ceb:gnum>
|
||||
<ceb:itemNo>1</ceb:itemNo>
|
||||
<ceb:itemName>LANNA兰纳</ceb:itemName>
|
||||
<ceb:gmodel>10片/包</ceb:gmodel>
|
||||
<ceb:itemDescribe></ceb:itemDescribe>
|
||||
<ceb:barCode>1</ceb:barCode>
|
||||
<ceb:unit>011</ceb:unit>
|
||||
<ceb:qty>1</ceb:qty>
|
||||
<ceb:price>1</ceb:price>
|
||||
<ceb:totalPrice>1</ceb:totalPrice>
|
||||
<ceb:currency>142</ceb:currency>
|
||||
<ceb:country>136</ceb:country>
|
||||
<ceb:note>test</ceb:note>
|
||||
</ceb:OrderList>
|
||||
</ceb:Order>
|
||||
<ceb:BaseTransfer>
|
||||
<ceb:copCode>4601630004</ceb:copCode>
|
||||
<ceb:copName>海南省荣誉进出口贸易有限公司</ceb:copName>
|
||||
<ceb:dxpMode>DXP</ceb:dxpMode>
|
||||
<ceb:dxpId>DXPENT0000530815</ceb:dxpId>
|
||||
<ceb:note>test</ceb:note>
|
||||
</ceb:BaseTransfer>
|
||||
</ceb:CEB311Message>
|
||||
""";
|
||||
SignRequest signRequest = new SignRequest(data);
|
||||
UkeyRequest ukeyRequest = JSONUtil.toBean(UkeyInitialConfig.getSHA1DigestAsPEMParams(signRequest), new TypeReference<>() {
|
||||
}, true);
|
||||
Args args = this.signClient.getUkeyResponseArgs(ukeyRequest);
|
||||
System.out.println(JSONUtil.toJsonPrettyStr(args));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("获取Usb-Key的序列号(ID)")
|
||||
void f1() {
|
||||
HashMap<String, Object> _args = new HashMap<>();
|
||||
UkeyRequest ukeyRequest = new UkeyRequest("cus-sec_SpcGetCardID", _args);
|
||||
Args args = signClient.getUkeyResponseArgs(ukeyRequest);
|
||||
System.err.println("获取卡号: " + JSONUtil.toJsonStr(args));
|
||||
|
||||
ukeyRequest = new UkeyRequest("cus-cus-sec_SpcGetCardAttachInfo", _args);
|
||||
args = signClient.getUkeyResponseArgs(ukeyRequest);
|
||||
System.err.println("获取附加信息: " + JSONUtil.toJsonStr(args));
|
||||
|
||||
ukeyRequest = new UkeyRequest("cus-sec_SpcGetCertNo", _args);
|
||||
args = signClient.getUkeyResponseArgs(ukeyRequest);
|
||||
System.err.println("获取证书编号: " + JSONUtil.toJsonStr(args));
|
||||
|
||||
ukeyRequest = new UkeyRequest("cus-sec_SpcGetUName", _args);
|
||||
args = signClient.getUkeyResponseArgs(ukeyRequest);
|
||||
System.err.println("申请者名称: " + JSONUtil.toJsonStr(args));
|
||||
|
||||
ukeyRequest = new UkeyRequest("cus-sec_SpcGetEntID", _args);
|
||||
args = signClient.getUkeyResponseArgs(ukeyRequest);
|
||||
System.err.println("单位ID: " + JSONUtil.toJsonStr(args));
|
||||
|
||||
ukeyRequest = new UkeyRequest("cus-sec_SpcGetEntName", _args);
|
||||
args = signClient.getUkeyResponseArgs(ukeyRequest);
|
||||
System.err.println("单位名: " + JSONUtil.toJsonStr(args));
|
||||
|
||||
ukeyRequest = new UkeyRequest("cus-sec_SpcGetSignCert", _args);
|
||||
args = signClient.getUkeyResponseArgs(ukeyRequest);
|
||||
System.err.println("取海关签名证书: " + JSONUtil.toJsonStr(args));
|
||||
|
||||
ukeyRequest = new UkeyRequest("cus-sec_SpcGetSignCertAsPEM", _args);
|
||||
args = signClient.getUkeyResponseArgs(ukeyRequest);
|
||||
System.err.println("取海关签名证书PEM: " + JSONUtil.toJsonStr(args));
|
||||
|
||||
ukeyRequest = new UkeyRequest("cus-sec_SpcGetEnvCert", _args);
|
||||
args = signClient.getUkeyResponseArgs(ukeyRequest);
|
||||
System.err.println("取海关加密证书: " + JSONUtil.toJsonStr(args));
|
||||
|
||||
ukeyRequest = new UkeyRequest("cus-sec_SpcGetEnvCertAsPEM", _args);
|
||||
args = signClient.getUkeyResponseArgs(ukeyRequest);
|
||||
System.err.println("取海关加密证书PEM: " + JSONUtil.toJsonStr(args));
|
||||
|
||||
ukeyRequest = new UkeyRequest("cus-sec_SpcGetValidTimeFromCert", _args);
|
||||
args = signClient.getUkeyResponseArgs(ukeyRequest);
|
||||
System.err.println("查看海关证书有效期: " + JSONUtil.toJsonStr(args));
|
||||
|
||||
// inData 原始内容
|
||||
_args.put("inData", "MIIEoDCCBESgAwIBAgIIAwAAAAAM3m8wDAYIKoEcz1UBg3UFADCBmDELMAkGA1UEBhMCQ04xDzANBgNVBAgMBuWMl+S6rDEPMA0GA1UEBwwG5YyX5LqsMRswGQYDVQQKDBLkuK3lm73nlLXlrZDlj6PlsrgxGzAZBgNVBAsMEuivgeS5pueuoeeQhuS4reW/gzEtMCsGA1UEAwwk5Lit5Zu955S15a2Q5Lia5Yqh6K+B5Lmm566h55CG5Lit5b+DMB4XDTIzMDMyOTAwMDAwMFoXDTMzMDMyOTAwMDAwMFowVjELMAkGA1UEBhMCQ04xMzAxBgNVBAsMKua1t+WNl+ecgeiNo+iqiei/m+WHuuWPo+i0uOaYk+aciemZkOWFrOWPuDESMBAGA1UEAwwJ5p2o5aaC6YeRMFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE0vOQmplAr9igPZrA8F1msqnFd0U++6G6NhG5rNuIUWft0BwQn7eSJkt5/fvSSoe7pUg2/awHUWPnzkeeQc7oVqOCArUwggKxMBEGCWCGSAGG+EIBAQQEAwIFoDAOBgNVHQ8BAf8EBAMCBsAwCQYDVR0TBAIwADApBgNVHSUEIjAgBggrBgEFBQcDAgYIKwYBBQUHAwQGCisGAQQBgjcUAgIwHwYDVR0jBBgwFoAURCQxt0wEvoAVXmuo4N1bjKXTh0UwHQYDVR0OBBYEFAytGob5L0WqhOCZ5l6Lf2jUdNrAMGgGA1UdIARhMF8wXQYEVR0gADBVMFMGCCsGAQUFBwIBFkdodHRwczovL3d3dy5jaGluYXBvcnQuZ292LmNuL3RjbXNmaWxlL3UvY21zL3d3dy8yMDIyMDQvMTIxMzI5NDh4dDZwLnBkZjB/BgNVHR8EeDB2MHSgcqBwhm5sZGFwOi8vbGRhcC5jaGluYXBvcnQuZ292LmNuOjM4OS9jbj1jcmwwMzAwMDAsb3U9Y3JsMDAsb3U9Y3JsLGM9Y24/Y2VydGlmaWNhdGVSZXZvY2F0aW9uTGlzdD9iYXNlP2NuPWNybDAzMDAwMDA+BggrBgEFBQcBAQQyMDAwLgYIKwYBBQUHMAGGImh0dHA6Ly9vY3NwLmNoaW5hcG9ydC5nb3YuY246ODgwMC8wOgYKKwYBBAGpQ2QFAQQsDCrmtbfljZfnnIHojaPoqonov5vlh7rlj6PotLjmmJPmnInpmZDlhazlj7gwEgYKKwYBBAGpQ2QFAwQEDAIwMTAiBgorBgEEAalDZAUIBBQMEjUxMjMyNDE5NjQxMDE3Mjk3WDAgBgorBgEEAalDZAUJBBIMEDAzLUpKMEc5MDAyMjA3NTIwGQYKKwYBBAGpQ2QFCwQLDAlNQTVUTkZHWTkwEgYKKwYBBAGpQ2QFDAQEDAIwMDASBgorBgEEAalDZAIBBAQMAjEyMBIGCisGAQQBqUNkAgQEBAwCMTQwDAYIKoEcz1UBg3UFAANIADBFAiBM4OVAc8aaCZU4XFfcVMkC7bWIIenRnPLxrnwVeYO3CQIhANQ767YIurkJCoLtwyqQPbUZe/+3BjGZcIWqB1mAl9T+"); //原始内容
|
||||
ukeyRequest = new UkeyRequest("cus-sec_SpcEncodePEM", _args);
|
||||
args = signClient.getUkeyResponseArgs(ukeyRequest);
|
||||
System.err.println("PEM编码: " + JSONUtil.toJsonStr(args));
|
||||
|
||||
//inData PEM编码的内容(字符串)
|
||||
_args.put("inData", "MIIEoDCCBESgAwIBAgIIAwAAAAAM3m8wDAYIKoEcz1UBg3UFADCBmDELMAkGA1UEBhMCQ04xDzANBgNVBAgMBuWMl+S6rDEPMA0GA1UEBwwG5YyX5LqsMRswGQYDVQQKDBLkuK3lm73nlLXlrZDlj6PlsrgxGzAZBgNVBAsMEuivgeS5pueuoeeQhuS4reW/gzEtMCsGA1UEAwwk5Lit5Zu955S15a2Q5Lia5Yqh6K+B5Lmm566h55CG5Lit5b+DMB4XDTIzMDMyOTAwMDAwMFoXDTMzMDMyOTAwMDAwMFowVjELMAkGA1UEBhMCQ04xMzAxBgNVBAsMKua1t+WNl+ecgeiNo+iqiei/m+WHuuWPo+i0uOaYk+aciemZkOWFrOWPuDESMBAGA1UEAwwJ5p2o5aaC6YeRMFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE0vOQmplAr9igPZrA8F1msqnFd0U++6G6NhG5rNuIUWft0BwQn7eSJkt5/fvSSoe7pUg2/awHUWPnzkeeQc7oVqOCArUwggKxMBEGCWCGSAGG+EIBAQQEAwIFoDAOBgNVHQ8BAf8EBAMCBsAwCQYDVR0TBAIwADApBgNVHSUEIjAgBggrBgEFBQcDAgYIKwYBBQUHAwQGCisGAQQBgjcUAgIwHwYDVR0jBBgwFoAURCQxt0wEvoAVXmuo4N1bjKXTh0UwHQYDVR0OBBYEFAytGob5L0WqhOCZ5l6Lf2jUdNrAMGgGA1UdIARhMF8wXQYEVR0gADBVMFMGCCsGAQUFBwIBFkdodHRwczovL3d3dy5jaGluYXBvcnQuZ292LmNuL3RjbXNmaWxlL3UvY21zL3d3dy8yMDIyMDQvMTIxMzI5NDh4dDZwLnBkZjB/BgNVHR8EeDB2MHSgcqBwhm5sZGFwOi8vbGRhcC5jaGluYXBvcnQuZ292LmNuOjM4OS9jbj1jcmwwMzAwMDAsb3U9Y3JsMDAsb3U9Y3JsLGM9Y24/Y2VydGlmaWNhdGVSZXZvY2F0aW9uTGlzdD9iYXNlP2NuPWNybDAzMDAwMDA+BggrBgEFBQcBAQQyMDAwLgYIKwYBBQUHMAGGImh0dHA6Ly9vY3NwLmNoaW5hcG9ydC5nb3YuY246ODgwMC8wOgYKKwYBBAGpQ2QFAQQsDCrmtbfljZfnnIHojaPoqonov5vlh7rlj6PotLjmmJPmnInpmZDlhazlj7gwEgYKKwYBBAGpQ2QFAwQEDAIwMTAiBgorBgEEAalDZAUIBBQMEjUxMjMyNDE5NjQxMDE3Mjk3WDAgBgorBgEEAalDZAUJBBIMEDAzLUpKMEc5MDAyMjA3NTIwGQYKKwYBBAGpQ2QFCwQLDAlNQTVUTkZHWTkwEgYKKwYBBAGpQ2QFDAQEDAIwMDASBgorBgEEAalDZAIBBAQMAjEyMBIGCisGAQQBqUNkAgQEBAwCMTQwDAYIKoEcz1UBg3UFAANIADBFAiBM4OVAc8aaCZU4XFfcVMkC7bWIIenRnPLxrnwVeYO3CQIhANQ767YIurkJCoLtwyqQPbUZe/+3BjGZcIWqB1mAl9T+"); //原始内容
|
||||
ukeyRequest = new UkeyRequest("cus-sec_SpcDecodePEM", _args);
|
||||
args = signClient.getUkeyResponseArgs(ukeyRequest);
|
||||
System.err.println("PEM编码: " + JSONUtil.toJsonStr(args));
|
||||
|
||||
// inData PEM编码的内容(字符串), PEM解码,返回String类型的原文
|
||||
_args.put("inData", "MIIEoDCCBESgAwIBAgIIAwAAAAAM3m8wDAYIKoEcz1UBg3UFADCBmDELMAkGA1UEBhMCQ04xDzANBgNVBAgMBuWMl+S6rDEPMA0GA1UEBwwG5YyX5LqsMRswGQYDVQQKDBLkuK3lm73nlLXlrZDlj6PlsrgxGzAZBgNVBAsMEuivgeS5pueuoeeQhuS4reW/gzEtMCsGA1UEAwwk5Lit5Zu955S15a2Q5Lia5Yqh6K+B5Lmm566h55CG5Lit5b+DMB4XDTIzMDMyOTAwMDAwMFoXDTMzMDMyOTAwMDAwMFowVjELMAkGA1UEBhMCQ04xMzAxBgNVBAsMKua1t+WNl+ecgeiNo+iqiei/m+WHuuWPo+i0uOaYk+aciemZkOWFrOWPuDESMBAGA1UEAwwJ5p2o5aaC6YeRMFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE0vOQmplAr9igPZrA8F1msqnFd0U++6G6NhG5rNuIUWft0BwQn7eSJkt5/fvSSoe7pUg2/awHUWPnzkeeQc7oVqOCArUwggKxMBEGCWCGSAGG+EIBAQQEAwIFoDAOBgNVHQ8BAf8EBAMCBsAwCQYDVR0TBAIwADApBgNVHSUEIjAgBggrBgEFBQcDAgYIKwYBBQUHAwQGCisGAQQBgjcUAgIwHwYDVR0jBBgwFoAURCQxt0wEvoAVXmuo4N1bjKXTh0UwHQYDVR0OBBYEFAytGob5L0WqhOCZ5l6Lf2jUdNrAMGgGA1UdIARhMF8wXQYEVR0gADBVMFMGCCsGAQUFBwIBFkdodHRwczovL3d3dy5jaGluYXBvcnQuZ292LmNuL3RjbXNmaWxlL3UvY21zL3d3dy8yMDIyMDQvMTIxMzI5NDh4dDZwLnBkZjB/BgNVHR8EeDB2MHSgcqBwhm5sZGFwOi8vbGRhcC5jaGluYXBvcnQuZ292LmNuOjM4OS9jbj1jcmwwMzAwMDAsb3U9Y3JsMDAsb3U9Y3JsLGM9Y24/Y2VydGlmaWNhdGVSZXZvY2F0aW9uTGlzdD9iYXNlP2NuPWNybDAzMDAwMDA+BggrBgEFBQcBAQQyMDAwLgYIKwYBBQUHMAGGImh0dHA6Ly9vY3NwLmNoaW5hcG9ydC5nb3YuY246ODgwMC8wOgYKKwYBBAGpQ2QFAQQsDCrmtbfljZfnnIHojaPoqonov5vlh7rlj6PotLjmmJPmnInpmZDlhazlj7gwEgYKKwYBBAGpQ2QFAwQEDAIwMTAiBgorBgEEAalDZAUIBBQMEjUxMjMyNDE5NjQxMDE3Mjk3WDAgBgorBgEEAalDZAUJBBIMEDAzLUpKMEc5MDAyMjA3NTIwGQYKKwYBBAGpQ2QFCwQLDAlNQTVUTkZHWTkwEgYKKwYBBAGpQ2QFDAQEDAIwMDASBgorBgEEAalDZAIBBAQMAjEyMBIGCisGAQQBqUNkAgQEBAwCMTQwDAYIKoEcz1UBg3UFAANIADBFAiBM4OVAc8aaCZU4XFfcVMkC7bWIIenRnPLxrnwVeYO3CQIhANQ767YIurkJCoLtwyqQPbUZe/+3BjGZcIWqB1mAl9T+"); //原始内容
|
||||
ukeyRequest = new UkeyRequest("cus-sec_SpcDecodePEMAsString", _args);
|
||||
args = signClient.getUkeyResponseArgs(ukeyRequest);
|
||||
System.err.println("PEM解码,返回String类型的原文: " + JSONUtil.toJsonStr(args));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
@DisplayName("签名+取海关签名证书PEM+验证签名")
|
||||
void f2() {
|
||||
HashMap<String, Object> _args = new HashMap<>();
|
||||
String xml = """
|
||||
<ds:SignedInfo xmlns:ceb="http://www.chinaport.gov.cn/ceb" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"></ds:CanonicalizationMethod><ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sm2-sm3"></ds:SignatureMethod><ds:Reference URI=""><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"></ds:Transform></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></ds:DigestMethod><ds:DigestValue>gTNsxKmXAJfylGtBHhi7HfMRnec=</ds:DigestValue></ds:Reference></ds:SignedInfo>
|
||||
""";
|
||||
_args.put("inData", xml);
|
||||
_args.put("passwd", PASSWORD);
|
||||
UkeyRequest ukeyRequest = new UkeyRequest("cus-sec_SpcSignDataAsPEM", _args);
|
||||
Args args = signClient.getUkeyResponseArgs(ukeyRequest);
|
||||
System.err.println("签名,返回PEM格式: " + args.getData().get(0));
|
||||
|
||||
ukeyRequest = new UkeyRequest("cus-sec_SpcGetSignCertAsPEM", _args);
|
||||
Args args2 = signClient.getUkeyResponseArgs(ukeyRequest);
|
||||
System.err.println("取海关签名证书PEM: " + JSONUtil.toJsonStr(args2));
|
||||
|
||||
CertificateHandler reportClient = SpringUtil.getBean(CertificateHandler.class);
|
||||
_args.put("inData", xml); //原文信息
|
||||
_args.put("signData", args.getData().get(0)); //签名信息
|
||||
//海关签名证书PEM
|
||||
//_args.put("certDataPEM", reportClient.buildX509CertificateWithoutHeader(args2.getData().get(0)).replaceAll("\n",""));
|
||||
_args.put("certDataPEM", args2.getData().get(0));
|
||||
args = signClient.getUkeyResponseArgs(new UkeyRequest("cus-sec_SpcVerifySignData", _args));
|
||||
System.err.println("验证签名: " + args.getData().get(0));
|
||||
//签名,返回PEM格式: PABavspV+F57kSl3bzcPmqqWg1fEq6e+TrdwuKr3jDGEtpTuxuBdY2IB+aTGyLFfagQZ6fo8Ph4541OCe6rhPQ==
|
||||
//取海关签名证书PEM: {"Result":true,"Data":["MIIEoDCCBESgAwIBAgIIAwAAAAAM3m8wDAYIKoEcz1UBg3UFADCBmDELMAkGA1UEBhMCQ04xDzANBgNVBAgMBuWMl+S6rDEPMA0GA1UEBwwG5YyX5LqsMRswGQYDVQQKDBLkuK3lm73nlLXlrZDlj6PlsrgxGzAZBgNVBAsMEuivgeS5pueuoeeQhuS4reW/gzEtMCsGA1UEAwwk5Lit5Zu955S15a2Q5Lia5Yqh6K+B5Lmm566h55CG5Lit5b+DMB4XDTIzMDMyOTAwMDAwMFoXDTMzMDMyOTAwMDAwMFowVjELMAkGA1UEBhMCQ04xMzAxBgNVBAsMKua1t+WNl+ecgeiNo+iqiei/m+WHuuWPo+i0uOaYk+aciemZkOWFrOWPuDESMBAGA1UEAwwJ5p2o5aaC6YeRMFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE0vOQmplAr9igPZrA8F1msqnFd0U++6G6NhG5rNuIUWft0BwQn7eSJkt5/fvSSoe7pUg2/awHUWPnzkeeQc7oVqOCArUwggKxMBEGCWCGSAGG+EIBAQQEAwIFoDAOBgNVHQ8BAf8EBAMCBsAwCQYDVR0TBAIwADApBgNVHSUEIjAgBggrBgEFBQcDAgYIKwYBBQUHAwQGCisGAQQBgjcUAgIwHwYDVR0jBBgwFoAURCQxt0wEvoAVXmuo4N1bjKXTh0UwHQYDVR0OBBYEFAytGob5L0WqhOCZ5l6Lf2jUdNrAMGgGA1UdIARhMF8wXQYEVR0gADBVMFMGCCsGAQUFBwIBFkdodHRwczovL3d3dy5jaGluYXBvcnQuZ292LmNuL3RjbXNmaWxlL3UvY21zL3d3dy8yMDIyMDQvMTIxMzI5NDh4dDZwLnBkZjB/BgNVHR8EeDB2MHSgcqBwhm5sZGFwOi8vbGRhcC5jaGluYXBvcnQuZ292LmNuOjM4OS9jbj1jcmwwMzAwMDAsb3U9Y3JsMDAsb3U9Y3JsLGM9Y24/Y2VydGlmaWNhdGVSZXZvY2F0aW9uTGlzdD9iYXNlP2NuPWNybDAzMDAwMDA+BggrBgEFBQcBAQQyMDAwLgYIKwYBBQUHMAGGImh0dHA6Ly9vY3NwLmNoaW5hcG9ydC5nb3YuY246ODgwMC8wOgYKKwYBBAGpQ2QFAQQsDCrmtbfljZfnnIHojaPoqonov5vlh7rlj6PotLjmmJPmnInpmZDlhazlj7gwEgYKKwYBBAGpQ2QFAwQEDAIwMTAiBgorBgEEAalDZAUIBBQMEjUxMjMyNDE5NjQxMDE3Mjk3WDAgBgorBgEEAalDZAUJBBIMEDAzLUpKMEc5MDAyMjA3NTIwGQYKKwYBBAGpQ2QFCwQLDAlNQTVUTkZHWTkwEgYKKwYBBAGpQ2QFDAQEDAIwMDASBgorBgEEAalDZAIBBAQMAjEyMBIGCisGAQQBqUNkAgQEBAwCMTQwDAYIKoEcz1UBg3UFAANIADBFAiBM4OVAc8aaCZU4XFfcVMkC7bWIIenRnPLxrnwVeYO3CQIhANQ767YIurkJCoLtwyqQPbUZe/+3BjGZcIWqB1mAl9T+"],"Error":[]}
|
||||
//验证签名: 成功!
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("签名+验证签名")
|
||||
void f2bak() {
|
||||
String inData = """
|
||||
<ds:SignedInfo xmlns:ceb="http://www.chinaport.gov.cn/ceb" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"></ds:CanonicalizationMethod><ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sm2-sm3"></ds:SignatureMethod><ds:Reference URI=""><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"></ds:Transform></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></ds:DigestMethod><ds:DigestValue>gTNsxKmXAJfylGtBHhi7HfMRnec=</ds:DigestValue></ds:Reference></ds:SignedInfo>
|
||||
""";
|
||||
String certDataPEM = """
|
||||
MIIEoDCCBESgAwIBAgIIAwAAAAAM3m8wDAYIKoEcz1UBg3UFADCBmDELMAkGA1UEBhMCQ04xDzANBgNVBAgMBuWMl+S6rDEPMA0GA1UEBwwG5YyX5LqsMRswGQYDVQQKDBLkuK3lm73nlLXlrZDlj6PlsrgxGzAZBgNVBAsMEuivgeS5pueuoeeQhuS4reW/gzEtMCsGA1UEAwwk5Lit5Zu955S15a2Q5Lia5Yqh6K+B5Lmm566h55CG5Lit5b+DMB4XDTIzMDMyOTAwMDAwMFoXDTMzMDMyOTAwMDAwMFowVjELMAkGA1UEBhMCQ04xMzAxBgNVBAsMKua1t+WNl+ecgeiNo+iqiei/m+WHuuWPo+i0uOaYk+aciemZkOWFrOWPuDESMBAGA1UEAwwJ5p2o5aaC6YeRMFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE0vOQmplAr9igPZrA8F1msqnFd0U++6G6NhG5rNuIUWft0BwQn7eSJkt5/fvSSoe7pUg2/awHUWPnzkeeQc7oVqOCArUwggKxMBEGCWCGSAGG+EIBAQQEAwIFoDAOBgNVHQ8BAf8EBAMCBsAwCQYDVR0TBAIwADApBgNVHSUEIjAgBggrBgEFBQcDAgYIKwYBBQUHAwQGCisGAQQBgjcUAgIwHwYDVR0jBBgwFoAURCQxt0wEvoAVXmuo4N1bjKXTh0UwHQYDVR0OBBYEFAytGob5L0WqhOCZ5l6Lf2jUdNrAMGgGA1UdIARhMF8wXQYEVR0gADBVMFMGCCsGAQUFBwIBFkdodHRwczovL3d3dy5jaGluYXBvcnQuZ292LmNuL3RjbXNmaWxlL3UvY21zL3d3dy8yMDIyMDQvMTIxMzI5NDh4dDZwLnBkZjB/BgNVHR8EeDB2MHSgcqBwhm5sZGFwOi8vbGRhcC5jaGluYXBvcnQuZ292LmNuOjM4OS9jbj1jcmwwMzAwMDAsb3U9Y3JsMDAsb3U9Y3JsLGM9Y24/Y2VydGlmaWNhdGVSZXZvY2F0aW9uTGlzdD9iYXNlP2NuPWNybDAzMDAwMDA+BggrBgEFBQcBAQQyMDAwLgYIKwYBBQUHMAGGImh0dHA6Ly9vY3NwLmNoaW5hcG9ydC5nb3YuY246ODgwMC8wOgYKKwYBBAGpQ2QFAQQsDCrmtbfljZfnnIHojaPoqonov5vlh7rlj6PotLjmmJPmnInpmZDlhazlj7gwEgYKKwYBBAGpQ2QFAwQEDAIwMTAiBgorBgEEAalDZAUIBBQMEjUxMjMyNDE5NjQxMDE3Mjk3WDAgBgorBgEEAalDZAUJBBIMEDAzLUpKMEc5MDAyMjA3NTIwGQYKKwYBBAGpQ2QFCwQLDAlNQTVUTkZHWTkwEgYKKwYBBAGpQ2QFDAQEDAIwMDASBgorBgEEAalDZAIBBAQMAjEyMBIGCisGAQQBqUNkAgQEBAwCMTQwDAYIKoEcz1UBg3UFAANIADBFAiBM4OVAc8aaCZU4XFfcVMkC7bWIIenRnPLxrnwVeYO3CQIhANQ767YIurkJCoLtwyqQPbUZe/+3BjGZcIWqB1mAl9T+
|
||||
""";
|
||||
UkeyRequest ukeyRequest = new UkeyRequest("cus-sec_SpcSignDataAsPEM", new HashMap<>() {{
|
||||
put("inData", inData);
|
||||
put("passwd", PASSWORD);
|
||||
}});
|
||||
Args _args = signClient.getUkeyResponseArgs(ukeyRequest);
|
||||
String signValue = _args.getData().get(0);
|
||||
System.err.println("signValue: " + signValue + ", certNo: " + _args.getData().get(1));
|
||||
// signValue: 9cxkIKyiFi6X5v4JZsJQExJS+3pKmWcWzQjO0ziHHN9BZV5p04gb8XaCkeIQZFlVe93mI47az8xZAwSB1y1l+g==, certNo: 03000000000cde6f
|
||||
Map<String, Object> argsMap = new LinkedHashMap<>();
|
||||
argsMap.put("inData", "<ds:SignedInfo xmlns:ceb=\"http://www.chinaport.gov.cn/ceb\" xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"><ds:CanonicalizationMethod Algorithm=\"http://www.w3.org/TR/2001/REC-xml-c14n-20010315\"></ds:CanonicalizationMethod><ds:SignatureMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#sm2-sm3\"></ds:SignatureMethod><ds:Reference URI=\"\"><ds:Transforms><ds:Transform Algorithm=\"http://www.w3.org/2000/09/xmldsig#enveloped-signature\"></ds:Transform></ds:Transforms><ds:DigestMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#sha1\"></ds:DigestMethod><ds:DigestValue>tYLIrRGmnwZb42LRdVr0ITzkFkA=</ds:DigestValue></ds:Reference></ds:SignedInfo>");
|
||||
argsMap.put("signData", "XvSyJzDPnePobS0kMCdCTevE4OYEKn5QTdHsx6SsZPzMfN9vks4Zlwngndn979VcZzaLw/U7G8aApb4tfarJ/A==");
|
||||
if (StringUtils.isNotBlank(certDataPEM)) {
|
||||
argsMap.put("certDataPEM", certDataPEM);
|
||||
}
|
||||
Args args = signClient.getUkeyResponseArgs(new UkeyRequest("cus-sec_SpcVerifySignData", argsMap));
|
||||
System.err.println("验证签名结果: " + args.getData().get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("取算法类型(取加密算法标识)")
|
||||
void algorithmType() {
|
||||
HashMap<String, Object> _args = new HashMap<>();
|
||||
UkeyRequest ukeyRequest = new UkeyRequest("cus-sec_SpcGetCardID", _args);
|
||||
Args args = signClient.getUkeyResponseArgs(ukeyRequest);
|
||||
System.err.println("获取卡号: " + JSONUtil.toJsonStr(args));
|
||||
|
||||
_args = new HashMap<>();
|
||||
_args.put("lReader", args.getData().get(0));
|
||||
ukeyRequest = new UkeyRequest("ra_GetCryptAlgo", _args);
|
||||
args = signClient.getUkeyResponseArgs(ukeyRequest);
|
||||
System.err.println("取算法类型(取加密算法标识)" + args.getData().get(0));//s
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("证书切割")
|
||||
void certificateCutting() {
|
||||
String pom = "MIIEoDCCBESgAwIBAgIIAwAAAAAM3m8wDAYIKoEcz1UBg3UFADCBmDELMAkGA1UEBhMCQ04xDzANBgNVBAgMBuWMl+S6rDEPMA0GA1UEBwwG5YyX5LqsMRswGQYDVQQKDBLkuK3lm73nlLXlrZDlj6PlsrgxGzAZBgNVBAsMEuivgeS5pueuoeeQhuS4reW/gzEtMCsGA1UEAwwk5Lit5Zu955S15a2Q5Lia5Yqh6K+B5Lmm566h55CG5Lit5b+DMB4XDTIzMDMyOTAwMDAwMFoXDTMzMDMyOTAwMDAwMFowVjELMAkGA1UEBhMCQ04xMzAxBgNVBAsMKua1t+WNl+ecgeiNo+iqiei/m+WHuuWPo+i0uOaYk+aciemZkOWFrOWPuDESMBAGA1UEAwwJ5p2o5aaC6YeRMFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE0vOQmplAr9igPZrA8F1msqnFd0U++6G6NhG5rNuIUWft0BwQn7eSJkt5/fvSSoe7pUg2/awHUWPnzkeeQc7oVqOCArUwggKxMBEGCWCGSAGG+EIBAQQEAwIFoDAOBgNVHQ8BAf8EBAMCBsAwCQYDVR0TBAIwADApBgNVHSUEIjAgBggrBgEFBQcDAgYIKwYBBQUHAwQGCisGAQQBgjcUAgIwHwYDVR0jBBgwFoAURCQxt0wEvoAVXmuo4N1bjKXTh0UwHQYDVR0OBBYEFAytGob5L0WqhOCZ5l6Lf2jUdNrAMGgGA1UdIARhMF8wXQYEVR0gADBVMFMGCCsGAQUFBwIBFkdodHRwczovL3d3dy5jaGluYXBvcnQuZ292LmNuL3RjbXNmaWxlL3UvY21zL3d3dy8yMDIyMDQvMTIxMzI5NDh4dDZwLnBkZjB/BgNVHR8EeDB2MHSgcqBwhm5sZGFwOi8vbGRhcC5jaGluYXBvcnQuZ292LmNuOjM4OS9jbj1jcmwwMzAwMDAsb3U9Y3JsMDAsb3U9Y3JsLGM9Y24/Y2VydGlmaWNhdGVSZXZvY2F0aW9uTGlzdD9iYXNlP2NuPWNybDAzMDAwMDA+BggrBgEFBQcBAQQyMDAwLgYIKwYBBQUHMAGGImh0dHA6Ly9vY3NwLmNoaW5hcG9ydC5nb3YuY246ODgwMC8wOgYKKwYBBAGpQ2QFAQQsDCrmtbfljZfnnIHojaPoqonov5vlh7rlj6PotLjmmJPmnInpmZDlhazlj7gwEgYKKwYBBAGpQ2QFAwQEDAIwMTAiBgorBgEEAalDZAUIBBQMEjUxMjMyNDE5NjQxMDE3Mjk3WDAgBgorBgEEAalDZAUJBBIMEDAzLUpKMEc5MDAyMjA3NTIwGQYKKwYBBAGpQ2QFCwQLDAlNQTVUTkZHWTkwEgYKKwYBBAGpQ2QFDAQEDAIwMDASBgorBgEEAalDZAIBBAQMAjEyMBIGCisGAQQBqUNkAgQEBAwCMTQwDAYIKoEcz1UBg3UFAANIADBFAiBM4OVAc8aaCZU4XFfcVMkC7bWIIenRnPLxrnwVeYO3CQIhANQ767YIurkJCoLtwyqQPbUZe/+3BjGZcIWqB1mAl9T+";
|
||||
CertificateHandler reportClient = SpringUtil.getBean(CertificateHandler.class);
|
||||
System.out.println(CertificateHandler.buildX509CertificateWithoutHeader(pom));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("获取证书信息")
|
||||
void getCertificateInformation() {
|
||||
Args args = signClient.getUkeyResponseArgs(new UkeyRequest("cus-sec_SpcGetSignCertAsPEM", new LinkedHashMap<>()));
|
||||
String x509Certificate = CertificateHandler.buildX509CertificateWithHeader(args.getData().get(0));
|
||||
CertificateParser.parseCertificateByCertText(x509Certificate);
|
||||
//2023-07-10T22:24:26.331+08:00 INFO 37111 --- [ main] c.a.e.signature.core.CertificateParser : 签发者: CN=中国电子业务证书管理中心,OU=证书管理中心,O=中国电子口岸,L=北京,ST=北京,C=CN
|
||||
//2023-07-10T22:24:26.331+08:00 INFO 37111 --- [ main] c.a.e.signature.core.CertificateParser : 主体: CN=杨XX,OU=XX省XX进出口贸易有限公司,C=CN
|
||||
//2023-07-10T22:24:26.344+08:00 INFO 37111 --- [ main] c.a.e.signature.core.CertificateParser : 有效期起始日期: Wed Mar 29 08:00:00 CST 2023
|
||||
//2023-07-10T22:24:26.344+08:00 INFO 37111 --- [ main] c.a.e.signature.core.CertificateParser : 有效期结束日期: Tue Mar 29 08:00:00 CST 2033
|
||||
//2023-07-10T22:24:26.427+08:00 INFO 37111 --- [ main] c.a.e.signature.core.CertificateParser : 公钥算法: EC
|
||||
//2023-07-10T22:24:26.427+08:00 INFO 37111 --- [ main] c.a.e.signature.core.CertificateParser : 签名算法: SM3WITHSM2
|
||||
}
|
||||
|
||||
}
|
||||
-183
@@ -1,183 +0,0 @@
|
||||
package cn.alphahub.eport.signature.controller.rpc;
|
||||
|
||||
import cn.alphahub.eport.signature.config.ChinaEportProperties;
|
||||
import cn.alphahub.eport.signature.core.ChinaEportReportClient;
|
||||
import cn.alphahub.eport.signature.core.SignHandler;
|
||||
import cn.alphahub.eport.signature.core.web.EportCebMessageHttpClient;
|
||||
import cn.alphahub.eport.signature.entity.SignRequest;
|
||||
import cn.alphahub.eport.signature.entity.SignResult;
|
||||
import cn.alphahub.eport.signature.entity.UkeyRequest;
|
||||
import cn.alphahub.eport.signature.entity.UkeyResponse.Args;
|
||||
import cn.hutool.core.codec.Base64;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import io.github.weasleyj.china.eport.sign.constants.MessageType;
|
||||
import io.github.weasleyj.china.eport.sign.model.cebmessage.CEB311Message;
|
||||
import io.github.weasleyj.china.eport.sign.model.request.MessageRequest;
|
||||
import io.github.weasleyj.china.eport.sign.util.GUIDUtil;
|
||||
import io.github.weasleyj.china.eport.sign.util.JAXBUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Eport Sign Controller Test
|
||||
*/
|
||||
@Slf4j
|
||||
@SpringBootTest
|
||||
class EportSignControllerTest {
|
||||
@Autowired
|
||||
SignHandler signHandler;
|
||||
@Autowired
|
||||
EportSignController controller;
|
||||
@Autowired
|
||||
ChinaEportProperties chinaEportProperties;
|
||||
@Autowired
|
||||
ChinaEportReportClient chinaEportReportClient;
|
||||
@Autowired
|
||||
EportCebMessageHttpClient cebMessageHttpClient;
|
||||
|
||||
@Test
|
||||
@DisplayName("海关XML数据加签+验正签名结果")
|
||||
void signAndVerifyXMLSignature() {
|
||||
String data = """
|
||||
{
|
||||
"id": 1,
|
||||
"data": "<?xml version=\\"1.0\\" encoding=\\"UTF-8\\"?>\\n<ceb:CEB311Message guid=\\"tCEB311_HNZB_HNFX_20230707223752_003\\" version=\\"1.0\\" xmlns:ceb=\\"http://www.chinaport.gov.cn/ceb\\" xmlns:xsi=\\"http://www.w3.org/2001/XMLSchema-instance\\">\\n <ceb:Order>\\n <ceb:OrderHead>\\n <ceb:guid>tCEB311_HNZB_HNFX_20230707223752_001</ceb:guid>\\n <ceb:appType>1</ceb:appType>\\n <ceb:appTime>20230704181028</ceb:appTime>\\n <ceb:appStatus>2</ceb:appStatus>\\n <ceb:orderType>I</ceb:orderType>\\n <ceb:orderNo>T_C5051511332138160010</ceb:orderNo>\\n <ceb:ebpCode>4601630004</ceb:ebpCode>\\n <ceb:ebpName>海南省荣誉进出口贸易有限公司</ceb:ebpName>\\n <ceb:ebcCode>4601630004</ceb:ebcCode>\\n <ceb:ebcName>海南省荣誉进出口贸易有限公司</ceb:ebcName>\\n <ceb:goodsValue>0.01</ceb:goodsValue>\\n <ceb:freight>0</ceb:freight>\\n <ceb:discount>0</ceb:discount>\\n <ceb:taxTotal>0</ceb:taxTotal>\\n <ceb:acturalPaid>0.01</ceb:acturalPaid>\\n <ceb:currency>142</ceb:currency>\\n <ceb:buyerRegNo>4</ceb:buyerRegNo>\\n <ceb:buyerName>袁晓雨</ceb:buyerName>\\n <ceb:buyerTelephone>13701727375</ceb:buyerTelephone>\\n <ceb:buyerIdType>1</ceb:buyerIdType>\\n <ceb:buyerIdNumber>130435200009241538</ceb:buyerIdNumber>\\n <ceb:consignee>袁晓雨</ceb:consignee>\\n <ceb:consigneeTelephone>13701727375</ceb:consigneeTelephone>\\n <ceb:consigneeAddress>北京北京市东城区</ceb:consigneeAddress>\\n <ceb:note>test</ceb:note>\\n </ceb:OrderHead>\\n <ceb:OrderList>\\n <ceb:gnum>1</ceb:gnum>\\n <ceb:itemNo>1</ceb:itemNo>\\n <ceb:itemName>LANNA兰纳</ceb:itemName>\\n <ceb:gmodel>10片/包</ceb:gmodel>\\n <ceb:itemDescribe></ceb:itemDescribe>\\n <ceb:barCode>1</ceb:barCode>\\n <ceb:unit>011</ceb:unit>\\n <ceb:qty>1</ceb:qty>\\n <ceb:price>1</ceb:price>\\n <ceb:totalPrice>1</ceb:totalPrice>\\n <ceb:currency>142</ceb:currency>\\n <ceb:country>136</ceb:country>\\n <ceb:note>test</ceb:note>\\n </ceb:OrderList>\\n </ceb:Order>\\n <ceb:BaseTransfer>\\n <ceb:copCode>4601630004</ceb:copCode>\\n <ceb:copName>海南省荣誉进出口贸易有限公司</ceb:copName>\\n <ceb:dxpMode>DXP</ceb:dxpMode>\\n <ceb:dxpId>DXPENT0000530815</ceb:dxpId>\\n <ceb:note>test</ceb:note>\\n </ceb:BaseTransfer>\\n</ceb:CEB311Message>"
|
||||
}
|
||||
""";
|
||||
SignRequest request = JSONUtil.toBean(data, SignRequest.class);
|
||||
SignResult result = controller.signature(request).getData();
|
||||
Map<String, Object> argsMap = new LinkedHashMap<>();
|
||||
argsMap.put("inData", result.getSignatureNode());
|
||||
argsMap.put("signData", result.getSignatureValue());
|
||||
argsMap.put("certDataPEM", result.getX509Certificate());
|
||||
Args args = signHandler.getUkeyResponseArgs(new UkeyRequest("cus-sec_SpcVerifySignData", argsMap));
|
||||
System.err.println("验签结果: " + args.getData().get(0)); //验签结果: 成功!
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("海关179数据加签+验正签名结果")
|
||||
void signAndVerify179Signature() {
|
||||
String data = """
|
||||
{
|
||||
"id": 1,
|
||||
"data": "\\"sessionID\\":\\"DUYOK9-WEASLEY-20230715114316-0UTMKF\\"||\\"payExchangeInfoHead\\":\\"{\\"guid\\":\\"DUYOK9-WEASLEY-20230715114316-0UTMKF\\",\\"initalRequest\\":\\"<xml><appid><![CDATA[xwxwxwxwx112121]]></appid><mch_id>11111111</mch_id><body><![CDATA[测试测试]]></body><nonce_str><![CDATA[ashdgahgdhaghgliqwueyqu1]]></nonce_str><out_trade_no>202009231454212140210352</out_trade_no><total_fee>500</total_fee><spbill_create_ip><![CDATA[192.168.0.1]]></spbill_create_ip><notify_url><![CDATA[https://www.baidu.com]]></notify_url><openid><![CDATA[adasyduiyasdhjxzycua-Q]]></openid><trade_type><![CDATA[JSAPI]]></trade_type><sign><![CDATA[asd1sa56d4545wqe44wq5]]></sign></xml>\\",\\"initalResponse\\":\\"<xml><appid><![CDATA[xwxwxwxwx112121]]></appid><bank_type><![CDATA[OTHERS]]></bank_type><cash_fee>500</cash_fee><fee_type><![CDATA[CNY]]></fee_type><is_subscribe><![CDATA[N]]></is_subscribe><mch_id>11111111</mch_id><nonce_str><![CDATA[ashdgahgdhaghgliqwueyqu1]]></nonce_str><openid><![CDATA[adasyduiyasdhjxzycua-Q]]></openid><out_trade_no>202009231454212140210352</out_trade_no><result_code><![CDATA[SUCCESS]]></result_code><return_code><![CDATA[SUCCESS]]></return_code><sign><![CDATA[asgdhasgdhasgdhgasgdhasgdh]]></sign><time_end>20200923145426</time_end><total_fee>500</total_fee><trade_type><![CDATA[JSAPI]]></trade_type><transaction_id>4200000681202009235085032319</transaction_id></xml>\\",\\"ebpCode\\":\\"46016602EV\\",\\"payCode\\":\\"1537923071\\",\\"payTransactionId\\":\\"4200000681202009235085032319\\",\\"totalAmount\\":5.0,\\"currency\\":\\"142\\",\\"verDept\\":\\"3\\",\\"payType\\":\\"4\\",\\"tradingTime\\":\\"20200923145426\\",\\"note\\":\\"\\"}\\"||\\"payExchangeInfoLists\\":\\"[{\\"orderNo\\":\\"202009231454218421271832\\",\\"goodsInfo\\":[{\\"gname\\":\\"济州花梨精华面膜\\",\\"itemLink\\":\\"http://m.yunjiweidian.com/yunjibuyer/static/vue-buyer/idc/index.html#/detail?itemId=999761&shopId=453\\"}],\\"recpAccount\\":\\"46016602EV\\",\\"recpCode\\":\\"46016602EV\\",\\"recpName\\":\\"海南省荣誉进出口贸易有限公司\\"}]\\"||\\"serviceTime\\":\\"1689392596517\\"";
|
||||
}
|
||||
""";
|
||||
SignRequest request = JSONUtil.toBean(data, SignRequest.class);
|
||||
SignResult result = controller.signature(request).getData();
|
||||
Map<String, Object> argsMap = new LinkedHashMap<>();
|
||||
argsMap.put("inData", request.getData());
|
||||
argsMap.put("signData", result.getSignatureValue());
|
||||
argsMap.put("certDataPEM", result.getX509Certificate());
|
||||
Args args = signHandler.getUkeyResponseArgs(new UkeyRequest("cus-sec_SpcVerifySignData", argsMap));
|
||||
System.err.println("验签结果: " + args.getData().get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("模拟海关XML数据加签推单")
|
||||
void signAndVerifyXMLSignatureAndPush() throws Exception {
|
||||
String xml = """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ceb:CEB311Message guid="CEB311_HNZB_HNFX_20230707223752_010" version="1.0" xmlns:ceb="http://www.chinaport.gov.cn/ceb" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<ceb:Order>
|
||||
<ceb:OrderHead>
|
||||
<ceb:guid>CEB311_HNZB_HNFX_20230707223752_010</ceb:guid>
|
||||
<ceb:appType>1</ceb:appType>
|
||||
<ceb:appTime>20230704181028</ceb:appTime>
|
||||
<ceb:appStatus>2</ceb:appStatus>
|
||||
<ceb:orderType>I</ceb:orderType>
|
||||
<ceb:orderNo>T_C5051511332138160010</ceb:orderNo>
|
||||
<ceb:ebpCode>4601630004</ceb:ebpCode>
|
||||
<ceb:ebpName>海南省荣誉进出口贸易有限公司</ceb:ebpName>
|
||||
<ceb:ebcCode>4601630004</ceb:ebcCode>
|
||||
<ceb:ebcName>海南省荣誉进出口贸易有限公司</ceb:ebcName>
|
||||
<ceb:goodsValue>0.01</ceb:goodsValue>
|
||||
<ceb:freight>0</ceb:freight>
|
||||
<ceb:discount>0</ceb:discount>
|
||||
<ceb:taxTotal>0</ceb:taxTotal>
|
||||
<ceb:acturalPaid>0.01</ceb:acturalPaid>
|
||||
<ceb:currency>142</ceb:currency>
|
||||
<ceb:buyerRegNo>4</ceb:buyerRegNo>
|
||||
<ceb:buyerName>袁晓雨</ceb:buyerName>
|
||||
<ceb:buyerTelephone>13701727375</ceb:buyerTelephone>
|
||||
<ceb:buyerIdType>1</ceb:buyerIdType>
|
||||
<ceb:buyerIdNumber>130435200009241538</ceb:buyerIdNumber>
|
||||
<ceb:consignee>袁晓雨</ceb:consignee>
|
||||
<ceb:consigneeTelephone>13701727375</ceb:consigneeTelephone>
|
||||
<ceb:consigneeAddress>北京北京市东城区</ceb:consigneeAddress>
|
||||
<ceb:note>test</ceb:note>
|
||||
</ceb:OrderHead>
|
||||
<ceb:OrderList>
|
||||
<ceb:gnum>1</ceb:gnum>
|
||||
<ceb:itemNo>1</ceb:itemNo>
|
||||
<ceb:itemName>LANNA兰纳</ceb:itemName>
|
||||
<ceb:gmodel>10片/包</ceb:gmodel>
|
||||
<ceb:itemDescribe></ceb:itemDescribe>
|
||||
<ceb:barCode>1</ceb:barCode>
|
||||
<ceb:unit>011</ceb:unit>
|
||||
<ceb:qty>1</ceb:qty>
|
||||
<ceb:price>1</ceb:price>
|
||||
<ceb:totalPrice>1</ceb:totalPrice>
|
||||
<ceb:currency>142</ceb:currency>
|
||||
<ceb:country>136</ceb:country>
|
||||
<ceb:note>test</ceb:note>
|
||||
</ceb:OrderList>
|
||||
</ceb:Order>
|
||||
<ceb:BaseTransfer>
|
||||
<ceb:copCode>4601630004</ceb:copCode>
|
||||
<ceb:copName>海南省荣誉进出口贸易有限公司</ceb:copName>
|
||||
<ceb:dxpMode>DXP</ceb:dxpMode>
|
||||
<ceb:dxpId>DXPENT0000530815</ceb:dxpId>
|
||||
<ceb:note>test</ceb:note>
|
||||
</ceb:BaseTransfer>
|
||||
</ceb:CEB311Message>
|
||||
""";
|
||||
SignRequest request = new SignRequest(xml);
|
||||
// 组装数据
|
||||
CEB311Message ceb311Message = JAXBUtil.toBean(xml, CEB311Message.class);
|
||||
String guid = GUIDUtil.getGuid();
|
||||
assert ceb311Message != null;
|
||||
ceb311Message.setGuid(guid);
|
||||
ceb311Message.getOrder().getOrderHead().setGuid(guid);
|
||||
chinaEportReportClient.buildBaseTransfer(ceb311Message.getBaseTransfer());
|
||||
MessageRequest messageRequest = chinaEportReportClient.buildMessageRequest(ceb311Message, MessageType.CEB311Message);
|
||||
/*
|
||||
String requestServer = StringUtils.defaultIfBlank(chinaEportProperties.getServer(), Base64.decodeStr(EPORT_CEBMESSAGE_SERVER_ENCODE));
|
||||
String requestBody = JSONUtil.toJsonStr(messageRequest);
|
||||
log.info("数据上报海关请求入参 {}\nRequest Server: {}", requestBody, requestServer);
|
||||
HttpResponse httpResponse = HttpUtil.createPost(requestServer + "/cebcmsg")
|
||||
.contentType(ContentType.JSON.getValue())
|
||||
.body(requestBody)
|
||||
.execute();
|
||||
log.info("开始上报,Http响应结果: {}", httpResponse.body());
|
||||
*/
|
||||
cebMessageHttpClient.reportCebMessage(messageRequest).subscribe(data -> {
|
||||
log.info("开始申报数据,Http响应结果: {}", data);
|
||||
});
|
||||
|
||||
// 睡8秒,开始查询回执
|
||||
LocalDateTime uploadTime = LocalDateTime.now();
|
||||
int sleepTime = 15;
|
||||
for (int i = sleepTime; i > 0; i--) {
|
||||
log.warn("开始查询推送回执,海关后台正在处理中,线程挂起剩余 {} 秒。", i);
|
||||
TimeUnit.SECONDS.sleep(1);
|
||||
}
|
||||
String url = "http://" + Base64.decodeStr("MzYuMTAxLjIwOC4yMzA=") + ":8090/ceb312msg?" + HttpUtil.toParams(new LinkedHashMap<>() {{
|
||||
put("dxpid", chinaEportProperties.getDxpId());
|
||||
put("qryid", DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(uploadTime));
|
||||
}});
|
||||
HttpResponse receiptResponse = HttpUtil.createGet(url).execute();
|
||||
log.info("\n{}\n回执结果: {}", url, receiptResponse.body()); //回执结果: [{org_id=1, instm=1690556389000, re_order_guid=AOF0YM-WEASLEY-20230728225932-6QVICD, returnstatus=-301014, ebpcode=4601630004, returntime2=20230728225949, ebccode=4601630004, trans_dxpid=DXPENT0000530815, returninfo=订单报文处理失败,接收报文为旧报文数据,不做处理;, returntime=1690556377000, orderno=T_C5051511332138160010}]
|
||||
}
|
||||
}
|
||||
-109
@@ -1,109 +0,0 @@
|
||||
package cn.alphahub.eport.signature.core;
|
||||
|
||||
import cn.alphahub.eport.signature.entity.SignRequest;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
|
||||
@SpringBootTest
|
||||
class SignHandlerTest {
|
||||
|
||||
@Resource
|
||||
SignHandler signHandler;
|
||||
|
||||
@Resource
|
||||
CertificateHandler certificateHandler;
|
||||
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void dynamicSignDataParameterTest() {
|
||||
String xml = """
|
||||
<ceb:CEB621Message xmlns:ceb="http://www.chinaport.gov.cn/ceb" guid="CEB621_HNZB_FXJK_20220208175054_0034"
|
||||
version="v1.0">
|
||||
<ceb:Inventory>
|
||||
<ceb:InventoryHead>
|
||||
<ceb:guid>CEB621_HNZB_FXJK_20220208175055_0035</ceb:guid>
|
||||
<ceb:appType>1</ceb:appType>
|
||||
<ceb:appTime>20220208175055</ceb:appTime>
|
||||
<ceb:appStatus>2</ceb:appStatus>
|
||||
<ceb:orderNo>48037605991776256</ceb:orderNo>
|
||||
<ceb:ebpCode>46121601BC</ceb:ebpCode>
|
||||
<ceb:ebpName>海南星创互联网医药有限公司</ceb:ebpName>
|
||||
<ceb:ebcCode>46121601BC</ceb:ebcCode>
|
||||
<ceb:ebcName>海南星创互联网医药有限公司</ceb:ebcName>
|
||||
<ceb:logisticsNo>2222222</ceb:logisticsNo>
|
||||
<ceb:logisticsCode>4101986180</ceb:logisticsCode>
|
||||
<ceb:logisticsName>圆通</ceb:logisticsName>
|
||||
<ceb:copNo>4101W68006</ceb:copNo>
|
||||
<ceb:assureCode>4101960AGP</ceb:assureCode>
|
||||
<ceb:ieFlag>I</ceb:ieFlag>
|
||||
<ceb:declTime>20220208</ceb:declTime>
|
||||
<ceb:customsCode>4601</ceb:customsCode>
|
||||
<ceb:portCode>4601</ceb:portCode>
|
||||
<ceb:ieDate>20220208</ceb:ieDate>
|
||||
<ceb:buyerIdType>1</ceb:buyerIdType>
|
||||
<ceb:buyerIdNumber>612326199005306719</ceb:buyerIdNumber>
|
||||
<ceb:buyerName>用户_15150368</ceb:buyerName>
|
||||
<ceb:buyerTelephone>18209182500</ceb:buyerTelephone>
|
||||
<ceb:consigneeAddress>哈布斯堡</ceb:consigneeAddress>
|
||||
<ceb:agentCode>46121601BC</ceb:agentCode>
|
||||
<ceb:agentName>海南星创互联网医药有限公司</ceb:agentName>
|
||||
<ceb:tradeMode>9610</ceb:tradeMode>
|
||||
<ceb:trafMode>W</ceb:trafMode>
|
||||
<ceb:country>142</ceb:country>
|
||||
<ceb:freight>0.00</ceb:freight>
|
||||
<ceb:insuredFee>0</ceb:insuredFee>
|
||||
<ceb:currency>142</ceb:currency>
|
||||
<ceb:packNo>1</ceb:packNo>
|
||||
<ceb:grossWeight>0.07</ceb:grossWeight>
|
||||
<ceb:netWeight>0.06</ceb:netWeight>
|
||||
</ceb:InventoryHead>
|
||||
<ceb:InventoryList>
|
||||
<ceb:gnum>1</ceb:gnum>
|
||||
<ceb:itemRecordNo>0797776159482</ceb:itemRecordNo>
|
||||
<ceb:itemNo>HNGGKJ00000057</ceb:itemNo>
|
||||
<ceb:itemName>通用铁剂胶囊</ceb:itemName>
|
||||
<ceb:gcode>2106909090</ceb:gcode>
|
||||
<ceb:gname>ACTIVE IRON</ceb:gname>
|
||||
<ceb:gmodel>4|3|乳清蛋白(牛奶)65%,胶囊壳(羟苯基甲基纤维素)21%,硫酸亚铁1.2%,酸度调节剂2%,填充剂 10.8%|30粒/盒|无中文名称/ACTIVE IRON</ceb:gmodel>
|
||||
<ceb:country>306</ceb:country>
|
||||
<ceb:currency>142</ceb:currency>
|
||||
<ceb:qty>1</ceb:qty>
|
||||
<ceb:unit>140</ceb:unit>
|
||||
<ceb:qty1>0.06</ceb:qty1>
|
||||
<ceb:unit1>035</ceb:unit1>
|
||||
<ceb:price>0.01</ceb:price>
|
||||
<ceb:totalPrice>0.01</ceb:totalPrice>
|
||||
</ceb:InventoryList>
|
||||
</ceb:Inventory>
|
||||
<ceb:BaseTransfer>
|
||||
<ceb:copCode>46121601BC</ceb:copCode>
|
||||
<ceb:copName>海南星创互联网医药有限公司</ceb:copName>
|
||||
<ceb:dxpMode>DXP</ceb:dxpMode>
|
||||
<ceb:dxpId>DXPENT0000458763</ceb:dxpId>
|
||||
</ceb:BaseTransfer>
|
||||
</ceb:CEB621Message>
|
||||
""";
|
||||
String dynamicSignDataParameter = signHandler.getSignDataParameter(new SignRequest(xml));
|
||||
String method = JSONUtil.parseObj(dynamicSignDataParameter).getStr("_method");
|
||||
String x509Certificate = certificateHandler.getX509Certificate(method);
|
||||
System.err.println("\t\t\t\t" + x509Certificate + "\n");
|
||||
|
||||
dynamicSignDataParameter = signHandler.getSignDataParameter(new SignRequest(xml));
|
||||
method = JSONUtil.parseObj(dynamicSignDataParameter).getStr("_method");
|
||||
x509Certificate = certificateHandler.getX509Certificate(method);
|
||||
System.err.println("\t\t\t\t" + x509Certificate);
|
||||
}
|
||||
}
|
||||
-104
@@ -1,104 +0,0 @@
|
||||
package cn.alphahub.eport.signature.core;
|
||||
|
||||
import cn.alphahub.eport.signature.entity.UkeyResponse;
|
||||
import cn.hutool.core.lang.TypeReference;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
@SpringBootTest
|
||||
class WebSocketClientHandlerTest {
|
||||
|
||||
@Autowired
|
||||
WebSocketClientHandler handler;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("复位读卡器失败发送邮件通知")
|
||||
void handleTextMessage() {
|
||||
String ukeyErrJson = """
|
||||
{
|
||||
"_id": 1,
|
||||
"_method": "cus-sec_SpcSignDataAsPEM",
|
||||
"_status": "00",
|
||||
"_args": {
|
||||
"Result": false,
|
||||
"Data": [],
|
||||
"Error": [
|
||||
"[读卡器底层库]复位读卡器失败:错误码\\u003d50070",
|
||||
"Err:Custom50070"
|
||||
]
|
||||
}
|
||||
}
|
||||
""";
|
||||
String messagePayload = """
|
||||
<ceb:CEB311Message guid="YGD8P9-WEASLEY-20230805155746-HOUDF2" version="1.0" xmlns:ceb="http://www.chinaport.gov.cn/ceb" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" >
|
||||
<ceb:Order>
|
||||
<ceb:OrderHead>
|
||||
<ceb:guid>YGD8P9-WEASLEY-20230805155746-HOUDF2</ceb:guid>
|
||||
<ceb:appType>1</ceb:appType>
|
||||
<ceb:appTime>20230704181028</ceb:appTime>
|
||||
<ceb:appStatus>2</ceb:appStatus>
|
||||
<ceb:orderType>I</ceb:orderType>
|
||||
<ceb:orderNo>T_C5051511332138160010</ceb:orderNo>
|
||||
<ceb:ebpCode>4601630004</ceb:ebpCode>
|
||||
<ceb:ebpName>海南省荣誉进出口贸易有限公司</ceb:ebpName>
|
||||
<ceb:ebcCode>4601630004</ceb:ebcCode>
|
||||
<ceb:ebcName>海南省荣誉进出口贸易有限公司</ceb:ebcName>
|
||||
<ceb:goodsValue>0.01</ceb:goodsValue>
|
||||
<ceb:freight>0</ceb:freight>
|
||||
<ceb:discount>0</ceb:discount>
|
||||
<ceb:taxTotal>0</ceb:taxTotal>
|
||||
<ceb:acturalPaid>0.01</ceb:acturalPaid>
|
||||
<ceb:currency>142</ceb:currency>
|
||||
<ceb:buyerRegNo>4</ceb:buyerRegNo>
|
||||
<ceb:buyerName>袁晓雨</ceb:buyerName>
|
||||
<ceb:buyerTelephone>13701727375</ceb:buyerTelephone>
|
||||
<ceb:buyerIdType>1</ceb:buyerIdType>
|
||||
<ceb:buyerIdNumber>130435200009241538</ceb:buyerIdNumber>
|
||||
<ceb:consignee>袁晓雨</ceb:consignee>
|
||||
<ceb:consigneeTelephone>13701727375</ceb:consigneeTelephone>
|
||||
<ceb:consigneeAddress>北京北京市东城区</ceb:consigneeAddress>
|
||||
<ceb:note>test</ceb:note>
|
||||
</ceb:OrderHead>
|
||||
<ceb:OrderList>
|
||||
<ceb:gnum>1</ceb:gnum>
|
||||
<ceb:itemNo>1</ceb:itemNo>
|
||||
<ceb:itemName>LANNA兰纳</ceb:itemName>
|
||||
<ceb:gmodel>10片/包</ceb:gmodel>
|
||||
<ceb:itemDescribe></ceb:itemDescribe>
|
||||
<ceb:barCode>1</ceb:barCode>
|
||||
<ceb:unit>011</ceb:unit>
|
||||
<ceb:qty>1</ceb:qty>
|
||||
<ceb:price>1</ceb:price>
|
||||
<ceb:totalPrice>1</ceb:totalPrice>
|
||||
<ceb:currency>142</ceb:currency>
|
||||
<ceb:country>136</ceb:country>
|
||||
<ceb:note>test</ceb:note>
|
||||
</ceb:OrderList>
|
||||
</ceb:Order>
|
||||
<ceb:BaseTransfer>
|
||||
<ceb:copCode>4601630004</ceb:copCode>
|
||||
<ceb:copName>海南省荣誉进出口贸易有限公司</ceb:copName>
|
||||
<ceb:dxpMode>DXP</ceb:dxpMode>
|
||||
<ceb:dxpId>DXPENT0000530815</ceb:dxpId>
|
||||
<ceb:note>test</ceb:note>
|
||||
</ceb:BaseTransfer>
|
||||
</ceb:CEB311Message>
|
||||
""";
|
||||
UkeyResponse response = JSONUtil.toBean(ukeyErrJson, new TypeReference<>() {
|
||||
}, true);
|
||||
handler.sendAlertSignFailure(response, messagePayload);
|
||||
}
|
||||
}
|
||||
-99
@@ -1,99 +0,0 @@
|
||||
package cn.alphahub.eport.signature.core.web;
|
||||
|
||||
import cn.alphahub.eport.signature.core.SignHandler;
|
||||
import cn.alphahub.eport.signature.entity.SignRequest;
|
||||
import cn.alphahub.eport.signature.entity.SignResult;
|
||||
import cn.alphahub.eport.signature.entity.UkeyRequest;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import io.github.weasleyj.china.eport.sign.model.customs179.Customs179Request;
|
||||
import io.github.weasleyj.china.eport.sign.util.GUIDUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static cn.alphahub.dtt.plus.util.JacksonUtil.toJson;
|
||||
import static cn.alphahub.eport.signature.core.CertificateHandler.SING_DATA_METHOD;
|
||||
|
||||
@Slf4j
|
||||
@SpringBootTest
|
||||
class EportCustoms179HttpClientTest {
|
||||
|
||||
@Autowired
|
||||
SignHandler signHandler;
|
||||
@Autowired
|
||||
EportCustoms179HttpClient eportCustoms179HttpClient;
|
||||
|
||||
@Test
|
||||
void report179Data() {
|
||||
String json = """
|
||||
{
|
||||
"sessionID": "fe2374-8fnejf97-32839218",
|
||||
"payExchangeInfoHead": {
|
||||
"guid": "fe2374-8fnejf97-32839218",
|
||||
"initalRequest": "<xml><appid>wxa3412c979c7c6fb7</appid><mch_id>1537923071</mch_id><nonce_str>20220214148382001542606848</nonce_str><sign>38198AD6D22E59B3CCC683D67D8BB402</sign><sign_type>MD5</sign_type><body>测试订单编号411312450000027161</body><attach><![CDATA[16dbeaa5-92d8-47f5-a0a8-813a4d45a5f9]]></attach><out_trade_no>20220214148382001542606848</out_trade_no><total_fee>1</total_fee><spbill_create_ip>10.0.12.208</spbill_create_ip><notify_url>https://boss-dev.fengyouhui.net/be/api/mall-payment-c/api/public/gateway/payment/notice/wx/pay/20220214148382001542606848</notify_url><trade_type>MWEB</trade_type><openid></openid></xml>",
|
||||
"initalResponse": "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg><result_code><![CDATA[SUCCESS]]></result_code><mch_id><![CDATA[1537923071]]></mch_id><appid><![CDATA[wxa3412c979c7c6fb7]]></appid><nonce_str><![CDATA[6SgxJ98xsr1nUnRG]]></nonce_str><sign><![CDATA[EB632B9C9F8BCB8027ACE9195878EE4F]]></sign><prepay_id><![CDATA[wx14105706563206ab24a00689936c800000]]></prepay_id><trade_type><![CDATA[MWEB]]></trade_type><mweb_url><![CDATA[https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?prepay_id=wx14105706563206ab24a00689936c800000&package=2518465012]]></mweb_url></xml>",
|
||||
"ebpCode": "请替换",
|
||||
"payCode": "1537923071",
|
||||
"payTransactionId": "20220214148382001542606848",
|
||||
"totalAmount": 0.01,
|
||||
"currency": "502",
|
||||
"verDept": "3",
|
||||
"payType": "1",
|
||||
"tradingTime": "20220214105714",
|
||||
"note": "备注"
|
||||
},
|
||||
"payExchangeInfoLists": [
|
||||
{
|
||||
"orderNo": "202009231454218421271832",
|
||||
"goodsInfo": [
|
||||
{
|
||||
"gname": "济州花梨精华面膜",
|
||||
"itemLink": "http://m.yunjiweidian.com/yunjibuyer/static/vue-buyer/idc/index.html#/detail?itemId=999761&shopId=453"
|
||||
}
|
||||
],
|
||||
"recpAccount": "请替换",
|
||||
"recpCode": "请替换",
|
||||
"recpName": "请替换"
|
||||
}
|
||||
],
|
||||
"serviceTime": "1533282603450"
|
||||
}
|
||||
""";
|
||||
Customs179Request customs179Request = JSONUtil.toBean(json, Customs179Request.class);
|
||||
|
||||
String guid = GUIDUtil.getGuid();
|
||||
customs179Request.setSessionID(guid);
|
||||
customs179Request.setServiceTime(String.valueOf(System.currentTimeMillis()));
|
||||
customs179Request.getPayExchangeInfoHead().setGuid(guid);
|
||||
|
||||
// 加签
|
||||
String dataInfo179 = "\"sessionID\":\"" + customs179Request.getSessionID() + "\"||" +
|
||||
"\"payExchangeInfoHead\":\"" + toJson(customs179Request.getPayExchangeInfoHead()) + "\"||" +
|
||||
"\"payExchangeInfoLists\":\"" + toJson(customs179Request.getPayExchangeInfoLists()) + "\"||" +
|
||||
"\"serviceTime\":" + "\"" + customs179Request.getServiceTime() + "\"";
|
||||
|
||||
UkeyRequest ukeyRequest = new UkeyRequest(SING_DATA_METHOD, new LinkedHashMap<>() {{
|
||||
put("inData", dataInfo179);
|
||||
put("passwd", "88888888");
|
||||
}});
|
||||
String signParams = toJson(ukeyRequest);
|
||||
SignRequest request = new SignRequest(dataInfo179);
|
||||
SignResult result = signHandler.sign(request, signParams);
|
||||
|
||||
customs179Request.setCertNo(result.getCertNo());
|
||||
customs179Request.setSignValue(result.getSignatureValue());
|
||||
|
||||
Map<String, Object> paramMap = new LinkedHashMap<>();
|
||||
paramMap.put("payExInfoStr", toJson(customs179Request));
|
||||
log.info("begin report,request data is :{}", toJson(paramMap));
|
||||
eportCustoms179HttpClient.report179Data(paramMap).subscribe(capture179DataResponse -> {
|
||||
Assertions.assertNotNull(capture179DataResponse, "海关 179 数据返回不为空");
|
||||
System.out.println(JSONUtil.toJsonPrettyStr(capture179DataResponse));
|
||||
});
|
||||
}
|
||||
}
|
||||
-37
@@ -1,37 +0,0 @@
|
||||
package cn.alphahub.eport.signature.core.web;
|
||||
|
||||
import cn.alphahub.eport.signature.config.ChinaEportProperties;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
@Slf4j
|
||||
@SpringBootTest
|
||||
class EportReportResultHttpClientTest {
|
||||
|
||||
@Autowired
|
||||
ChinaEportProperties chinaEportProperties;
|
||||
@Autowired
|
||||
EportReportResultHttpClient eportReportResultHttpClient;
|
||||
|
||||
@Test
|
||||
@DisplayName("查询 311 申报回执")
|
||||
void getCeb312msg() {
|
||||
eportReportResultHttpClient.getCeb312msgResult(chinaEportProperties.getDxpId(), "20230806121200").subscribe(data -> {
|
||||
log.info("311回执结果:{}", data);
|
||||
Assertions.assertNotNull(data, "311回执结果不为空");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("查询 621 申报回执")
|
||||
void getCe622msg() {
|
||||
eportReportResultHttpClient.getCe622msgResult(chinaEportProperties.getDxpId(), "20230806121200").subscribe(data -> {
|
||||
log.info("621回执结果:{}", data);
|
||||
Assertions.assertNotNull(data, "621回执结果不为空");
|
||||
});
|
||||
}
|
||||
}
|
||||
-47
@@ -1,47 +0,0 @@
|
||||
package cn.alphahub.eport.signature.interview;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.locks.LockSupport;
|
||||
|
||||
public class ExchangeOddEven {
|
||||
|
||||
final static AtomicInteger atomicInteger = new AtomicInteger(0);
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
Print print1 = new Print();
|
||||
Print print2 = new Print();
|
||||
|
||||
Thread t1 = new Thread(print1);
|
||||
Thread t2 = new Thread(print2);
|
||||
|
||||
print1.setCurrentThread(t2);
|
||||
print2.setCurrentThread(t1);
|
||||
|
||||
t1.start();
|
||||
t2.start();
|
||||
|
||||
LockSupport.unpark(t1);
|
||||
}
|
||||
|
||||
static class Print implements Runnable {
|
||||
private volatile Thread currentThread;
|
||||
|
||||
public void setCurrentThread(Thread currentThread) {
|
||||
this.currentThread = currentThread;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
for (; ; ) {
|
||||
LockSupport.park();
|
||||
if (atomicInteger.get() > 1000) {
|
||||
LockSupport.unpark(currentThread);
|
||||
return;
|
||||
}
|
||||
System.out.println(Thread.currentThread().getName() + ":" + atomicInteger.getAndIncrement());
|
||||
LockSupport.unpark(currentThread);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,9 +10,9 @@
|
||||
</parent>
|
||||
|
||||
<groupId>cn.alphahub.eport.signature</groupId>
|
||||
<artifactId>chinaport-data-signature-app</artifactId>
|
||||
<artifactId>chinaport-data-signature-webapp</artifactId>
|
||||
<version>1.1.1</version>
|
||||
<name>chinaport-data-signature-app</name>
|
||||
<name>chinaport-data-signature-webapp</name>
|
||||
<url>https://github.com/Weasley-J/chinaport-data-signature.git</url>
|
||||
|
||||
<description>China E-Port data signature, 中国电子口岸报文加签推送应用,2022年2月11日开源此解决方案</description>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user