初始化提交源代码.

This commit is contained in:
julian
2022-02-14 00:36:21 +08:00
parent e6e0a0d9c9
commit e75122e468
50 changed files with 8640 additions and 5 deletions
+1
View File
@@ -58,3 +58,4 @@ hs_err_pid*
#MAC
.DS_Store
/README.assets/
+2
View File
@@ -0,0 +1,2 @@
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.4/apache-maven-3.8.4-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar
+26
View File
@@ -0,0 +1,26 @@
# Getting Started
### Reference Documentation
For further reference, please consider the following sections:
* [Official Apache Maven documentation](https://maven.apache.org/guides/index.html)
* [Spring Boot Maven Plugin Reference Guide](https://docs.spring.io/spring-boot/docs/3.0.0-M1/maven-plugin/reference/html/)
* [Create an OCI image](https://docs.spring.io/spring-boot/docs/3.0.0-M1/maven-plugin/reference/html/#build-image)
* [Spring Web](https://docs.spring.io/spring-boot/docs/2.6.3/reference/htmlsingle/#boot-features-developing-web-applications)
* [Cloud Bootstrap](https://spring.io/projects/spring-cloud-commons)
* [Validation](https://docs.spring.io/spring-boot/docs/2.6.3/reference/htmlsingle/#boot-features-validation)
* [Spring Configuration Processor](https://docs.spring.io/spring-boot/docs/2.6.3/reference/htmlsingle/#configuration-metadata-annotation-processor)
* [WebSocket](https://docs.spring.io/spring-boot/docs/2.6.3/reference/htmlsingle/#boot-features-websockets)
* [Cloud LoadBalancer](https://docs.spring.io/spring-cloud-commons/docs/current/reference/html/#spring-cloud-loadbalancer)
### Guides
The following guides illustrate how to use some features concretely:
* [Building a RESTful Web Service](https://spring.io/guides/gs/rest-service/)
* [Serving Web Content with Spring MVC](https://spring.io/guides/gs/serving-web-content/)
* [Building REST services with Spring](https://spring.io/guides/tutorials/bookmarks/)
* [Using WebSocket to build an interactive web application](https://spring.io/guides/gs/messaging-stomp-websocket/)
* [Client-side load-balancing with Spring Cloud LoadBalancer](https://spring.io/guides/gs/spring-cloud-loadbalancer/)
+72 -5
View File
@@ -1,6 +1,6 @@
# 中国电子口岸CEBXXXMessage进口单报文加签
> China E-Port data signature,中国电子口岸(海口海关CEBXxxMessage)末三段加签服务,开箱即用,无任何中间件。本项目遵守GNU 3.0协议。本项目里面所有到的x509证书不具有任何真实性、合法性,企业根据自己的情况替换,ukey加签直接直接下载release运行修改参数运行既可。
> China E-Port data signature,中国电子口岸(海口海关CEBXxxMessage)末三段加签服务,开箱即用,无任何中间件。本项目遵守`GNU 3.0`协议。本项目里面所有到的`x509`证书不具有任何真实性、合法性,企业根据自己的情况替换成自己真实的证书,`ukey`加签直接直接下载`release`运行修改参数运行既可。不需要导出`.cer`证书
@@ -16,20 +16,87 @@ todo
todo
## 3 启动脚本介绍
本人已提供好`Windows`环境、`Linux`/`MacOS`的环境下的脚本,企业只需要修改**2**个参数既可**开箱即用**。
启动文件相对的位置:
![image-20220214002812928](https://alphahub-test-bucket.oss-cn-shanghai.aliyuncs.com/image/image-20220214002812928.png)
#### 3.1 Windows环境
> 运行方式:双击打开
```bat
@ECHO OFF
SETLOCAL EnableDelayedExpansion
REM ------ 全局参数配置 ------
REM 以下3个参数: .cer证书没方法jar包里面可以不用
REM APP_NAME: jar包名称
REM UKEY_CERT_PATH: .cer证书在classpath下的相对路径,直接下载release运行的同学不需要配置
REM UKEY_HOST: 中国电子口岸操作员u-key所连接的Windows PC的主机ip
REM UKEY_PASSWORD: 中国电子口岸操作员u-key密码,默认: 88888888
#### 3.2 Linux/MacOS环境
set "APP_NAME=chinaport-data-signature-v1.0.0"
set "UKEY_CERT_PATH=cert/01691fe9.cer"
set "UKEY_HOST=192.168.31.108"
set "UKEY_PASSWORD=88888888"
REM ------ 全局参数配置 ------
title %APP_NAME%
PUSHD %~DP0 & cd /d "%~dp0"
%1 %2
mshta vbscript:createobject("shell.application").shellexecute("%~s0","goto :runas","","runas",1)(window.close)&goto :eof
:runas
set "APP=%CD%\%APP_NAME%.jar"
set "JAVA=%CD%\jre\bin\java.exe"
set "JVM_ARGS=-Dfile.encoding=GBK -Xms512m -Xmx512m"
%JAVA% %JVM_ARGS% -jar %APP% ^
--server.port=8080 ^
--eport.signature.ukey.cert-path=%UKEY_CERT_PATH% ^
--eport.signature.ukey.ws-url=ws://%UKEY_HOST%:61232 ^
--eport.signature.ukey.password=%UKEY_PASSWORD%
echo 按任意键退出.
pause >nul
exit
```
#### 3.2 Linux/MacOS环境
> 运行方式:终端执行`sh start.sh`启动程序
```bash
#!/bin/bash
# ------ 全局参数配置 ------
# 参数说明:
# APP_NAME: jar包名称
# UKEY_CERT_PATH: .cer证书在classpath下的相对路径,直接下载release运行的同学不需要配置
# UKEY_HOST: 中国电子口岸操作员u-key所连接的Windows PC的主机ip
# UKEY_PASSWORD: 中国电子口岸操作员u-key密码,默认: 88888888
APP_NAME="chinaport-data-signature-v1.0.0"
UKEY_CERT_PATH="cert/01691fe9.cer"
UKEY_HOST="192.168.31.108"
UKEY_PASSWORD="88888888"
# ------ 全局参数配置 ------
APP="./${APP_NAME}.jar"
JVM_ARGS="-Xms512m -Xmx512m"
java ${JVM_ARGS} -jar ${APP} \
--server.port=8080 \
--eport.signature.ukey.cert-path=${UKEY_CERT_PATH} \
--eport.signature.ukey.ws-url=ws://${UKEY_HOST}:61232 \
--eport.signature.ukey.password=${UKEY_PASSWORD}
```
Vendored Executable
+331
View File
@@ -0,0 +1,331 @@
#!/bin/sh
# ----------------------------------------------------------------------------
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
# Maven Start Up Batch script
#
# Required ENV vars:
# ------------------
# JAVA_HOME - location of a JDK home dir
#
# Optional ENV vars
# -----------------
# M2_HOME - location of maven2's installed home dir
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
# e.g. to debug Maven itself, use
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
# ----------------------------------------------------------------------------
if [ -z "$MAVEN_SKIP_RC" ]; then
if [ -f /usr/local/etc/mavenrc ]; then
. /usr/local/etc/mavenrc
fi
if [ -f /etc/mavenrc ]; then
. /etc/mavenrc
fi
if [ -f "$HOME/.mavenrc" ]; then
. "$HOME/.mavenrc"
fi
fi
# OS specific support. $var _must_ be set to either true or false.
cygwin=false
darwin=false
mingw=false
case "$(uname)" in
CYGWIN*) cygwin=true ;;
MINGW*) mingw=true ;;
Darwin*)
darwin=true
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html
if [ -z "$JAVA_HOME" ]; then
if [ -x "/usr/libexec/java_home" ]; then
export JAVA_HOME="$(/usr/libexec/java_home)"
else
export JAVA_HOME="/Library/Java/Home"
fi
fi
;;
esac
if [ -z "$JAVA_HOME" ]; then
if [ -r /etc/gentoo-release ]; then
JAVA_HOME=$(java-config --jre-home)
fi
fi
if [ -z "$M2_HOME" ]; then
## resolve links - $0 may be a link to maven's home
PRG="$0"
# need this for relative symlinks
while [ -h "$PRG" ]; do
ls=$(ls -ld "$PRG")
link=$(expr "$ls" : '.*-> \(.*\)$')
if expr "$link" : '/.*' >/dev/null; then
PRG="$link"
else
PRG="$(dirname "$PRG")/$link"
fi
done
saveddir=$(pwd)
M2_HOME=$(dirname "$PRG")/..
# make it fully qualified
M2_HOME=$(cd "$M2_HOME" && pwd)
cd "$saveddir"
# echo Using m2 at $M2_HOME
fi
# For Cygwin, ensure paths are in UNIX format before anything is touched
if $cygwin; then
[ -n "$M2_HOME" ] &&
M2_HOME=$(cygpath --unix "$M2_HOME")
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=$(cygpath --unix "$JAVA_HOME")
[ -n "$CLASSPATH" ] &&
CLASSPATH=$(cygpath --path --unix "$CLASSPATH")
fi
# For Mingw, ensure paths are in UNIX format before anything is touched
if $mingw; then
[ -n "$M2_HOME" ] &&
M2_HOME="$( (
cd "$M2_HOME"
pwd
))"
[ -n "$JAVA_HOME" ] &&
JAVA_HOME="$( (
cd "$JAVA_HOME"
pwd
))"
fi
if [ -z "$JAVA_HOME" ]; then
javaExecutable="$(which javac)"
if [ -n "$javaExecutable" ] && ! [ "$(expr \"$javaExecutable\" : '\([^ ]*\)')" = "no" ]; then
# readlink(1) is not available as standard on Solaris 10.
readLink=$(which readlink)
if [ ! $(expr "$readLink" : '\([^ ]*\)') = "no" ]; then
if $darwin; then
javaHome="$(dirname \"$javaExecutable\")"
javaExecutable="$(cd \"$javaHome\" && pwd -P)/javac"
else
javaExecutable="$(readlink -f \"$javaExecutable\")"
fi
javaHome="$(dirname \"$javaExecutable\")"
javaHome=$(expr "$javaHome" : '\(.*\)/bin')
JAVA_HOME="$javaHome"
export JAVA_HOME
fi
fi
fi
if [ -z "$JAVACMD" ]; then
if [ -n "$JAVA_HOME" ]; then
if [ -x "$JAVA_HOME/jre/sh/java" ]; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
else
JAVACMD="$(
\unset -f command
\command -v java
)"
fi
fi
if [ ! -x "$JAVACMD" ]; then
echo "Error: JAVA_HOME is not defined correctly." >&2
echo " We cannot execute $JAVACMD" >&2
exit 1
fi
if [ -z "$JAVA_HOME" ]; then
echo "Warning: JAVA_HOME environment variable is not set."
fi
CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
# traverses directory structure from process work directory to filesystem root
# first directory with .mvn subdirectory is considered project base directory
find_maven_basedir() {
if [ -z "$1" ]; then
echo "Path not specified to find_maven_basedir"
return 1
fi
basedir="$1"
wdir="$1"
while [ "$wdir" != '/' ]; do
if [ -d "$wdir"/.mvn ]; then
basedir=$wdir
break
fi
# workaround for JBEAP-8937 (on Solaris 10/Sparc)
if [ -d "${wdir}" ]; then
wdir=$(
cd "$wdir/.."
pwd
)
fi
# end of workaround
done
echo "${basedir}"
}
# concatenates all lines of a file
concat_lines() {
if [ -f "$1" ]; then
echo "$(tr -s '\n' ' ' <"$1")"
fi
}
BASE_DIR=$(find_maven_basedir "$(pwd)")
if [ -z "$BASE_DIR" ]; then
exit 1
fi
##########################################################################################
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
# This allows using the maven wrapper in projects that prohibit checking in binary data.
##########################################################################################
if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found .mvn/wrapper/maven-wrapper.jar"
fi
else
if [ "$MVNW_VERBOSE" = true ]; then
echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
fi
if [ -n "$MVNW_REPOURL" ]; then
jarUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
else
jarUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
fi
while IFS="=" read key value; do
case "$key" in wrapperUrl)
jarUrl="$value"
break
;;
esac
done <"$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
if [ "$MVNW_VERBOSE" = true ]; then
echo "Downloading from: $jarUrl"
fi
wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
if $cygwin; then
wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath")
fi
if command -v wget >/dev/null; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found wget ... using wget"
fi
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
wget "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
else
wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
fi
elif command -v curl >/dev/null; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found curl ... using curl"
fi
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
curl -o "$wrapperJarPath" "$jarUrl" -f
else
curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
fi
else
if [ "$MVNW_VERBOSE" = true ]; then
echo "Falling back to using Java to download"
fi
javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
# For Cygwin, switch paths to Windows format before running javac
if $cygwin; then
javaClass=$(cygpath --path --windows "$javaClass")
fi
if [ -e "$javaClass" ]; then
if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
if [ "$MVNW_VERBOSE" = true ]; then
echo " - Compiling MavenWrapperDownloader.java ..."
fi
# Compiling the Java class
("$JAVA_HOME/bin/javac" "$javaClass")
fi
if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
# Running the downloader
if [ "$MVNW_VERBOSE" = true ]; then
echo " - Running MavenWrapperDownloader.java ..."
fi
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
fi
fi
fi
fi
##########################################################################################
# End of extension
##########################################################################################
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
if [ "$MVNW_VERBOSE" = true ]; then
echo $MAVEN_PROJECTBASEDIR
fi
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
# For Cygwin, switch paths to Windows format before running java
if $cygwin; then
[ -n "$M2_HOME" ] &&
M2_HOME=$(cygpath --path --windows "$M2_HOME")
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME")
[ -n "$CLASSPATH" ] &&
CLASSPATH=$(cygpath --path --windows "$CLASSPATH")
[ -n "$MAVEN_PROJECTBASEDIR" ] &&
MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR")
fi
# Provide a "standardized" way to retrieve the CLI args that will
# work with both Windows and non-Windows executions.
MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
export MAVEN_CMD_LINE_ARGS
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
exec "$JAVACMD" \
$MAVEN_OPTS \
$MAVEN_DEBUG_OPTS \
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
"-Dmaven.home=${M2_HOME}" \
"-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
Vendored
+188
View File
@@ -0,0 +1,188 @@
@REM ----------------------------------------------------------------------------
@REM Licensed to the Apache Software Foundation (ASF) under one
@REM or more contributor license agreements. See the NOTICE file
@REM distributed with this work for additional information
@REM regarding copyright ownership. The ASF licenses this file
@REM to you under the Apache License, Version 2.0 (the
@REM "License"); you may not use this file except in compliance
@REM with the License. You may obtain a copy of the License at
@REM
@REM https://www.apache.org/licenses/LICENSE-2.0
@REM
@REM Unless required by applicable law or agreed to in writing,
@REM software distributed under the License is distributed on an
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@REM KIND, either express or implied. See the License for the
@REM specific language governing permissions and limitations
@REM under the License.
@REM ----------------------------------------------------------------------------
@REM ----------------------------------------------------------------------------
@REM Maven Start Up Batch script
@REM
@REM Required ENV vars:
@REM JAVA_HOME - location of a JDK home dir
@REM
@REM Optional ENV vars
@REM M2_HOME - location of maven2's installed home dir
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
@REM e.g. to debug Maven itself, use
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
@REM ----------------------------------------------------------------------------
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
@echo off
@REM set title of command window
title %0
@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
@REM set %HOME% to equivalent of $HOME
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
@REM Execute a user defined script before this one
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %*
if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %*
:skipRcPre
@setlocal
set ERROR_CODE=0
@REM To isolate internal variables from possible post scripts, we use another setlocal
@setlocal
@REM ==== START VALIDATION ====
if not "%JAVA_HOME%" == "" goto OkJHome
echo.
echo Error: JAVA_HOME not found in your environment. >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
:OkJHome
if exist "%JAVA_HOME%\bin\java.exe" goto init
echo.
echo Error: JAVA_HOME is set to an invalid directory. >&2
echo JAVA_HOME = "%JAVA_HOME%" >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
@REM ==== END VALIDATION ====
:init
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
@REM Fallback to current working directory if not found.
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
set EXEC_DIR=%CD%
set WDIR=%EXEC_DIR%
:findBaseDir
IF EXIST "%WDIR%"\.mvn goto baseDirFound
cd ..
IF "%WDIR%"=="%CD%" goto baseDirNotFound
set WDIR=%CD%
goto findBaseDir
:baseDirFound
set MAVEN_PROJECTBASEDIR=%WDIR%
cd "%EXEC_DIR%"
goto endDetectBaseDir
:baseDirNotFound
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
cd "%EXEC_DIR%"
:endDetectBaseDir
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
@setlocal EnableExtensions EnableDelayedExpansion
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
:endReadAdditionalConfig
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
)
@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
if exist %WRAPPER_JAR% (
if "%MVNW_VERBOSE%" == "true" (
echo Found %WRAPPER_JAR%
)
) else (
if not "%MVNW_REPOURL%" == "" (
SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
)
if "%MVNW_VERBOSE%" == "true" (
echo Couldn't find %WRAPPER_JAR%, downloading it ...
echo Downloading from: %DOWNLOAD_URL%
)
powershell -Command "&{"^
"$webclient = new-object System.Net.WebClient;"^
"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
"}"^
"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
"}"
if "%MVNW_VERBOSE%" == "true" (
echo Finished downloading %WRAPPER_JAR%
)
)
@REM End of extension
@REM Provide a "standardized" way to retrieve the CLI args that will
@REM work with both Windows and non-Windows executions.
set MAVEN_CMD_LINE_ARGS=%*
%MAVEN_JAVA_EXE% ^
%JVM_CONFIG_MAVEN_PROPS% ^
%MAVEN_OPTS% ^
%MAVEN_DEBUG_OPTS% ^
-classpath %WRAPPER_JAR% ^
"-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
%WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
if ERRORLEVEL 1 goto error
goto end
:error
set ERROR_CODE=1
:end
@endlocal & set ERROR_CODE=%ERROR_CODE%
if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost
@REM check for post script, once with legacy .bat ending and once with .cmd ending
if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat"
if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd"
:skipRcPost
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
if "%MAVEN_BATCH_PAUSE%"=="on" pause
if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE%
cmd /C exit /B %ERROR_CODE%
+260
View File
@@ -0,0 +1,260 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.0-M1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>cn.gov.chinaport</groupId>
<artifactId>chinaport-data-signature</artifactId>
<version>1.0.0</version>
<name>chinaport-data-signature</name>
<url>https://github.com/Weasley-J/chinaport-data-signature.git</url>
<description>
China E-Port data signature,中国电子口岸(海口海关CEBXxxMessage)加签服务,写这个项目的时间是2022-02-11,
在这之前由于海关官方关于数据加签的资料贫瘠,导致对接很不顺利,我司和海关对接,海关不到那不提供数据加签方法,还沟通成本增加;
这里有人反驳我了:试问海关的加签资料是给人看的吗?这里便让付费的加签在开源社区共享,所有人都能免费使用。
</description>
<properties>
<java.version>17</java.version>
<spring-cloud.version>2022.0.0-M1</spring-cloud.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<sonar.host.url>http://192.168.31.105:9001</sonar.host.url>
<sonar.login>admin</sonar.login>
<sonar.password>123456</sonar.password>
<sonar.projectKey>${project.groupId}:${project.artifactId}</sonar.projectKey>
<sonar.projectVersion>${project.version}</sonar.projectVersion>
<sonar.projectDescription>${project.description}</sonar.projectDescription>
<sonar.links.homepage>${project.url}</sonar.links.homepage>
<sonar.sourceEncoding>${project.build.sourceEncoding}</sonar.sourceEncoding>
<sonar.exclusions>src/test/**/*,*.js,*.css,*.html,*.vue,*.bat,*.cmd,*.md,*.NET,*.cpp</sonar.exclusions>
<sonar-maven-plugin.version>3.9.0.2155</sonar-maven-plugin.version>
<maven-compiler-plugin.version>3.9.0</maven-compiler-plugin.version>
<maven-source-plugin.version>3.2.1</maven-source-plugin.version>
<hutool-all.version>5.7.20</hutool-all.version>
<smart-doc.version>2.3.7</smart-doc.version>
<xmlsec.version>2.3.0</xmlsec.version>
<jna.version>3.0.9</jna.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--commons-lang3工具包-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
</dependency>
<!-- hutool工具包 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool-all.version}</version>
</dependency>
<dependency>
<groupId>org.apache.santuario</groupId>
<artifactId>xmlsec</artifactId>
<version>${xmlsec.version}</version>
</dependency>
<dependency>
<groupId>com.sun.jna</groupId>
<artifactId>jna</artifactId>
<version>${jna.version}</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<finalName>${project.name}-v${project.version}</finalName>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<target>${java.version}</target>
<source>${java.version}</source>
<encoding>${project.build.sourceEncoding}</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>${maven-source-plugin.version}</version>
</plugin>
<plugin>
<groupId>com.github.shalousun</groupId>
<artifactId>smart-doc-maven-plugin</artifactId>
<version>${smart-doc.version}</version>
</plugin>
<plugin>
<groupId>org.sonarsource.scanner.maven</groupId>
<artifactId>sonar-maven-plugin</artifactId>
<version>${sonar-maven-plugin.version}</version>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
<!-- 打包跳过所有测试类 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
<!-- sonar qube代码审查 -->
<plugin>
<groupId>org.sonarsource.scanner.maven</groupId>
<artifactId>sonar-maven-plugin</artifactId>
</plugin>
<!-- source源代码 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<executions>
<execution>
<id>attach-sources</id>
<phase>package</phase>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- smart-doc -->
<plugin>
<groupId>com.github.shalousun</groupId>
<artifactId>smart-doc-maven-plugin</artifactId>
<configuration>
<configFile>./src/main/resources/smart-doc.json</configFile>
<projectName>${project.artifactId}</projectName>
<excludes>
<exclude>com.alibaba:fastjson</exclude>
</excludes>
<includes>
<!--格式为:groupId:artifactId;参考如下-->
<!--<include>cn.alphahub.mall.common:lejing-common-base-public</include>-->
</includes>
</configuration>
<executions>
<execution>
<!--如果不需要在执行编译时启动smart-doc,则将phase注释掉-->
<!--<phase>compile</phase>
<goals>
<goal>html</goal>
</goals>-->
</execution>
</executions>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</project>
@@ -0,0 +1,17 @@
package cn.alphahub.eport.signature;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* 中国电子口岸(海口海关CEBXxxMessage)加签服务
*
* @author Weasley J
* @date 2022-02-13
*/
@SpringBootApplication
public class ChinaEportSignatureApplication {
public static void main(String[] args) {
SpringApplication.run(ChinaEportSignatureApplication.class, args);
}
}
@@ -0,0 +1,166 @@
package cn.alphahub.eport.signature.basic.advice;
import cn.alphahub.eport.signature.basic.domain.Result;
import cn.alphahub.eport.signature.basic.enums.BizCodeEnum;
import cn.alphahub.eport.signature.basic.exception.SignException;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.servlet.NoHandlerFoundException;
import org.yaml.snakeyaml.constructor.DuplicateKeyException;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* 全局异常处理(集中处理全局异常处理)
*
* @author liuwenjing
*/
@Slf4j
@RestControllerAdvice(basePackages = {"cn.alphahub"})
public class GlobalExceptionHandler {
/**
* 处理自定义业务异常
*
* @param e 业务异常
* @return 错误信息
*/
@ExceptionHandler(SignException.class)
public Result<SignException> customizeExceptionHandler(SignException e) {
return Result.fail(e.getCode(), e.getMessage());
}
/**
* 找不到处理程序异常
*
* @param e 异常
* @return 错误信息
*/
@ExceptionHandler(NoHandlerFoundException.class)
public Result<Object> notFoundExceptionHandler(NoHandlerFoundException e) {
log.error(e.getMessage(), e);
return Result.fail(404, "路径不存在,请检查路径是否正确: " + e.getMessage());
}
/**
* 重复键异常
*
* @param e 异常
* @return 错误信息
*/
@ExceptionHandler(DuplicateKeyException.class)
public Result<Object> duplicateKeyExceptionHandler(DuplicateKeyException e) {
log.error(e.getMessage(), e);
return Result.fail("数据库中已存在该记录: " + e.getMessage());
}
/**
* 数据绑定异常
*
* @param e 异常
* @return 错误信息
*/
@ExceptionHandler(BindException.class)
public Result<Object> handleBindException(BindException e) {
log.error(e.getMessage(), e);
Map<String, Object> errorMap = new LinkedHashMap<>();
for (ObjectError error : e.getAllErrors()) {
errorMap.putIfAbsent(error.getObjectName(), error.getDefaultMessage());
}
return Result.error(BizCodeEnum.VALID_EXCEPTION.getCode(), BizCodeEnum.VALID_EXCEPTION.getMessage(), errorMap);
}
/**
* 请求方式不支持
*
* @param e http request method not supported exception
* @param request http servlet request
* @return 错误信息
*/
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
public Result<Object> handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e, HttpServletRequest request) {
String uri = request.getRequestURI();
log.error("请求地址'{}',不支持'{}'请求", uri, e.getMethod());
return Result.error("请求地址'" + uri + "',不支持'" + e.getMethod() + "'请求");
}
/**
* 超出最大上传大小异常
*
* @param e 超出最大上传大小异常
* @return 错误信息
*/
@ExceptionHandler(value = {MaxUploadSizeExceededException.class})
public Result<Object> handleMaxUploadSizeExceededException(MaxUploadSizeExceededException e) {
log.error("超出最大上传大小异常: {}", e.getMessage(), e);
return Result.error("上传文件超过允许的最大上传大小:" + e.getLocalizedMessage(), "最大文件大小:" + e.getMaxUploadSize() + "(bytes)");
}
/**
* 数据校验异常
*
* @param e 异常对象
* @return 数据校验异常具体错误信息
*/
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public Result<Object> handleValidException(MethodArgumentNotValidException e) {
BindingResult result = e.getBindingResult();
Map<String, Map<String, Object>> errorMap = new LinkedHashMap<>();
Map<String, Object> globalErrorMap = new LinkedHashMap<>();
Map<String, Object> fieldErrorMap = new LinkedHashMap<>();
result.getGlobalErrors().forEach(objectError -> {
globalErrorMap.putIfAbsent(objectError.getObjectName(), e.getTarget());
globalErrorMap.putIfAbsent("errorMsg", objectError.getDefaultMessage());
});
result.getFieldErrors().forEach(fieldError -> fieldErrorMap.putIfAbsent(fieldError.getField(), fieldError.getDefaultMessage()));
errorMap.putIfAbsent("globalError", globalErrorMap);
errorMap.putIfAbsent("fieldError", fieldErrorMap);
log.error("数据校验异常:{},异常类型:{}", e.getMessage(), e.getClass());
return Result.error(BizCodeEnum.VALID_EXCEPTION.getCode(), BizCodeEnum.VALID_EXCEPTION.getMessage(), errorMap);
}
/**
* Exception异常
*
* @param e 异常
* @return 错误信息
*/
@ExceptionHandler(Exception.class)
public Result<Object> exceptionHandler(Exception e) {
log.error("异常信息:{}", e.getLocalizedMessage(), e);
return Result.fail(e.getMessage());
}
/**
* RuntimeException异常
*
* @param re 异常
* @return 错误提示
*/
@ExceptionHandler(RuntimeException.class)
public Result<Object> handleUnknownException(RuntimeException re) {
log.error("运行期间抛出的异常:{}", re.getMessage(), re);
return Result.error(re.getMessage());
}
/**
* Throwable系统未知异常
*
* @param throwable 异常对象
* @return 异常提示
*/
@ExceptionHandler(value = Throwable.class)
public Result<Object> handleException(Throwable throwable) {
log.error("错误信息: ", throwable);
return Result.error(BizCodeEnum.UNKNOWN_EXCEPTION.getCode(), BizCodeEnum.UNKNOWN_EXCEPTION.getMessage(), "Caused by:" + throwable.getLocalizedMessage());
}
}
@@ -0,0 +1,45 @@
package cn.alphahub.eport.signature.basic.domain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* 数据返回封装顶层抽象类
* <p>顶层抽象类, 可以使用我已经写好的BaseResult<T>, 也可以选择自己实现AbstractResult<T></p>
*
* @author liuwenjing
* @version 1.2.0
* @date 2022年2月11日
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public abstract sealed class AbstractResult<T> implements Serializable permits Result {
/**
* 返回消息
*/
private String message;
/**
* 是否成功
*/
private Boolean success = false;
/**
* 响应时间戳
*/
private String timestamp;
/**
* 状态码
*/
private Integer code;
/**
* 响应数据
*/
private T data;
}
@@ -0,0 +1,342 @@
package cn.alphahub.eport.signature.basic.domain;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.io.Serial;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
/**
* 数据返回封装
* <p>通用面向对象基础返回数据封装</p>
*
* @param <T> 返回数据对象
* @author liuwenjing
* @version 1.2.0
* @date 2022年2月11日
* @see AbstractResult
*/
@Data
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public final class Result<T> extends AbstractResult<T> implements Serializable {
@Serial
private static final long serialVersionUID = -7804054241710086L;
/**
* 初始化一个新创建的 Result 对象,使其表示一个空消息
*/
public Result() {
}
/**
* 初始化一个新创建的 Result 对象
*
* @param code 状态码
* @param msg 返回内容
* @param success 成功状态
*/
public Result(Integer code, String msg, Boolean success) {
this.setCode(code);
this.setMessage(msg);
this.setSuccess(success);
}
/**
* 初始化一个新创建的 Result 对象
*
* @param success 成功状态
*/
public Result(Boolean success) {
this.setSuccess(success);
}
/**
* 初始化一个新创建的 Result 对象
*
* @param success 成功状态
* @param message 返回消息
*/
public Result(Boolean success, String message) {
this.setSuccess(success);
this.setMessage(message);
}
/**
* 初始化一个新创建的 Result 对象
*
* @param code 状态码
* @param message 返回消息
*/
public Result(Integer code, String message) {
this.setCode(code);
this.setMessage(message);
}
/**
* 初始化一个新创建的 Result 对象
*
* @param success 成功提示
* @param message 返回消息
* @param data 数据对象
*/
public Result(Boolean success, String message, T data) {
this.setSuccess(success);
this.setMessage(message);
this.setData(data);
}
/**
* 初始化一个新创建的 Result 对象
*
* @param code 状态码
* @param msg 返回内容
* @param data 数据对象
*/
public Result(Integer code, String msg, T data) {
this.setCode(code);
this.setMessage(msg);
this.setData(data);
}
/**
* 类加载时初始化一个BaseResult<T>对象
*
* @param code 状态码
* @param msg 返回消息
* @param success 成功标志
* @param <T> 数据对象
* @return BaseResult封装好的数据对象
*/
private static <T> Result<T> preCreate(Integer code, String msg, Boolean success, T data) {
Result<T> result = new Result<>();
result.setCode(code);
result.setMessage(msg);
result.setSuccess(success);
result.setTimestamp(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now()));
result.setData(data);
return result;
}
/**
* 返回成功消息
*
* @param <T> 数据对象
* @return 成功消息
*/
public static <T> Result<T> ok() {
return ok(200, "操作成功");
}
/**
* 携带数据返回成功消息
*
* @param <T> 数据对象
* @param data 返回消息
* @return 数据对象
*/
public static <T> Result<T> ok(T data) {
return preCreate(200, "操作成功", true, data);
}
/**
* 返回成功消息
*
* @param <T> 数据对象
* @param code 状态码
* @param msg 返回内容
* @return 成功消息
*/
public static <T> Result<T> ok(Integer code, String msg) {
return preCreate(code, msg, true, null);
}
/**
* 携带数据返回成功消息
*
* @param <T> 数据对象
* @param msg 返回消息
* @param data 数据载体
* @return 封装的数据
*/
public static <T> Result<T> ok(String msg, T data) {
return preCreate(200, msg, true, data);
}
/**
* 返回失败消息
*
* @param <T> 数据对象
* @return 失败消息
*/
public static <T> Result<T> fail() {
return fail(500, "操作失败");
}
/**
* 返回失败消息
*
* @param <T> 数据对象
* @param msg 返回消息
* @return 失败消息
*/
public static <T> Result<T> fail(String msg) {
return fail(500, msg);
}
/**
* 返回错误消息
*
* @param <T> 数据对象
* @param code 响应状态码
* @param msg 响应消息
* @return 错误消息
*/
public static <T> Result<T> fail(Integer code, String msg) {
return preCreate(code, msg, false, null);
}
/**
* 返回错误消息
*
* @param <T> 数据对象
* @param msg 响应消息
* @param data 响应失败的消息体
* @return 错误消息
*/
public static <T> Result<T> fail(String msg, T data) {
return preCreate(400, msg, false, data);
}
/**
* 返回错误消息
*
* @param <T> 数据对象
* @param code 响应状态码
* @param msg 响应消息
* @param data 响应失败的消息体
* @return 错误消息
*/
public static <T> Result<T> fail(Integer code, String msg, T data) {
return preCreate(code, msg, false, data);
}
/**
* 返回成功消息
*
* @param <T> 数据对象
* @return 成功消息
*/
public static <T> Result<T> success() {
return Result.success(200, "操作成功");
}
/**
* 携带数据返回成功消息
*
* @param <T> 数据对象
* @param data 封装返回的数据对象
* @return 成功消息
*/
public static <T> Result<T> success(T data) {
return Result.success("操作成功", data);
}
/**
* 返回成功消息
*
* @param code 状态码
* @param msg 返回内容
* @param <T> 数据对象
* @return 成功消息
*/
public static <T> Result<T> success(Integer code, String msg) {
return preCreate(code, msg, true, null);
}
/**
* 返回成功消息
*
* @param msg 返回内容
* @param data 数据对象
* @param <T> 数据对象
* @return 成功消息
*/
public static <T> Result<T> success(String msg, T data) {
return preCreate(200, msg, true, data);
}
/**
* 返回错误消息
*
* @param <T> 数据对象
* @return 错误消息
*/
public static <T> Result<T> error() {
return Result.error("操作失败");
}
/**
* 返回错误消息
*
* @param msg 返回内容
* @param <T> 数据对象
* @return 警告消息
*/
public static <T> Result<T> error(String msg) {
return Result.error(msg, null);
}
/**
* 返回错误消息
*
* @param <T> 数据对象
* @param msg 返回内容
* @param data 数据对象
* @return 警告消息
*/
public static <T> Result<T> error(String msg, T data) {
return preCreate(500, msg, false, data);
}
/**
* 返回错误消息
*
* @param <T> 数据对象
* @param msg 返回内容
* @param data 数据对象
* @param code 状态码
* @return 警告消息
*/
public static <T> Result<T> error(Integer code, String msg, T data) {
return preCreate(code, msg, false, data);
}
/**
* 返回错误消息
*
* @param <T> 数据对象
* @param code 状态码
* @param msg 返回内容
* @return 警告消息
*/
public static <T> Result<T> error(Integer code, String msg) {
return preCreate(code, msg, false, null);
}
/**
* 封装数据并返回一个BaseResult实例
*
* @param data 数据
* @return BaseResult实例
*/
public Result<T> setResult(T data) {
this.setData(data);
return this;
}
}
@@ -0,0 +1,44 @@
package cn.alphahub.eport.signature.basic.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* <b>错误码和错误信息枚举类</b>
* <br>
* 1. 错误码定义规则为5为数字
* 2. 前两位表示业务场景,最后三位表示错误码。例如:100001。10:通用 001:系统未知异常
* 3. 维护错误码后需要维护错误描述,将他们定义为枚举形式
* <br>
* 错误码列表:
* 10: 通用
* 001:参数格式校验
* 11: 商品
* 12: 订单
* 13: 购物车
* 14: 物流
*
* @author liuwenjing
*/
@Getter
@AllArgsConstructor
public enum BizCodeEnum {
/**
* 系统未知异常
*/
UNKNOWN_EXCEPTION(10000, "系统未知异常"),
/**
* 参数格式校验异常
*/
VALID_EXCEPTION(10001, "参数格式校验异常"),
;
/**
* 错误码
*/
private final Integer code;
/**
* 错误消息
*/
private final String message;
}
@@ -0,0 +1,49 @@
package cn.alphahub.eport.signature.basic.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;
}
}
@@ -0,0 +1,43 @@
package cn.alphahub.eport.signature.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
import java.util.Collections;
import java.util.List;
/**
* 跨域配置类,解决ajax请求跨域问题
* <p>Spring WebFlux Java config CORS 配置的替代方案
*
* @author liuwenjing
* @version 1.1.3
* @date 2022年2月13日
*/
@Configuration
public class CorsConfig {
/**
* @return Spring WebFlux Java config CORS配置的{@code CorsWebFilter}
*/
@Bean
public CorsWebFilter corsWebFilter() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.setAllowedHeaders(Collections.singletonList(CorsConfiguration.ALL));
config.setAllowedMethods(Collections.singletonList(CorsConfiguration.ALL));
config.setAllowedOriginPatterns(Collections.singletonList(CorsConfiguration.ALL));
config.setExposedHeaders(List.of(HttpHeaders.SET_COOKIE));
config.setMaxAge(3600L);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
}
@@ -0,0 +1,258 @@
package cn.alphahub.eport.signature.config;
import cn.alphahub.eport.signature.core.SignatureHandler;
import cn.alphahub.eport.signature.core.X509CertificateHandler;
import cn.alphahub.eport.signature.entity.SignRequest;
import cn.alphahub.eport.signature.entity.UkeyResponse;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
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.core.io.ClassPathResource;
import org.springframework.lang.Nullable;
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.nio.charset.StandardCharsets;
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;
/**
* 初始化配置
*
* @author lwj
* @version 1.0
* @date 2022-01-11 15:29
*/
@Slf4j
@Data
@Configuration
@EnableConfigurationProperties({UkeyProperties.class})
public class InitialConfig implements ApplicationRunner {
/**
* jackson序列化处禁止换成其他json序列化工具
*/
private static final ObjectMapper MAPPER = new ObjectMapper();
/**
* ukey默认密码8个8不要修改
*/
private static final String DEFAULT_PASSWORD = "88888888";
/**
* u-key的.cer证书是否存在
*/
private Boolean isCertFileExist = Boolean.FALSE;
/* *********************** 获取入参方法开始,已下方法的返回值最为参数连接好海关u-key通过socket实例发送给[ws://127.0.0.1:61232] *********************** */
/**
* 取海关签名证书PEM, X509Certificate证书(未经HASH算法散列过)
*
* @return 取海关签名证书PEM, X509Certificate证书是加签证书,真正的X509Certificate证书
*/
@SneakyThrows
public static String getX509CertificateParameter() {
Map<String, Object> parameterMap = new LinkedHashMap<>(3);
parameterMap.put("_method", "cus-sec_SpcGetSignCertAsPEM");
parameterMap.put("_id", 1);
parameterMap.put("args", "{}");
return MAPPER.writeValueAsString(parameterMap);
}
/**
* 使用卡计算摘要,返回PEM格式信息, 这个不调也罢,已经硬核计算出来了
*
* @param sourceXml 不含ds:Signature节点的<ceb:CEBXxxMessage></ceb:CEBXxxMessage>源xml原文数据
* @param uniqueId uniqueId 唯一id, 用来区分是哪一次发送的消息,int32,最大32位大于0
* @return DigestValue的值
*/
@SneakyThrows
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));
Map<String, Object> parameterMap = new LinkedHashMap<>(3);
parameterMap.put("_method", "cus-sec_SpcSHA1DigestAsPEM");
parameterMap.put("_id", uniqueId);
parameterMap.put("args", args);
return MAPPER.writeValueAsString(parameterMap);
}
/**
* 签名, 不对原文计算摘要, 请您自行计算好摘要传入
*
* @param request 原文入参
* @return SignatureValue的值, 返回的数组,包含您的证书编号,可作为KeyName的值
*/
@SneakyThrows
public static String getSignDataAsPEMParameter(SignRequest request) {
Map<String, Object> args = new LinkedHashMap<>(2);
args.put("inData", SignatureHandler.getSignatureValueBeforeSend(request));
args.put("passwd", ObjectUtils.defaultIfNull(SpringUtil.getBean(UkeyProperties.class).getPassword(), DEFAULT_PASSWORD));
Map<String, Object> parameterMap = new LinkedHashMap<>(3);
parameterMap.put("_method", X509CertificateHandler.METHOD_OF_X509_WITH_HASH);
parameterMap.put("_id", request.getId());
parameterMap.put("args", args);
return MAPPER.writeValueAsString(parameterMap);
}
/**
* 签名,对原文计算摘要(方法内部已经计算好)
*
* @param request cebXxxMessage加签请求参数
* @return SignatureValue的值
*/
@SneakyThrows
public static String getSignDataNoHashAsPEMParameter(SignRequest request) {
Map<String, Object> args = new LinkedHashMap<>(2);
//对原文计算摘:SHA-1 digest as a hex string
String sha1Hex = DigestUtils.sha1Hex(SignatureHandler.getSignatureValueBeforeSend(request));
args.put("inData", sha1Hex);
args.put("passwd", ObjectUtils.defaultIfNull(SpringUtil.getBean(UkeyProperties.class).getPassword(), DEFAULT_PASSWORD));
Map<String, Object> parameterMap = new LinkedHashMap<>(3);
parameterMap.put("_method", X509CertificateHandler.METHOD_OF_X509_WITHOUT_HASH);
parameterMap.put("_id", request.getId());
parameterMap.put("args", args);
return MAPPER.writeValueAsString(parameterMap);
}
/**
* 验证签名,不对原文计算摘要,请您自行计算好摘要传入
*
* @param sourceXml 不带ds:Signature节点的CEbXXXMessage.xml原文
* @param signatureValue 签名信息
* @param certDataPEM 签名证书,PEM编码格式 可以为空,则取当前插着的卡中的证书
* @param uniqueId uniqueId 唯一id, 用来区分是哪一次发送的消息,int32,最大32位大于0
*/
@SneakyThrows
public static String getVerifySignDataNoHashParameter(String sourceXml, String signatureValue, @Nullable String certDataPEM, Integer uniqueId) {
Map<String, Object> argsMap = new LinkedHashMap<>(2);
//对原文计算摘:SHA-1 digest as a hex string
String sha1Hex = DigestUtils.sha1Hex(SignatureHandler.getSignatureValueBeforeSend(new SignRequest(null, sourceXml)));
argsMap.put("inData", sha1Hex);
argsMap.put("signData", signatureValue);
if (StringUtils.isNotBlank(certDataPEM)) {
argsMap.put("certDataPEM", certDataPEM);
}
Map<String, Object> parameterMap = new LinkedHashMap<>(3);
parameterMap.put("_method", "cus-sec_SpcVerifySignDataNoHash");
parameterMap.put("_id", uniqueId);
parameterMap.put("args", argsMap);
return MAPPER.writeValueAsString(parameterMap);
}
/* *********************** 获取入参方法结束(这几个方法值5000RMB,小心修改) *********************** */
/**
* Callback used to run the bean.
*
* @param args incoming application arguments
* @throws Exception on error
*/
@Override
public void run(ApplicationArguments args) throws Exception {
org.apache.xml.security.Init.init();
if (log.isDebugEnabled()) {
log.debug("XMl output format with C14n Initial Succeed.");
}
}
/**
* Standard WebSocket Client
*
* @return StandardWebSocketClient
*/
@Bean
@ConditionalOnMissingBean({StandardWebSocketClient.class})
public StandardWebSocketClient standardWebSocketClient() {
return new StandardWebSocketClient();
}
/**
* 注入一个X509Certificate证书判断的Bean
*
* @return X509Certificate证书判断
*/
@Bean
@SneakyThrows
public X509CertificateHandler x509CertificateHandler(UkeyProperties properties, StandardWebSocketClient standardWebSocketClient) {
X509CertificateHandler handler = new X509CertificateHandler();
Map<String, String> x509Map = new ConcurrentHashMap<>(2);
ClassPathResource classPathResource = new ClassPathResource(properties.getCertPath());
if (classPathResource.exists()) {
this.isCertFileExist = Boolean.TRUE;
String x509CertificateWithHash = IoUtil.read(classPathResource.getInputStream(), StandardCharsets.UTF_8).replace("-----BEGIN CERTIFICATE-----\n", "").replace("\n-----END CERTIFICATE-----", "");
x509Map.put(X509CertificateHandler.METHOD_OF_X509_WITH_HASH, x509CertificateWithHash);
}
AtomicReference<Thread> reference = new AtomicReference<>();
reference.set(Thread.currentThread());
WebSocketConnectionManager manager = new WebSocketConnectionManager(standardWebSocketClient, new TextWebSocketHandler() {
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
log.warn("已和[{}]建立websocket连接.", properties.getWsUrl());
session.sendMessage(new TextMessage(InitialConfig.getX509CertificateParameter()));
}
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) {
UkeyResponse response = JSONUtil.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) && CollectionUtil.isNotEmpty(responseArgs.getData())) {
x509Map.put(X509CertificateHandler.METHOD_OF_X509_WITHOUT_HASH, responseArgs.getData().get(0));
log.warn("\n已从电子口岸U-Key中获取到未经hash算法的x509Certificate证书: {}", MAPPER.writeValueAsString(x509Map));
}
}
} catch (Exception e) {
log.error("唤醒线程异常 {}", e.getLocalizedMessage(), e);
} finally {
LockSupport.unpark(reference.get());
}
}
}, properties.getWsUrl());
manager.start();
//线程等待
try {
LockSupport.parkNanos(Thread.currentThread(), 1000 * 1000 * 1000 * 3L);
} catch (Exception e) {
log.error("线程自动unpark异常 {}", e.getLocalizedMessage(), e);
}
handler.setX509Map(x509Map);
return handler;
}
}
@@ -0,0 +1,30 @@
package cn.alphahub.eport.signature.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;
/**
* 电子口岸u-key的配置参数
*
* @author weasley
* @version 1.0
* @date 2022/2/13
*/
@Data
@Validated
@ConfigurationProperties(prefix = "eport.signature.ukey")
public class UkeyProperties {
/**
* u-key的.cer证书在classpath的路径
*/
private String certPath;
/**
* socket链接url
*/
private String wsUrl = "ws://127.0.0.1:61232";
/**
* u-key密码
*/
private String password = "88888888";
}
@@ -0,0 +1,132 @@
package cn.alphahub.eport.signature.controller.rpc;
import cn.alphahub.eport.signature.basic.domain.Result;
import cn.alphahub.eport.signature.core.EportSignHandler;
import cn.alphahub.eport.signature.entity.SignRequest;
import cn.alphahub.eport.signature.entity.SignResult;
import cn.hutool.json.JSONUtil;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 电子口岸报文加签Controller
*
* @author lwj
* @version 1.0
* @date 2022-01-11 15:29
*/
@Slf4j
@RestController
@RequestMapping("/rpc/eport")
public class EportSignController {
@Resource
private EportSignHandler eportSignHandler;
/**
* CEBXxxMessage.xml加签
*
* @param request CEBXxxMessage XML加签请求入参
* @return 签名结果
*/
@PostMapping("/signature")
public Result<SignResult> signature(@RequestBody @Validated SignRequest request) {
log.info("加签请求参数 {}", JSONUtil.toJsonStr(request));
SignResult sign = eportSignHandler.sign(request, eportSignHandler.getDynamicSignDataParameter(request));
log.info("加签结果响应体 {}", JSONUtil.toJsonStr(sign));
if (sign.getSuccess().equals(false)) {
return Result.error("加签失败");
}
return Result.success(sign);
}
/**
* 加签测试接口
*
* @return 签名结果
*/
@GetMapping("/signature/test")
public Result<SignResult> signatureTest() {
String sourceXml = """
<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>
""";
SignRequest request = new SignRequest();
request.setSourceXml(sourceXml);
SignResult sign = eportSignHandler.sign(request, eportSignHandler.getDynamicSignDataParameter(request));
log.info("加签结果响应体 {}", JSONUtil.toJsonStr(sign));
if (sign.getSuccess().equals(false)) {
return Result.error("加签失败");
}
return Result.success(sign);
}
}
@@ -0,0 +1,149 @@
package cn.alphahub.eport.signature.core;
import cn.alphahub.eport.signature.config.InitialConfig;
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.UkeyResponse;
import cn.alphahub.eport.signature.entity.WebSocketWrapper;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.json.JSONUtil;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.socket.CloseStatus;
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
*/
@Slf4j
@Service
@Validated
public class EportSignHandler {
/**
* 电子口岸u-key的配置参数
*/
private final UkeyProperties ukeyProperties = SpringUtil.getBean(UkeyProperties.class);
/**
* StandardWebSocketClient
*/
private final StandardWebSocketClient standardWebSocketClient = SpringUtil.getBean(StandardWebSocketClient.class);
/**
* X509Certificate证书策略
*/
private final X509CertificateHandler x509CertificateHandler = SpringUtil.getBean(X509CertificateHandler.class);
@Resource
private InitialConfig config;
/**
* 如果.cer证书不存在时动态切换发送u-key的签名方法
*
* @return 发送u-key的签名的入参
*/
public String getDynamicSignDataParameter(SignRequest request) {
return config.getIsCertFileExist() ? InitialConfig.getSignDataAsPEMParameter(request) : InitialConfig.getSignDataNoHashAsPEMParameter(request);
}
/**
* WebSocket发送消息给电子口岸u-key
*
* @param request 加签请求参数
* @param payload websocket发送的数据载荷
*/
public SignResult sign(@Valid SignRequest request, @NotBlank(message = "websocket发送的数据载荷不能为空") String payload) {
log.info("\nukey入参: \n{}; \r\n加签请求xml原文: \n{}", payload, request.getSourceXml());
WebSocketWrapper socketWrapper = new WebSocketWrapper();
socketWrapper.setPayload(payload);
socketWrapper.setRequest(request);
socketWrapper.setThreadAtomicReference(new AtomicReference<>(Thread.currentThread()));
socketWrapper.getSignResult().setDigestValue(SignatureHandler.getDigestValueOfCEBXxxMessage(request.getSourceXml()));
WebSocketConnectionManager manager = initWebSocketConnectionManager(socketWrapper);
manager.start();
try {
LockSupport.parkNanos(socketWrapper.getThreadAtomicReference(), 1000 * 1000 * 1000 * 5L);
} catch (Exception e) {
socketWrapper.getSignResult().setSuccess(false);
manager.stop();
log.error("线程自动unpark异常 {}", e.getLocalizedMessage(), e);
}
if (manager.isRunning()) {
manager.stop();
}
return socketWrapper.getSignResult();
}
/**
* 初始化WebSocketConnectionManager
*
* @param socketWrapper WebSocket包装类
* @return WebSocketConnectionManager
*/
private WebSocketConnectionManager initWebSocketConnectionManager(WebSocketWrapper socketWrapper) {
return new WebSocketConnectionManager(standardWebSocketClient, new TextWebSocketHandler() {
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
super.afterConnectionEstablished(session);
log.warn("已和[{}]建立websocket连接...", ukeyProperties.getWsUrl());
session.sendMessage(new TextMessage(socketWrapper.getPayload()));
}
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
super.handleTextMessage(session, message);
log.warn("收到ukey响应数据: {}", message.getPayload());
UkeyResponse response = JSONUtil.toBean(message.getPayload(), new TypeReference<>() {
}, true);
if (Objects.equals(response.get_id(), socketWrapper.getRequest().getId())) {
try {
UkeyResponse.Args responseArgs = response.get_args();
if (responseArgs.getResult().equals(true) && CollectionUtils.isNotEmpty(responseArgs.getData())) {
socketWrapper.getSignResult().setSuccess(true);
socketWrapper.getSignResult().setX509Certificate(x509CertificateHandler.get509Certificate(response.get_method()));
socketWrapper.getSignResult().setSignatureValue(responseArgs.getData().get(0));
socketWrapper.getSignResult().setCertNo(responseArgs.getData().get(1));
log.warn("\n收到ukey加签响应结果:{}", JSONUtil.toJsonStr(responseArgs));
}
} catch (Exception e) {
socketWrapper.getSignResult().setSuccess(false);
log.error("唤醒线程异常 {}", e.getLocalizedMessage(), e);
} finally {
LockSupport.unpark(socketWrapper.getThreadAtomicReference().get());
}
}
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
super.afterConnectionClosed(session, status);
log.info("WebSocket connection has been closed, Close Status: {}, reason: {}", status.getCode(), status.getReason());
}
}, ukeyProperties.getWsUrl());
}
}
@@ -0,0 +1,197 @@
package cn.alphahub.eport.signature.core;
import cn.alphahub.eport.signature.basic.exception.SignException;
import cn.alphahub.eport.signature.entity.SignRequest;
import cn.alphahub.eport.signature.util.XMLUtils;
import jakarta.validation.constraints.NotBlank;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.xml.security.c14n.Canonicalizer;
import org.apache.xml.security.utils.IgnoreAllErrorHandler;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.StringReader;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
/**
* 末三加密核心方法
* <p>华讯云的商业机密,价值5000RMB的解决方案</p>
*
* @author 刘雯静
* @version 1.0
* @date 2022/2/11
*/
@Slf4j
public final class SignatureHandler {
@SuppressWarnings("restriction")
private static final IgnoreAllErrorHandler IGNORE_ALL_ERROR_HANDLER = new IgnoreAllErrorHandler();
public static DocumentBuilderFactory documentBuilderFactory;
public static Transformer transFormer;
static {
documentBuilderFactory = DocumentBuilderFactory.newInstance();
documentBuilderFactory.setNamespaceAware(true);
documentBuilderFactory.setValidating(true);
try {
//spring中已在org.springframework.boot.ApplicationRunner子类初始化过,防止普通类调用出错
org.apache.xml.security.Init.init();
transFormer = TransformerFactory.newInstance().newTransformer();
} catch (Exception e) {
log.error("TransFormer init false. Error {}", e.getMessage(), e);
}
}
private SignatureHandler() {
throw new IllegalStateException("Utility class");
}
/**
* 获取发送给U-KEY加签的xml的ds:Signature节点内容
*
* @param request 原文信息
* @return SignatureValue的发送给U-KEY加签的xml
* @see SignatureHandler#getDigestValueOfCEBXxxMessage(String) 不含ds:Signature节点的digestValue的计算方法
*/
@SneakyThrows
public static String getSignatureValueBeforeSend(SignRequest request) {
long start = System.currentTimeMillis();
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
documentBuilder.setErrorHandler(IGNORE_ALL_ERROR_HANDLER);
Document document = documentBuilder.parse(new InputSource(new StringReader(request.getSourceXml())));
// 原始xml摘要
DOMSource ds = new DOMSource(document);
StringWriter sw = new StringWriter();
StreamResult sr = new StreamResult(sw);
transFormer.transform(ds, sr);
Canonicalizer canon = Canonicalizer.getInstance("http://www.w3.org/TR/2001/REC-xml-c14n-20010315");
canon.canonicalize(sw.toString().getBytes(), new ByteArrayOutputStream(), true);
String digestValue = getDigestValueOfCEBXxxMessage(request.getSourceXml());
// document增加signature节点
createSignatureElement(document, digestValue);
// 读取签名段的报文
NodeList nodeList = document.getElementsByTagName("ds:SignedInfo");
Node node = nodeList.item(0);
ByteArrayOutputStream writer = new ByteArrayOutputStream();
canon.canonicalizeSubtree(node, writer);
String canonicalizeStr = writer.toString(StandardCharsets.UTF_8);
log.info("\n<ds:DigestValue>{}</ds:DigestValue>\n{}\ntotal sign time:{}ms, content-length:{}", digestValue, canonicalizeStr, (System.currentTimeMillis() - start), canonicalizeStr.length());
return canonicalizeStr;
}
/**
* sign digest value with c14n
*
* @param sourceXml 源CEBXxxMessage,不含ds:Signature节点的<ceb:CEBXxxMessage></ceb:CEBXxxMessage>源xml
* @return digest value
*/
public static String getDigestValueOfCEBXxxMessage(@NotBlank String sourceXml) {
Document doc;
try (InputStream is = new ByteArrayInputStream(sourceXml.getBytes(StandardCharsets.UTF_8))) {
doc = XMLUtils.read(is, false);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
org.apache.xml.security.utils.XMLUtils.outputDOMc14nWithComments(doc, bos);
String res = bos.toString(StandardCharsets.UTF_8.name());
byte[] sha1 = DigestUtils.sha1(res);
String base64String = Base64.encodeBase64String(sha1);
log.info("c14n source xml result, sha1: {}, base64: {}", sha1, base64String);
return base64String;
} catch (Exception e) {
log.error("使用c14n格式化xml异常", e);
throw new SignException("使用c14n格式化xml异常: " + e.getMessage());
}
}
/**
* 创建签名节点,初始化摘要信息
*
* @param document xml document
* @param digestValue digestValue
*/
private static void createSignatureElement(Document document, String digestValue) {
Element canonicalizationMethodElement = document.createElement("ds:CanonicalizationMethod");
canonicalizationMethodElement.setAttribute("Algorithm", "http://www.w3.org/TR/2001/REC-xml-c14n-20010315");
canonicalizationMethodElement.setTextContent("");
Element signatureMethodElement = document.createElement("ds:SignatureMethod");
signatureMethodElement.setAttribute("Algorithm", "http://www.w3.org/2000/09/xmldsig#rsa-sha1");
signatureMethodElement.setTextContent("");
Element transformElement = document.createElement("ds:Transform");
transformElement.setAttribute("Algorithm", "http://www.w3.org/2000/09/xmldsig#enveloped-signature");
transformElement.setTextContent("");
Element transformsElement = document.createElement("ds:Transforms");
transformsElement.appendChild(transformElement);
//初始化摘要信息
Element digestValueElement = document.createElement("ds:DigestValue");
digestValueElement.setTextContent(digestValue);
Element digestMethodElement = document.createElement("ds:DigestMethod");
digestMethodElement.setAttribute("Algorithm", "http://www.w3.org/2000/09/xmldsig#sha1");
Element referenceElement = document.createElement("ds:Reference");
referenceElement.setAttribute("URI", "");
referenceElement.appendChild(transformsElement);
referenceElement.appendChild(digestMethodElement);
referenceElement.appendChild(digestValueElement);
Element signedInfoElement = document.createElement("ds:SignedInfo");
signedInfoElement.setAttribute("xmlns:ceb", "http://www.chinaport.gov.cn/ceb");
signedInfoElement.setAttribute("xmlns:ds", "http://www.w3.org/2000/09/xmldsig#");
signedInfoElement.setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
signedInfoElement.appendChild(canonicalizationMethodElement);
signedInfoElement.appendChild(signatureMethodElement);
signedInfoElement.appendChild(referenceElement);
Element signatureValueElement = document.createElement("ds:SignatureValue");
signatureValueElement.setTextContent("signaturValue");
Element x509Certificate = document.createElement("ds:X509Certificate");
x509Certificate.setTextContent("");
Element x509DataElement = document.createElement("ds:X509Data");
x509DataElement.appendChild(x509Certificate);
Element keyNameElement = document.createElement("ds:KeyName");
keyNameElement.setTextContent("");
Element keyInfoElement = document.createElement("ds:KeyInfo");
keyInfoElement.appendChild(keyNameElement);
keyInfoElement.appendChild(x509DataElement);
Element signatureElement = document.createElement("ds:Signature");
signatureElement.setAttribute("xmlns:ds", "http://www.w3.org/2000/09/xmldsig#");
signatureElement.appendChild(signedInfoElement);
signatureElement.appendChild(signatureValueElement);
signatureElement.appendChild(keyInfoElement);
Element rootElement = document.getDocumentElement();
rootElement.appendChild(signatureElement);
}
}
@@ -0,0 +1,57 @@
package cn.alphahub.eport.signature.core;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import java.io.Serializable;
import java.util.Map;
/**
* X509Certificate证书判断
*
* @author weasley
* @version 1.0
* @date 2022/2/13
*/
@Data
@Slf4j
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class X509CertificateHandler implements Serializable {
/**
* 从u-key读取含发送推送{@code <ds:X509Certificate>}(经HASH散列过)证书对应的方法
*/
public static final String METHOD_OF_X509_WITH_HASH = "cus-sec_SpcSignDataAsPEM";
/**
* 从u-key读取含发送推送{@code <ds:X509Certificate>}(不经HASH散列,就是一个字符串)证书对应的方法
*/
public static final String METHOD_OF_X509_WITHOUT_HASH = "cus-sec_SpcSignDataNoHashAsPEM";
/**
* 判断用那一个证书的集合
* <ul>
* <li>key -> 从u-key读取<ds:SignatureValue>的方法名</li>
* <li>value -> 从u-key读取<ds:SignatureValue>的方法名对应的X509Certificate证书</li>
* </ul>
*/
private Map<String, String> x509Map;
/**
* 获取509Certificate证书
*
* @param method 发送给电子口岸u-key的入参的[_method]字段的值
* @return 509Certificate证书
*/
public String get509Certificate(String method) {
return switch (method) {
case METHOD_OF_X509_WITH_HASH -> this.getX509Map().get(X509CertificateHandler.METHOD_OF_X509_WITH_HASH);
case METHOD_OF_X509_WITHOUT_HASH -> this.getX509Map().get(X509CertificateHandler.METHOD_OF_X509_WITHOUT_HASH);
default -> throw new IllegalStateException("Unexpected value: " + method);
};
}
}
@@ -0,0 +1,45 @@
package cn.alphahub.eport.signature.entity;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import org.springframework.lang.Nullable;
import java.io.Serializable;
/**
* CEBXxxMessage加签请求入参
*
* @author weasley
* @version 1.0
* @date 2022/2/13
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class SignRequest implements Serializable {
/**
* 唯一id, 用来区分是哪一次发送的消息,默认值=1,从1开始,{@code int} 最大, 2<sup>31</sup>-1.
*/
@Nullable
@Min(1)
@Max(Integer.MAX_VALUE)
private Integer id = 1;
/**
* 不带ds:Signature节点的CEbXXXMessage.xml原文
*/
@NotBlank(message = "不带<ds:Signature></ds:Signature>节点的CEbXXXMessage.xml原文不能为空")
private String sourceXml;
@Nullable
public Integer getId() {
return (id == null || id <= 0) ? 1 : id;
}
}
@@ -0,0 +1,47 @@
package cn.alphahub.eport.signature.entity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.io.Serializable;
/**
* 加签结果返回
*
* @author weasley
* @version 1.0
* @date 2022/2/13
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class SignResult implements Serializable {
/**
* 本次是否加签成功
*/
private Boolean success = false;
/**
* 签名的ukey的卡序列号,需要用专用的工具导出
*/
private String certNo;
/**
* 签名的ukey证书,需要用专用工具导出
*/
private String x509Certificate;
/**
* xml的数字摘要,先对不包含Signature节点的原始报文,进行C14n格式化,然后取shal二进制摘要,然后对sha1的值进行base64编码
* <ul>
* <li><a href='http://tool.qdhuaxun.cn/ceb/CEB311Message.xml'> 待加签报文样例</a></li>
* </ul>
*/
private String digestValue;
/**
* 调用ukey获取的签名值
*/
private String signatureValue;
}
@@ -0,0 +1,149 @@
package cn.alphahub.eport.signature.entity;
import java.io.Serial;
import java.io.Serializable;
import java.util.List;
import java.util.Objects;
/**
* u-key加签返回结果包装类
* <p>以下对象模型禁止修改</p>
*
* @author Weasley
* @date 2022-02-12 19:7:6
*/
public class UkeyResponse implements Serializable {
@Serial
private static final long serialVersionUID = -1L;
/**
* 会话id,用来区分是哪次加签
*/
private Integer _id;
private String _method;
private String _status;
private Args _args;
public UkeyResponse() {
}
public UkeyResponse(Integer _id, String _method, String _status, Args _args) {
this._id = _id;
this._method = _method;
this._status = _status;
this._args = _args;
}
public Integer get_id() {
return _id;
}
public void set_id(Integer _id) {
this._id = _id;
}
public String get_method() {
return _method;
}
public void set_method(String _method) {
this._method = _method;
}
public String get_status() {
return _status;
}
public void set_status(String _status) {
this._status = _status;
}
public Args get_args() {
return _args;
}
public void set_args(Args _args) {
this._args = _args;
}
@Override
public String toString() {
return "UkeyResponse{" + "_id=" + _id + ", _method='" + _method + '\'' + ", _status='" + _status + '\'' + ", _args=" + _args + '}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof UkeyResponse that)) return false;
return Objects.equals(get_id(), that.get_id()) && Objects.equals(get_method(), that.get_method()) && Objects.equals(get_status(), that.get_status()) && Objects.equals(get_args(), that.get_args());
}
@Override
public int hashCode() {
return Objects.hash(get_id(), get_method(), get_status(), get_args());
}
/**
* u-key加签返回内层对象,我们要的数据在这里面
*/
public static class Args implements Serializable {
@Serial
private static final long serialVersionUID = -1L;
private Boolean Result;
/**
* 我们要的数据在这里面
*/
private List<String> Data;
private List<String> Error;
public Args() {
}
public Args(Boolean result, List<String> data, List<String> error) {
Result = result;
Data = data;
Error = error;
}
public Boolean getResult() {
return Result;
}
public void setResult(Boolean result) {
Result = result;
}
public List<String> getData() {
return Data;
}
public void setData(List<String> data) {
Data = data;
}
public List<String> getError() {
return Error;
}
public void setError(List<String> error) {
Error = error;
}
@Override
public String toString() {
return "Args{" + "Result=" + Result + ", Data=" + Data + ", Error=" + Error + '}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Args args)) return false;
return Objects.equals(getResult(), args.getResult()) && Objects.equals(getData(), args.getData()) && Objects.equals(getError(), args.getError());
}
@Override
public int hashCode() {
return Objects.hash(getResult(), getData(), getError());
}
}
}
@@ -0,0 +1,40 @@
package cn.alphahub.eport.signature.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.concurrent.atomic.AtomicReference;
/**
* WebSocket包装类
*
* @author weasley
* @version 1.0
* @date 2022/2/13
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class WebSocketWrapper implements Serializable {
/**
* websocket发送的数据载荷
*/
private String payload;
/**
* 当前线程
*/
private AtomicReference<Thread> threadAtomicReference = new AtomicReference<>();
/**
* CEBXxxMessage加签请求入参
*/
private SignRequest request = new SignRequest();
/**
* 加签结果
*/
private SignResult signResult = new SignResult();
}
@@ -0,0 +1,297 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package cn.alphahub.eport.signature.util;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.xml.sax.EntityResolver;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.validation.Schema;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.util.Collections;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;
/**
* DOM and XML accessibility and comfort functions.
*/
@SuppressWarnings({"unchecked"})
public final class XMLUtils {
private static final WeakObjectPool<DocumentBuilder, ParserConfigurationException>[] pools = new WeakObjectPool[2];
private static final String dsPrefix = "ds";
private static final String ds11Prefix = "dsig11";
private static final String xencPrefix = "xenc";
private static final String xenc11Prefix = "xenc11";
static {
pools[0] = new DocumentBuilderPool(false);
pools[1] = new DocumentBuilderPool(true);
}
/**
* Constructor XMLUtils
*/
private XMLUtils() {
// we don't allow instantiation
}
public static Document read(InputStream inputStream) throws ParserConfigurationException, SAXException, IOException {
return read(inputStream, true);
}
public static Document read(InputStream inputStream, boolean disAllowDocTypeDeclarations) throws ParserConfigurationException, SAXException, IOException {
DocumentBuilder documentBuilder = createDocumentBuilder(disAllowDocTypeDeclarations);
Document doc = documentBuilder.parse(inputStream);
repoolDocumentBuilder(documentBuilder);
return doc;
}
private static DocumentBuilder createDocumentBuilder(boolean disAllowDocTypeDeclarations) throws ParserConfigurationException {
int idx = getPoolsIndex(disAllowDocTypeDeclarations);
return pools[idx].getObject();
}
private static boolean repoolDocumentBuilder(DocumentBuilder db) {
if (!(db instanceof DocumentBuilderProxy)) {
return false;
}
db.reset();
boolean disAllowDocTypeDeclarations = ((DocumentBuilderProxy) db).disAllowDocTypeDeclarations();
int idx = getPoolsIndex(disAllowDocTypeDeclarations);
return pools[idx].repool(db);
}
/**
* Maps the boolean configuration options for the factories to the array index for the WeakObjectPool
*
* @param disAllowDocTypeDeclarations
* @return the index to the {@link #pools}
*/
private static int getPoolsIndex(boolean disAllowDocTypeDeclarations) {
return (disAllowDocTypeDeclarations ? 1 : 0);
}
private static final class DocumentBuilderPool extends WeakObjectPool<DocumentBuilder, ParserConfigurationException> {
private final boolean disAllowDocTypeDeclarations;
public DocumentBuilderPool(boolean disAllowDocTypeDeclarations) {
this.disAllowDocTypeDeclarations = disAllowDocTypeDeclarations;
}
@Override
protected DocumentBuilder createObject() throws ParserConfigurationException {
DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance();
dfactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, Boolean.TRUE);
if (disAllowDocTypeDeclarations) {
dfactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
}
dfactory.setNamespaceAware(true);
return new DocumentBuilderProxy(dfactory.newDocumentBuilder(), disAllowDocTypeDeclarations);
}
}
/**
* We need this proxy wrapping DocumentBuilder to record the value
* passed to disAllowDoctypeDeclarations. It's needed to figure out
* on which pool to return.
*/
private static class DocumentBuilderProxy extends DocumentBuilder {
private final DocumentBuilder delegate;
private final boolean disAllowDocTypeDeclarations;
private DocumentBuilderProxy(DocumentBuilder actual, boolean disAllowDocTypeDeclarations) {
delegate = actual;
this.disAllowDocTypeDeclarations = disAllowDocTypeDeclarations;
}
boolean disAllowDocTypeDeclarations() {
return disAllowDocTypeDeclarations;
}
public void reset() {
delegate.reset();
}
public Document parse(InputStream is) throws SAXException, IOException {
return delegate.parse(is);
}
public Document parse(InputStream is, String systemId) throws SAXException, IOException {
return delegate.parse(is, systemId);
}
public Document parse(String uri) throws SAXException, IOException {
return delegate.parse(uri);
}
public Document parse(File f) throws SAXException, IOException {
return delegate.parse(f);
}
public Schema getSchema() {
return delegate.getSchema();
}
public boolean isXIncludeAware() {
return delegate.isXIncludeAware();
}
@Override
public Document parse(InputSource is) throws SAXException, IOException {
return delegate.parse(is);
}
@Override
public boolean isNamespaceAware() {
return delegate.isNamespaceAware();
}
@Override
public boolean isValidating() {
return delegate.isValidating();
}
@Override
public void setEntityResolver(EntityResolver er) {
delegate.setEntityResolver(er);
}
@Override
public void setErrorHandler(ErrorHandler eh) {
delegate.setErrorHandler(eh);
}
@Override
public Document newDocument() {
return delegate.newDocument();
}
@Override
public DOMImplementation getDOMImplementation() {
return delegate.getDOMImplementation();
}
}
/**
* Abstract base class for pooling objects. The two public methods are
* {@link #getObject()} and ({@link #repool(Object)}. Objects are held through
* weak references so even objects that are not repooled are subject to garbage collection.
* <p>
* Subclasses must implement the abstract {@link #createObject()}.
* <p>
* <p>
* Internally, the pool is stored in a java.util.concurrent.LinkedBlockingDeque
* instance.
*
* @apiNote This class is no longer in use in Santuario 2.1.4, no longer use.
*/
public abstract static class WeakObjectPool<T, E extends Throwable> {
private static final Integer MARKER_VALUE = Integer.MAX_VALUE;//once here rather than auto-box it?
/**
* created, available objects to be checked out to clients
*/
private final BlockingQueue<WeakReference<T>> available;
/**
* Synchronized, identity map of loaned out objects (WeakHashMap);
* use to ensure we repool only object originating from here
* and do it once.
*/
private final Map<T, Integer> onLoan;
/**
* The lone constructor.
*/
protected WeakObjectPool() {
//alternative implementations: ArrayBlockingQueue has a fixed size
// PriorityBlockingQueue: requires a dummy comparator; less memory but more overhead
available = new LinkedBlockingDeque<>();
this.onLoan = Collections.synchronizedMap(new WeakHashMap<>());
}
/**
* Called whenever a new pool object is desired; subclasses must implement.
*
* @return object of the type desired by the subclass
* @throws E Throwable's subclass
*/
protected abstract T createObject() throws E;
/**
* Subclasses can subclass to return a more specific type.
*
* @return an object from the pool; will block until an object is available
* @throws E
*/
public T getObject() throws E {
WeakReference<T> ref;
T retValue = null;
do {
//remove any stale entries as well
ref = available.poll();
} while (ref != null && (retValue = ref.get()) == null);
if (retValue == null) {
//empty pool; create & add new one
retValue = createObject();
}
onLoan.put(retValue, MARKER_VALUE);
return retValue;
}
/**
* Adds the given object to the pool, provided that the object
* was created by this pool.
*
* @param obj the object to return to the pool
* @return whether the object was successfully added as available
*/
public boolean repool(T obj) {
if (obj != null && onLoan.containsKey(obj)) {
//synchronize to protect against a caller returning the same object again...
synchronized (obj) {
//...and check to see that it was removed
if (onLoan.remove(obj) != null) {
return available.offer(new WeakReference<T>(obj));
}
}
}
return false;
}
}
}
+30
View File
@@ -0,0 +1,30 @@
server:
port: 8080
shutdown: graceful
spring:
profiles:
active: dev
jackson:
time-zone: GMT+8
date-format: yyyy-MM-dd HH:mm:ss
mvc:
pathmatch:
matching-strategy: ant_path_matcher
format:
date-time: yyyy-MM-dd HH:mm:ss
date: yyyy-MM-dd
time: HH:mm:ss
#电子口岸u-key的配置参数
eport:
signature:
ukey:
#u-key的.cer证书在classpath的路径
cert-path: cert/01691fe9.cer
ws-url: ws://127.0.0.1:61232
password: 88888888
+3
View File
@@ -0,0 +1,3 @@
spring:
application:
name: chinaport-data-signature
+29
View File
@@ -0,0 +1,29 @@
-----BEGIN CERTIFICATE-----
MIIE+TCCBGKgAwIBAgIEAWkf6TANBgkqhkiG9w0BAQUFADB8MQswCQYDVQQGEwJj
DTALBgNVBAgeBFMXTqwxITAfBgNVBAMeGE4tVv11NVtQThpSoYvBTmZ7oXQGTi1f
bjEVMBMGA1UECh4MTi1W/XU1W1BT41y4MRUwEwYDVQQLHgyLwU5me6F0Bk4tX8Mx
MBwxDTALBgNVBAMeBH8qdDMxCzAJBgNVBBIeAgAxMIGfMA0GCSqGSIb3DQEBAQUA
wzENMAsGA1UEBx4EUxdOrDAeFw0yMTA1MzEwMDAwMDBaFw0zMTA1MzEwMDAwMDBa
o4IC5jCCAuIwCwYDVR0PBAQDAgbAMAkGA1UdEwQCMAAwgacGA1UdIwSBnzCBnIAU
zZlK6AkZMDYJBM2/IJGI0QjWjBJVZ8hEUkgJb4UOOBCBM+cCGFCNY5LX/mAo5Boe
A4GNADCBiQKBgQDt9T7JOQikKKL7oY7RWbRMp7y2VsrXIbFzSDh5DZQPimduUijZ
xgG4Kdr0TwxAQ+s3H85fCU81ROgqAS05/IzOR7eDSEYYT9CRmNYXIRTPVwIDAQAB
Ch4MTi1W/XU1W1BT41y4MRUwEwYDVQQLHgyLwU5me6F0Bk4tX8MxDTALBgNVBAge
+XWjeEULQmjCBFOr68NOPlR4dGChgYCkfjB8MQswCQYDVQQGEwJjbjEVMBMGA1UE
BFMXTqwxITAfBgNVBAMeGE4tVv11NVtQThpSoYvBTmZ7oXQGTi1fwzENMAsGA1UE
Bx4EUxdOrIIBADAdBgNVHQ4EFgQU1eAz50lwZ6gidtkJNPIr7O0jKvUwQgYDVR0g
YXBvcnQuZ292LmNuOjM4OS8wMDAtMS5jcmwwNaAzoDGGL2h0dHA6Ly9sZGFwLmNo
BDswOTA3BgYrgQcBAQIwLTArBggrBgEFBQcCARYfaHR0cDovL2Nwcy5jaGluYXBv
aW5hcG9ydC5nb3YuY246ODA4OC9kemthMDAwLTEuY3JsMG0GCCsGAQUFBwEBBGEw
cnQuZ292LmNuL0NQUzByBgNVHR8EazBpMDCgLqAshipsZGFwOi8vbGRhcC5jaGlu
LzAtBggrBgEFBQcwAYYhaHR0cDovL29jc3AuY2hpbmFwb3J0Lmdvdi5jbjo4MDg4
XzAuBggrBgEFBQcwAYYiaHR0cDovL29jc3AuY2hpbmFwb3J0Lmdvdi5jbjo4ODAw
MCoGCisGAQQBqUNkBQEEHBYauqPEz9DHtLS7pcGqzfjSvdKp09DP3rmry74wEgYK
MzIxNDAdBgorBgEEAalDZAUJBA8WDUpKMEc5MDAxNTkyNzIwGQYKKwYBBAGpQ2QF
KwYBBAGpQ2QFAwQEFgIwMTAiBgorBgEEAalDZAUIBBQWEjMxMDEwMjE5ODAwNzI0
CwQLFglNQTVUTFVBNTMwEgYKKwYBBAGpQ2QFDAQEFgIwMDASBgorBgEEAalDZAIE
BAQWAjE0MBIGCisGAQQBqUNkAgEEBBYCMTIwDQYJKoZIhvcNAQEFBQADgYEAqSuO
MxAzM4bXvdlDcE6fODsCvQMFKctlA+LCllFQwl58HaBmcWx4T/ddKF9LBYc8A986
LlcUw6Mkwxraj2WO+meXdDzRLEO8t3gyZk7tYp5aneV4zGYUsphwyMdLt8N8o4kV
gg16bQy43XgA1jRMv8nvhb908IqQQBxwv0SIuXU=
-----END CERTIFICATE-----
+35
View File
@@ -0,0 +1,35 @@
{
"serverUrl": "http://localhost:8080",
"isStrict": false,
"allInOne": true,
"outPath": "src/main/resources/static",
"coverOld": true,
"createDebugPage": true,
"packageFilters": "cn.alphahub.*.controller.rpc.*Controller",
"md5EncryptedHtmlName": false,
"style": "xt256",
"projectName": "中国电子口岸[海口海关CEBXxxMessage.xml]加签后端接口文档",
"skipTransientField": true,
"sortByTitle": false,
"showAuthor": true,
"requestFieldToUnderline": false,
"responseFieldToUnderline": false,
"inlineEnum": true,
"recursionLimit": 7,
"allInOneDocFileName": "index.html",
"requestExample": "true",
"responseExample": "true",
"displayActualType": false,
"ignoreRequestParams": [
"org.springframework.ui.ModelMap"
],
"revisionLogs": [
{
"version": "1.0",
"revisionTime": "2022-02-12 10:30",
"status": "创建",
"author": "Weasley J",
"remarks": "创建api文档"
}
]
}
+13
View File
@@ -0,0 +1,13 @@
article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,video{display:inline-block}audio:not([controls]){display:none;height:0}[hidden],template{display:none}script{display:none!important}html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}a{background:transparent}a:focus{outline:thin dotted}a:active,a:hover{outline:0}h1{font-size:2em;margin:.67em 0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}dfn{font-style:italic}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}mark{background:#ff0;color:#000}code,kbd,pre,samp{font-family:monospace;font-size:1em}pre{white-space:pre-wrap}q{quotes:"\201C" "\201D" "\2018" "\2019"}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:0}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}button,input,select,textarea{font-family:inherit;font-size:100%;margin:0}button,input{line-height:normal}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}textarea{overflow:auto;vertical-align:top}table{border-collapse:collapse;border-spacing:0}*,*:before,*:after{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}html,body{font-size:100%}body{background:#fff;color:rgba(0,0,0,.8);padding:0;margin:0;font-family:"Noto Serif","DejaVu Serif",serif;font-weight:400;font-style:normal;line-height:1;position:relative;cursor:auto;tab-size:4;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased}a:hover{cursor:pointer}img,object,embed{max-width:100%;height:auto}object,embed{height:100%}img{-ms-interpolation-mode:bicubic}.left{float:left!important}.right{float:right!important}.text-left{text-align:left!important}.text-right{text-align:right!important}.text-center{text-align:center!important}.text-justify{text-align:justify!important}.hide{display:none}img,object,svg{display:inline-block;vertical-align:middle}textarea{height:auto;min-height:50px}select{width:100%}.center{margin-left:auto;margin-right:auto}.spread{width:100%}p.lead,.paragraph.lead>p,#preamble>.sectionbody>.paragraph:first-of-type p{font-size:1.21875em;line-height:1.6}.subheader,.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{line-height:1.45;color:#7a2518;font-weight:400;margin-top:0;margin-bottom:.25em}div,dl,dt,dd,ul,ol,li,h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6,pre,form,p,blockquote,th,td{margin:0;padding:0;direction:ltr}a{color:#364149;text-decoration:underline;line-height:inherit}a:hover,a:focus{color:#364149}a img{border:0}p{font-family:inherit;font-weight:400;font-size:1em;line-height:1.6;margin-bottom:1.25em;text-rendering:optimizeLegibility}p aside{font-size:.875em;line-height:1.35;font-style:italic}h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{font-family:"Open Sans","DejaVu Sans",sans-serif;font-weight:300;font-style:normal;color:#ba3925;text-rendering:optimizeLegibility;margin-top:1em;margin-bottom:.5em;line-height:1.0125em}h1 small,h2 small,h3 small,#toctitle small,.sidebarblock>.content>.title small,h4 small,h5 small,h6 small{font-size:60%;color:#e99b8f;line-height:0}h1{font-size:2.125em}h2{font-size:1.6875em}h3,#toctitle,.sidebarblock>.content>.title{font-size:1.375em}h4,h5{font-size:1.125em}h6{font-size:1em}hr{border:solid #ddddd8;border-width:1px 0 0;clear:both;margin:1.25em 0 1.1875em;height:0}em,i{font-style:italic;line-height:inherit}strong,b{font-weight:bold;line-height:inherit}small{font-size:60%;line-height:inherit}code{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;color:rgba(0,0,0,.9)}
ul,ol,dl{font-size:1em;line-height:1.6;margin-bottom:1.25em;list-style-position:outside;font-family:inherit}ul,ol{margin-left:1.5em}ul li ul,ul li ol{margin-left:1.25em;margin-bottom:0;font-size:1em}ul.square li ul,ul.circle li ul,ul.disc li ul{list-style:inherit}ul.square{list-style-type:square}ul.circle{list-style-type:circle}ul.disc{list-style-type:disc}ol li ul,ol li ol{margin-left:1.25em;margin-bottom:0}dl dt{margin-bottom:.3125em;font-weight:bold}dl dd{margin-bottom:1.25em}abbr,acronym{text-transform:uppercase;font-size:90%;color:rgba(0,0,0,.8);border-bottom:1px dotted #ddd;cursor:help}abbr{text-transform:none}blockquote{margin:0 0 1.25em;padding:.5625em 1.25em 0 1.1875em;border-left:1px solid #ddd}blockquote cite{display:block;font-size:.9375em;color:rgba(0,0,0,.6)}blockquote cite:before{content:"\2014 \0020"}blockquote cite a,blockquote cite a:visited{color:rgba(0,0,0,.6)}blockquote,blockquote p{line-height:1.6;color:rgba(0,0,0,.85)}@media only screen and (min-width:768px){h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2}h1{font-size:2.75em}h2{font-size:2.3125em}h3,#toctitle,.sidebarblock>.content>.title{font-size:1.6875em}h4{font-size:1.4375em}}table{background:#fff;margin-bottom:1.25em;border:solid 1px #dedede}table thead,table tfoot{background:#f7f8f7;font-weight:bold}table thead tr th,table thead tr td,table tfoot tr th,table tfoot tr td{padding:.5em .625em .625em;font-size:inherit;color:rgba(0,0,0,.8);text-align:left}table tr th,table tr td{padding:.5625em .625em;font-size:inherit;color:rgba(0,0,0,.8)}table tr.even,table tr.alt,table tr:nth-of-type(even){background:#f8f8f7}table thead tr th,table tfoot tr th,table tbody tr td,table tr td,table tfoot tr td{display:table-cell;line-height:1.6}h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2;word-spacing:-.05em}h1 strong,h2 strong,h3 strong,#toctitle strong,.sidebarblock>.content>.title strong,h4 strong,h5 strong,h6 strong{font-weight:400}.clearfix:before,.clearfix:after,.float-group:before,.float-group:after{content:" ";display:table}.clearfix:after,.float-group:after{clear:both}*:not(pre)>code{font-size:.9375em;font-style:normal!important;letter-spacing:0;padding:.1em .5ex;word-spacing:-.15em;background-color:#f7f7f8;-webkit-border-radius:4px;border-radius:4px;line-height:1.45;text-rendering:optimizeSpeed;word-wrap:break-word}*:not(pre)>code.nobreak{word-wrap:normal}*:not(pre)>code.nowrap{white-space:nowrap}pre,pre>code{line-height:1.45;color:rgba(0,0,0,.9);font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;text-rendering:optimizeSpeed}em em{font-style:normal}strong strong{font-weight:400}.keyseq{color:rgba(51,51,51,.8)}kbd{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;display:inline-block;color:rgba(0,0,0,.8);font-size:.65em;line-height:1.45;background-color:#f7f7f7;border:1px solid #ccc;-webkit-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em white inset;box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em #fff inset;margin:0 .15em;padding:.2em .5em;vertical-align:middle;position:relative;top:-.1em;white-space:nowrap}.keyseq kbd:first-child{margin-left:0}.keyseq kbd:last-child{margin-right:0}.menuseq,.menuref{color:#000}.menuseq b:not(.caret),.menuref{font-weight:inherit}.menuseq{word-spacing:-.02em}.menuseq b.caret{font-size:1.25em;line-height:.8}.menuseq i.caret{font-weight:bold;text-align:center;width:.45em}b.button:before,b.button:after{position:relative;top:-1px;font-weight:400}b.button:before{content:"[";padding:0 3px 0 2px}b.button:after{content:"]";padding:0 2px 0 3px}p a>code:hover{color:rgba(0,0,0,.9)}#header,#content,#footnotes,#footer{width:100%;margin-left:auto;margin-right:auto;margin-top:0;margin-bottom:0;max-width:62.5em;*zoom:1;position:relative;padding-left:.9375em;padding-right:.9375em}#header:before,#header:after,#content:before,#content:after,#footnotes:before,#footnotes:after,#footer:before,#footer:after{content:" ";display:table}#header:after,#content:after,#footnotes:after,#footer:after{clear:both}#content{margin-top:1.25em}#content:before{content:none}#header>h1:first-child{color:rgba(0,0,0,.85);margin-top:2.25rem;margin-bottom:0}#header>h1:first-child+#toc{margin-top:8px;border-top:1px solid #ddddd8}#header>h1:only-child,body.toc2 #header>h1:nth-last-child(2){border-bottom:1px solid #ddddd8;padding-bottom:8px}#header .details{border-bottom:1px solid #ddddd8;line-height:1.45;padding-top:.25em;padding-bottom:.25em;padding-left:.25em;color:rgba(0,0,0,.6);display:-ms-flexbox;display:-webkit-flex;display:flex;-ms-flex-flow:row wrap;-webkit-flex-flow:row wrap;flex-flow:row wrap}#header .details span:first-child{margin-left:-.125em}#header .details span.email a{color:rgba(0,0,0,.85)}#header .details br{display:none}
#header .details br+span:before{content:"\00a0\2013\00a0"}#header .details br+span.author:before{content:"\00a0\22c5\00a0";color:rgba(0,0,0,.85)}#header .details br+span#revremark:before{content:"\00a0|\00a0"}#header #revnumber{text-transform:capitalize}#header #revnumber:after{content:"\00a0"}#content>h1:first-child:not([class]){color:rgba(0,0,0,.85);border-bottom:1px solid #ddddd8;padding-bottom:8px;margin-top:0;padding-top:1rem;margin-bottom:1.25rem}#toc{border-bottom:1px solid #efefed;padding-bottom:.5em}#toc>ul{margin-left:.125em;padding-left:1.25em}#toc ul.sectlevel0>li>a{font-style:italic}#toc ul.sectlevel0 ul.sectlevel1{margin:.5em 0}#toc ul{font-family:"Open Sans","DejaVu Sans",sans-serif;list-style-type:none}#toc li{line-height:1.3334;margin-top:.3334em;padding-bottom:4px;padding-top:4px}#toc a{text-decoration:none}#toc a:active{text-decoration:underline}#toctitle{color:#7a2518;font-size:1.2em}@media only screen and (min-width:768px){#toctitle{font-size:1.375em}body.toc2{padding-left:15em;padding-right:0}#toc.toc2{margin-top:0!important;background-color:#f8f8f7;position:fixed;width:15em;left:0;top:0;border-right:1px solid #efefed;border-top-width:0!important;border-bottom-width:0!important;z-index:1000;#padding:1.25em 1em;height:100%;overflow:auto}#toc.toc2 #toctitle{margin-top:0;margin-bottom:.8rem;font-size:1.2em}#toc.toc2>ul{font-size:.9em;margin-bottom:0}#toc.toc2 ul ul{margin-left:0;padding-left:1em}#toc.toc2 ul.sectlevel0 ul.sectlevel1{padding-left:0;margin-top:.5em;margin-bottom:.5em}body.toc2.toc-right{padding-left:0;padding-right:15em}body.toc2.toc-right #toc.toc2{border-right-width:0;border-left:1px solid #efefed;left:auto;right:0}}@media only screen and (min-width:1280px){body.toc2{padding-left:20em;padding-right:0}#toc.toc2{width:20em}#toc.toc2 #toctitle{border-bottom:1px solid rgba(0,0,0,.07);padding-top:20px;padding-bottom:15px}#toc.toc2 #toctitle span{padding-left:1.25em;padding-bottom:15px}#toc.toc2>ul{font-size:.95em}#toc.toc2 ul ul{padding-left:1.25em}body.toc2.toc-right{padding-left:0;padding-right:20em}}#content #toc{border-style:solid;border-width:1px;border-color:#e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;-webkit-border-radius:4px;border-radius:4px}#content #toc>:first-child{margin-top:0}#content #toc>:last-child{margin-bottom:0}#footer{max-width:100%;background-color:rgba(0,0,0,.8);padding:1.25em}#footer-text{color:rgba(255,255,255,.8);line-height:1.44}.sect1{padding-bottom:.625em}@media only screen and (min-width:768px){.sect1{padding-bottom:1.25em}}.sect1+.sect1{border-top:1px solid #efefed}#content h1>a.anchor,h2>a.anchor,h3>a.anchor,#toctitle>a.anchor,.sidebarblock>.content>.title>a.anchor,h4>a.anchor,h5>a.anchor,h6>a.anchor{position:absolute;z-index:1001;width:1.5ex;margin-left:-1.5ex;display:block;text-decoration:none!important;visibility:hidden;text-align:center;font-weight:400}#content h1>a.anchor:before,h2>a.anchor:before,h3>a.anchor:before,#toctitle>a.anchor:before,.sidebarblock>.content>.title>a.anchor:before,h4>a.anchor:before,h5>a.anchor:before,h6>a.anchor:before{content:"\00A7";font-size:.85em;display:block;padding-top:.1em}#content h1:hover>a.anchor,#content h1>a.anchor:hover,h2:hover>a.anchor,h2>a.anchor:hover,h3:hover>a.anchor,#toctitle:hover>a.anchor,.sidebarblock>.content>.title:hover>a.anchor,h3>a.anchor:hover,#toctitle>a.anchor:hover,.sidebarblock>.content>.title>a.anchor:hover,h4:hover>a.anchor,h4>a.anchor:hover,h5:hover>a.anchor,h5>a.anchor:hover,h6:hover>a.anchor,h6>a.anchor:hover{visibility:visible}#content h1>a.link,h2>a.link,h3>a.link,#toctitle>a.link,.sidebarblock>.content>.title>a.link,h4>a.link,h5>a.link,h6>a.link{color:#ba3925;text-decoration:none}#content h1>a.link:hover,h2>a.link:hover,h3>a.link:hover,#toctitle>a.link:hover,.sidebarblock>.content>.title>a.link:hover,h4>a.link:hover,h5>a.link:hover,h6>a.link:hover{color:#a53221}.audioblock,.imageblock,.literalblock,.listingblock,.stemblock,.videoblock{margin-bottom:1.25em}.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{text-rendering:optimizeLegibility;text-align:left;font-family:"Noto Serif","DejaVu Serif",serif;font-size:1rem;font-style:italic}table.tableblock>caption.title{white-space:nowrap;overflow:visible;max-width:0}.paragraph.lead>p,#preamble>.sectionbody>.paragraph:first-of-type p{color:rgba(0,0,0,.85)}table.tableblock #preamble>.sectionbody>.paragraph:first-of-type p{font-size:inherit}.admonitionblock>table{border-collapse:separate;border:0;background:0;width:100%}.admonitionblock>table td.icon{text-align:center;width:80px}
.admonitionblock>table td.icon img{max-width:initial}.admonitionblock>table td.icon .title{font-weight:bold;font-family:"Open Sans","DejaVu Sans",sans-serif;text-transform:uppercase}.admonitionblock>table td.content{padding-left:1.125em;padding-right:1.25em;border-left:1px solid #ddddd8;color:rgba(0,0,0,.6)}.admonitionblock>table td.content>:last-child>:last-child{margin-bottom:0}.exampleblock>.content{border-style:solid;border-width:1px;border-color:#e6e6e6;margin-bottom:1.25em;padding:1.25em;background:#fff;-webkit-border-radius:4px;border-radius:4px}.exampleblock>.content>:first-child{margin-top:0}.exampleblock>.content>:last-child{margin-bottom:0}.sidebarblock{border-style:solid;border-width:1px;border-color:#e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;-webkit-border-radius:4px;border-radius:4px}.sidebarblock>:first-child{margin-top:0}.sidebarblock>:last-child{margin-bottom:0}.sidebarblock>.content>.title{color:#7a2518;margin-top:0;text-align:center}.exampleblock>.content>:last-child>:last-child,.exampleblock>.content .olist>ol>li:last-child>:last-child,.exampleblock>.content .ulist>ul>li:last-child>:last-child,.exampleblock>.content .qlist>ol>li:last-child>:last-child,.sidebarblock>.content>:last-child>:last-child,.sidebarblock>.content .olist>ol>li:last-child>:last-child,.sidebarblock>.content .ulist>ul>li:last-child>:last-child,.sidebarblock>.content .qlist>ol>li:last-child>:last-child{margin-bottom:0}.literalblock pre,.listingblock pre:not(.highlight),.listingblock pre[class="highlight"],.listingblock pre[class^="highlight "],.listingblock pre.CodeRay,.listingblock pre.prettyprint{background:#f7f7f8}.sidebarblock .literalblock pre,.sidebarblock .listingblock pre:not(.highlight),.sidebarblock .listingblock pre[class="highlight"],.sidebarblock .listingblock pre[class^="highlight "],.sidebarblock .listingblock pre.CodeRay,.sidebarblock .listingblock pre.prettyprint{background:#f2f1f1}.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{-webkit-border-radius:4px;border-radius:4px;word-wrap:break-word;padding:1em;font-size:.8125em}.literalblock pre.nowrap,.literalblock pre[class].nowrap,.listingblock pre.nowrap,.listingblock pre[class].nowrap{overflow-x:auto;white-space:pre;word-wrap:normal}@media only screen and (min-width:768px){.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{font-size:.90625em}}@media only screen and (min-width:1280px){.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{font-size:1em}}.literalblock.output pre{color:#f7f7f8;background-color:rgba(0,0,0,.9)}.listingblock pre.highlightjs{padding:0}.listingblock pre.highlightjs>code{padding:1em;-webkit-border-radius:4px;border-radius:4px}.listingblock pre.prettyprint{border-width:0}.listingblock>.content{position:relative}.listingblock code[data-lang]:before{display:none;content:attr(data-lang);position:absolute;font-size:.75em;top:.425rem;right:.5rem;line-height:1;text-transform:uppercase;color:#999}.listingblock:hover code[data-lang]:before{display:block}.listingblock.terminal pre .command:before{content:attr(data-prompt);padding-right:.5em;color:#999}.listingblock.terminal pre .command:not([data-prompt]):before{content:"$"}table.pyhltable{border-collapse:separate;border:0;margin-bottom:0;background:0}table.pyhltable td{vertical-align:top;padding-top:0;padding-bottom:0;line-height:1.45}table.pyhltable td.code{padding-left:.75em;padding-right:0}pre.pygments .lineno,table.pyhltable td:not(.code){color:#999;padding-left:0;padding-right:.5em;border-right:1px solid #ddddd8}pre.pygments .lineno{display:inline-block;margin-right:.25em}table.pyhltable .linenodiv{background:none!important;padding-right:0!important}.quoteblock{margin:0 1em 1.25em 1.5em;display:table}.quoteblock>.title{margin-left:-1.5em;margin-bottom:.75em}.quoteblock blockquote,.quoteblock blockquote p{color:rgba(0,0,0,.85);font-size:1.15rem;line-height:1.75;word-spacing:.1em;letter-spacing:0;font-style:italic;text-align:justify}.quoteblock blockquote{margin:0;padding:0;border:0}.quoteblock blockquote:before{content:"\201c";float:left;font-size:2.75em;font-weight:bold;line-height:.6em;margin-left:-.6em;color:#7a2518;text-shadow:0 1px 2px rgba(0,0,0,.1)}.quoteblock blockquote>.paragraph:last-child p{margin-bottom:0}.quoteblock .attribution{margin-top:.5em;margin-right:.5ex;text-align:right}.quoteblock .quoteblock{margin-left:0;margin-right:0;padding:.5em 0;border-left:3px solid rgba(0,0,0,.6)}.quoteblock .quoteblock blockquote{padding:0 0 0 .75em}.quoteblock .quoteblock blockquote:before{display:none}.verseblock{margin:0 1em 1.25em 1em}.verseblock pre{font-family:"Open Sans","DejaVu Sans",sans;font-size:1.15rem;color:rgba(0,0,0,.85);font-weight:300;text-rendering:optimizeLegibility}
.verseblock pre strong{font-weight:400}.verseblock .attribution{margin-top:1.25rem;margin-left:.5ex}.quoteblock .attribution,.verseblock .attribution{font-size:.9375em;line-height:1.45;font-style:italic}.quoteblock .attribution br,.verseblock .attribution br{display:none}.quoteblock .attribution cite,.verseblock .attribution cite{display:block;letter-spacing:-.025em;color:rgba(0,0,0,.6)}.quoteblock.abstract{margin:0 0 1.25em 0;display:block}.quoteblock.abstract blockquote,.quoteblock.abstract blockquote p{text-align:left;word-spacing:0}.quoteblock.abstract blockquote:before,.quoteblock.abstract blockquote p:first-of-type:before{display:none}table.tableblock{max-width:100%;border-collapse:separate}table.tableblock td>.paragraph:last-child p>p:last-child,table.tableblock th>p:last-child,table.tableblock td>p:last-child{margin-bottom:0}table.tableblock,th.tableblock,td.tableblock{border:0 solid #dedede}table.grid-all>thead>tr>.tableblock,table.grid-all>tbody>tr>.tableblock{border-width:0 1px 1px 0}table.grid-all>tfoot>tr>.tableblock{border-width:1px 1px 0 0}table.grid-cols>*>tr>.tableblock{border-width:0 1px 0 0}table.grid-rows>thead>tr>.tableblock,table.grid-rows>tbody>tr>.tableblock{border-width:0 0 1px 0}table.grid-rows>tfoot>tr>.tableblock{border-width:1px 0 0 0}table.grid-all>*>tr>.tableblock:last-child,table.grid-cols>*>tr>.tableblock:last-child{border-right-width:0}table.grid-all>tbody>tr:last-child>.tableblock,table.grid-all>thead:last-child>tr>.tableblock,table.grid-rows>tbody>tr:last-child>.tableblock,table.grid-rows>thead:last-child>tr>.tableblock{border-bottom-width:0}table.frame-all{border-width:1px}table.frame-sides{border-width:0 1px}table.frame-topbot{border-width:1px 0}th.halign-left,td.halign-left{text-align:left}th.halign-right,td.halign-right{text-align:right}th.halign-center,td.halign-center{text-align:center}th.valign-top,td.valign-top{vertical-align:top}th.valign-bottom,td.valign-bottom{vertical-align:bottom}th.valign-middle,td.valign-middle{vertical-align:middle}table thead th,table tfoot th{font-weight:bold}tbody tr th{display:table-cell;line-height:1.6;background:#f7f8f7}tbody tr th,tbody tr th p,tfoot tr th,tfoot tr th p{color:rgba(0,0,0,.8);font-weight:bold}p.tableblock>code:only-child{background:0;padding:0}p.tableblock{font-size:1em}td>div.verse{white-space:pre}ol{margin-left:1.75em}ul li ol{margin-left:1.5em}dl dd{margin-left:1.125em}dl dd:last-child,dl dd:last-child>:last-child{margin-bottom:0}ol>li p,ul>li p,ul dd,ol dd,.olist .olist,.ulist .ulist,.ulist .olist,.olist .ulist{margin-bottom:.625em}ul.checklist,ul.none,ol.none,ul.no-bullet,ol.no-bullet,ol.unnumbered,ul.unstyled,ol.unstyled{list-style-type:none}ul.no-bullet,ol.no-bullet,ol.unnumbered{margin-left:.625em}ul.unstyled,ol.unstyled{margin-left:0}ul.checklist{margin-left:.625em}ul.checklist li>p:first-child>.fa-square-o:first-child,ul.checklist li>p:first-child>.fa-check-square-o:first-child{width:1.25em;font-size:.8em;position:relative;bottom:.125em}ul.checklist li>p:first-child>input[type="checkbox"]:first-child{margin-right:.25em}ul.inline{margin:0 auto .625em auto;margin-left:-1.375em;margin-right:0;padding:0;list-style:none;overflow:hidden}ul.inline>li{list-style:none;float:left;margin-left:1.375em;display:block}ul.inline>li>*{display:block}.unstyled dl dt{font-weight:400;font-style:normal}ol.arabic{list-style-type:decimal}ol.decimal{list-style-type:decimal-leading-zero}ol.loweralpha{list-style-type:lower-alpha}ol.upperalpha{list-style-type:upper-alpha}ol.lowerroman{list-style-type:lower-roman}ol.upperroman{list-style-type:upper-roman}ol.lowergreek{list-style-type:lower-greek}.hdlist>table,.colist>table{border:0;background:0}.hdlist>table>tbody>tr,.colist>table>tbody>tr{background:0}td.hdlist1,td.hdlist2{vertical-align:top;padding:0 .625em}td.hdlist1{font-weight:bold;padding-bottom:1.25em}.literalblock+.colist,.listingblock+.colist{margin-top:-.5em}.colist>table tr>td:first-of-type{padding:.4em .75em 0 .75em;line-height:1;vertical-align:top}.colist>table tr>td:first-of-type img{max-width:initial}.colist>table tr>td:last-of-type{padding:.25em 0}.thumb,.th{line-height:0;display:inline-block;border:solid 4px #fff;-webkit-box-shadow:0 0 0 1px #ddd;box-shadow:0 0 0 1px #ddd}.imageblock.left,.imageblock[style*="float:left"]{margin:.25em .625em 1.25em 0}.imageblock.right,.imageblock[style*="float:right"]{margin:.25em 0 1.25em .625em}.imageblock>.title{margin-bottom:0}.imageblock.thumb,.imageblock.th{border-width:6px}.imageblock.thumb>.title,.imageblock.th>.title{padding:0 .125em}.image.left,.image.right{margin-top:.25em;margin-bottom:.25em;display:inline-block;line-height:0}.image.left{margin-right:.625em}.image.right{margin-left:.625em}a.image{text-decoration:none;display:inline-block}a.image object{pointer-events:none}sup.footnote,sup.footnoteref{font-size:.875em;position:static;vertical-align:super}sup.footnote a,sup.footnoteref a{text-decoration:none}
sup.footnote a:active,sup.footnoteref a:active{text-decoration:underline}#footnotes{padding-top:.75em;padding-bottom:.75em;margin-bottom:.625em}#footnotes hr{width:20%;min-width:6.25em;margin:-.25em 0 .75em 0;border-width:1px 0 0 0}#footnotes .footnote{padding:0 .375em 0 .225em;line-height:1.3334;font-size:.875em;margin-left:1.2em;text-indent:-1.05em;margin-bottom:.2em}#footnotes .footnote a:first-of-type{font-weight:bold;text-decoration:none}#footnotes .footnote:last-of-type{margin-bottom:0}#content #footnotes{margin-top:-.625em;margin-bottom:0;padding:.75em 0}.gist .file-data>table{border:0;background:#fff;width:100%;margin-bottom:0}.gist .file-data>table td.line-data{width:99%}div.unbreakable{page-break-inside:avoid}.big{font-size:larger}.small{font-size:smaller}.underline{text-decoration:underline}.overline{text-decoration:overline}.line-through{text-decoration:line-through}.aqua{color:#00bfbf}.aqua-background{background-color:#00fafa}.black{color:#000}.black-background{background-color:#000}.blue{color:#0000bf}.blue-background{background-color:#0000fa}.fuchsia{color:#bf00bf}.fuchsia-background{background-color:#fa00fa}.gray{color:#606060}.gray-background{background-color:#7d7d7d}.green{color:#006000}.green-background{background-color:#007d00}.lime{color:#00bf00}.lime-background{background-color:#00fa00}.maroon{color:#600000}.maroon-background{background-color:#7d0000}.navy{color:#000060}.navy-background{background-color:#00007d}.olive{color:#606000}.olive-background{background-color:#7d7d00}.purple{color:#600060}.purple-background{background-color:#7d007d}.red{color:#bf0000}.red-background{background-color:#fa0000}.silver{color:#909090}.silver-background{background-color:#bcbcbc}.teal{color:#006060}.teal-background{background-color:#007d7d}.white{color:#bfbfbf}.white-background{background-color:#fafafa}.yellow{color:#bfbf00}.yellow-background{background-color:#fafa00}span.icon>.fa{cursor:default}a span.icon>.fa{cursor:inherit}.admonitionblock td.icon [class^="fa icon-"]{font-size:2.5em;text-shadow:1px 1px 2px rgba(0,0,0,.5);cursor:default}.admonitionblock td.icon .icon-note:before{content:"\f05a";color:#19407c}.admonitionblock td.icon .icon-tip:before{content:"\f0eb";text-shadow:1px 1px 2px rgba(155,155,0,.8);color:#111}.admonitionblock td.icon .icon-warning:before{content:"\f071";color:#bf6900}.admonitionblock td.icon .icon-caution:before{content:"\f06d";color:#bf3400}.admonitionblock td.icon .icon-important:before{content:"\f06a";color:#bf0000}.conum[data-value]{display:inline-block;color:#fff!important;background-color:rgba(0,0,0,.8);-webkit-border-radius:100px;border-radius:100px;text-align:center;font-size:.75em;width:1.67em;height:1.67em;line-height:1.67em;font-family:"Open Sans","DejaVu Sans",sans-serif;font-style:normal;font-weight:bold}.conum[data-value] *{color:#fff!important}.conum[data-value]+b{display:none}.conum[data-value]:after{content:attr(data-value)}pre .conum[data-value]{position:relative;top:-.125em}b.conum *{color:inherit!important}.conum:not([data-value]):empty{display:none}dt,th.tableblock,td.content,div.footnote{text-rendering:optimizeLegibility}h1,h2,p,td.content,span.alt{letter-spacing:-.01em}p strong,td.content strong,div.footnote strong{letter-spacing:-.005em}p,blockquote,dt,td.content,span.alt{font-size:1.0625rem}p{margin-bottom:1.25rem}.sidebarblock p,.sidebarblock dt,.sidebarblock td.content,p.tableblock{font-size:1em}.exampleblock>.content{background-color:#fffef7;border-color:#e0e0dc;-webkit-box-shadow:0 1px 4px #e0e0dc;box-shadow:0 1px 4px #e0e0dc}.print-only{display:none!important}@media print{@page{margin:1.25cm .75cm}*{-webkit-box-shadow:none!important;box-shadow:none!important;text-shadow:none!important}a{color:inherit!important;text-decoration:underline!important}a.bare,a[href^="#"],a[href^="mailto:"]{text-decoration:none!important}a[href^="http:"]:not(.bare):after,a[href^="https:"]:not(.bare):after{content:"(" attr(href) ")";display:inline-block;font-size:.875em;padding-left:.25em}abbr[title]:after{content:" (" attr(title) ")"}pre,blockquote,tr,img,object,svg{page-break-inside:avoid}thead{display:table-header-group}svg{max-width:100%}p,blockquote,dt,td.content{font-size:1em;orphans:3;widows:3}h2,h3,#toctitle,.sidebarblock>.content>.title{page-break-after:avoid}#toc,.sidebarblock,.exampleblock>.content{background:none!important}#toc{border-bottom:1px solid #ddddd8!important;padding-bottom:0!important}.sect1{padding-bottom:0!important}.sect1+.sect1{border:0!important}#header>h1:first-child{margin-top:1.25rem}
body.book #header{text-align:center}body.book #header>h1:first-child{border:0!important;margin:2.5em 0 1em 0}body.book #header .details{border:0!important;display:block;padding:0!important}body.book #header .details span:first-child{margin-left:0!important}body.book #header .details br{display:block}body.book #header .details br+span:before{content:none!important}body.book #toc{border:0!important;text-align:left!important;padding:0!important;margin:0!important}body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{page-break-before:always}.listingblock code[data-lang]:before{display:block}#footer{background:none!important;padding:0 .9375em}#footer-text{color:rgba(0,0,0,.6)!important;font-size:.9em}.hide-on-print{display:none!important}.print-only{display:block!important}.hide-for-print{display:none!important}.show-for-print{display:inherit!important}}#content .page-footer{height:100px;border-top:1px solid #ccc;overflow:hidden;padding:10px 0;font-size:14px;color:gray}#content .footer-modification{float:right}#content .footer-modification a{text-decoration:none}.sectlevel2{display:none}.submenu{background:#e7e7e6}.submenu li{border:0}.submenu a{color:#555}.checkbox{position:relative;height:30px}.checkbox input[type='checkbox']{position:absolute;left:0;top:0;width:20px;height:20px;opacity:0;border-radius:4px}.checkbox label{position:absolute;left:30px;top:0;height:20px;line-height:20px}.checkbox label:before{content:'';position:absolute;left:-30px;top:2px;width:20px;height:20px;border:1px solid #ddd;border-radius:4px;transition:all .3s ease;-webkit-transition:all .3s ease;-moz-transition:all .3s ease}.checkbox label:after{content:'';position:absolute;left:-22px;top:3px;width:6px;height:12px;border:0;border-right:1px solid #fff;border-bottom:1px solid #fff;border-radius:2px;transform:rotate(45deg);-webkit-transform:rotate(45deg);-moz-transform:rotate(45deg);-ms-transform:rotate(45deg);transition:all .3s ease;-webkit-transition:all .3s ease;-moz-transition:all .3s ease}.checkbox input[type='checkbox']:checked+label:before{background:#4cd764;border-color:#4cd764}.checkbox input[type='checkbox']:checked+label:after{background:#4cd764}.send-button{color:#fff;background-color:#5cb85c;display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px;outline-color:transparent}textarea{width:100%;background-color:#f7f7f8;border:1px solid #f7f7f8;border-radius:4px;font-size:1em;padding:1em;font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;outline-color:#dedede}input{border:0;background-color:transparent;outline-color:transparent;outline-style:dotted;max-width:100%}#book-search-input{padding:13px;background:0;transition:top .5s ease;border-bottom:1px solid rgba(0,0,0,.07);border-top:1px solid rgba(0,0,0,.07);margin-top:-1px}#book-search-input input,#book-search-input input:focus,#book-search-input input:hover{width:100%;background:0;border:1px solid transparent;box-shadow:none;outline:0;line-height:22px;padding:7px 7px;color:inherit}[contenteditable="plaintext-only"]:focus{border:0;outline:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,video{display:inline-block}audio:not([controls]){display:none;height:0}[hidden],template{display:none}script{display:none!important}html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}a{background:transparent}a:focus{outline:thin dotted}a:active,a:hover{outline:0}h1{font-size:2em;margin:.67em 0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}dfn{font-style:italic}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}mark{background:#ff0;color:#000}code,kbd,pre,samp{font-family:monospace;font-size:1em}pre{white-space:pre-wrap}q{quotes:"\201C" "\201D" "\2018" "\2019"}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:0}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}button,input,select,textarea{font-family:inherit;font-size:100%;margin:0}button,input{line-height:normal}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}
button[disabled],html input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}textarea{overflow:auto;vertical-align:top}table{border-collapse:collapse;border-spacing:0}*,*:before,*:after{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}html,body{font-size:100%}body{background:#fff;color:rgba(0,0,0,.8);padding:0;margin:0;font-family:"Noto Serif","DejaVu Serif",serif;font-weight:400;font-style:normal;line-height:1;position:relative;cursor:auto;tab-size:4;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased}a:hover{cursor:pointer}img,object,embed{max-width:100%;height:auto}object,embed{height:100%}img{-ms-interpolation-mode:bicubic}.left{float:left!important}.right{float:right!important}.text-left{text-align:left!important}.text-right{text-align:right!important}.text-center{text-align:center!important}.text-justify{text-align:justify!important}.hide{display:none}img,object,svg{display:inline-block;vertical-align:middle}textarea{height:auto;min-height:50px}select{width:100%}.center{margin-left:auto;margin-right:auto}.spread{width:100%}p.lead,.paragraph.lead>p,#preamble>.sectionbody>.paragraph:first-of-type p{font-size:1.21875em;line-height:1.6}.subheader,.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{line-height:1.45;color:#7a2518;font-weight:400;margin-top:0;margin-bottom:.25em}div,dl,dt,dd,ul,ol,li,h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6,pre,form,p,blockquote,th,td{margin:0;padding:0;direction:ltr}a{color:#364149;text-decoration:underline;line-height:inherit}a:hover,a:focus{color:#364149}a img{border:0}p{font-family:inherit;font-weight:400;font-size:1em;line-height:1.6;margin-bottom:1.25em;text-rendering:optimizeLegibility}p aside{font-size:.875em;line-height:1.35;font-style:italic}h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{font-family:"Open Sans","DejaVu Sans",sans-serif;font-weight:300;font-style:normal;color:#ba3925;text-rendering:optimizeLegibility;margin-top:1em;margin-bottom:.5em;line-height:1.0125em}h1 small,h2 small,h3 small,#toctitle small,.sidebarblock>.content>.title small,h4 small,h5 small,h6 small{font-size:60%;color:#e99b8f;line-height:0}h1{font-size:2.125em}h2{font-size:1.6875em}h3,#toctitle,.sidebarblock>.content>.title{font-size:1.375em}h4,h5{font-size:1.125em}h6{font-size:1em}hr{border:solid #ddddd8;border-width:1px 0 0;clear:both;margin:1.25em 0 1.1875em;height:0}em,i{font-style:italic;line-height:inherit}strong,b{font-weight:bold;line-height:inherit}small{font-size:60%;line-height:inherit}code{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;color:rgba(0,0,0,.9)}ul,ol,dl{font-size:1em;line-height:1.6;margin-bottom:1.25em;list-style-position:outside;font-family:inherit}ul,ol{margin-left:1.5em}ul li ul,ul li ol{margin-left:1.25em;margin-bottom:0;font-size:1em}ul.square li ul,ul.circle li ul,ul.disc li ul{list-style:inherit}ul.square{list-style-type:square}ul.circle{list-style-type:circle}ul.disc{list-style-type:disc}ol li ul,ol li ol{margin-left:1.25em;margin-bottom:0}dl dt{margin-bottom:.3125em;font-weight:bold}dl dd{margin-bottom:1.25em}abbr,acronym{text-transform:uppercase;font-size:90%;color:rgba(0,0,0,.8);border-bottom:1px dotted #ddd;cursor:help}abbr{text-transform:none}blockquote{margin:0 0 1.25em;padding:.5625em 1.25em 0 1.1875em;border-left:1px solid #ddd}blockquote cite{display:block;font-size:.9375em;color:rgba(0,0,0,.6)}blockquote cite:before{content:"\2014 \0020"}blockquote cite a,blockquote cite a:visited{color:rgba(0,0,0,.6)}blockquote,blockquote p{line-height:1.6;color:rgba(0,0,0,.85)}@media only screen and (min-width:768px){h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2}h1{font-size:2.75em}h2{font-size:2.3125em}h3,#toctitle,.sidebarblock>.content>.title{font-size:1.6875em}h4{font-size:1.4375em}}table{background:#fff;margin-bottom:1.25em;border:solid 1px #dedede}table thead,table tfoot{background:#f7f8f7;font-weight:bold}table thead tr th,table thead tr td,table tfoot tr th,table tfoot tr td{padding:.5em .625em .625em;font-size:inherit;color:rgba(0,0,0,.8);text-align:left}
table tr th,table tr td{padding:.5625em .625em;font-size:inherit;color:rgba(0,0,0,.8)}table tr.even,table tr.alt,table tr:nth-of-type(even){background:#f8f8f7}table thead tr th,table tfoot tr th,table tbody tr td,table tr td,table tfoot tr td{display:table-cell;line-height:1.6}h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2;word-spacing:-.05em}h1 strong,h2 strong,h3 strong,#toctitle strong,.sidebarblock>.content>.title strong,h4 strong,h5 strong,h6 strong{font-weight:400}.clearfix:before,.clearfix:after,.float-group:before,.float-group:after{content:" ";display:table}.clearfix:after,.float-group:after{clear:both}*:not(pre)>code{font-size:.9375em;font-style:normal!important;letter-spacing:0;padding:.1em .5ex;word-spacing:-.15em;background-color:#f7f7f8;-webkit-border-radius:4px;border-radius:4px;line-height:1.45;text-rendering:optimizeSpeed;word-wrap:break-word}*:not(pre)>code.nobreak{word-wrap:normal}*:not(pre)>code.nowrap{white-space:nowrap}pre,pre>code{line-height:1.45;color:rgba(0,0,0,.9);font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;text-rendering:optimizeSpeed}em em{font-style:normal}strong strong{font-weight:400}.keyseq{color:rgba(51,51,51,.8)}kbd{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;display:inline-block;color:rgba(0,0,0,.8);font-size:.65em;line-height:1.45;background-color:#f7f7f7;border:1px solid #ccc;-webkit-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em white inset;box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em #fff inset;margin:0 .15em;padding:.2em .5em;vertical-align:middle;position:relative;top:-.1em;white-space:nowrap}.keyseq kbd:first-child{margin-left:0}.keyseq kbd:last-child{margin-right:0}.menuseq,.menuref{color:#000}.menuseq b:not(.caret),.menuref{font-weight:inherit}.menuseq{word-spacing:-.02em}.menuseq b.caret{font-size:1.25em;line-height:.8}.menuseq i.caret{font-weight:bold;text-align:center;width:.45em}b.button:before,b.button:after{position:relative;top:-1px;font-weight:400}b.button:before{content:"[";padding:0 3px 0 2px}b.button:after{content:"]";padding:0 2px 0 3px}p a>code:hover{color:rgba(0,0,0,.9)}#header,#content,#footnotes,#footer{width:100%;margin-left:auto;margin-right:auto;margin-top:0;margin-bottom:0;max-width:62.5em;*zoom:1;position:relative;padding-left:.9375em;padding-right:.9375em}#header:before,#header:after,#content:before,#content:after,#footnotes:before,#footnotes:after,#footer:before,#footer:after{content:" ";display:table}#header:after,#content:after,#footnotes:after,#footer:after{clear:both}#content{margin-top:1.25em}#content:before{content:none}#header>h1:first-child{color:rgba(0,0,0,.85);margin-top:2.25rem;margin-bottom:0}#header>h1:first-child+#toc{margin-top:8px;border-top:1px solid #ddddd8}#header>h1:only-child,body.toc2 #header>h1:nth-last-child(2){border-bottom:1px solid #ddddd8;padding-bottom:8px}#header .details{border-bottom:1px solid #ddddd8;line-height:1.45;padding-top:.25em;padding-bottom:.25em;padding-left:.25em;color:rgba(0,0,0,.6);display:-ms-flexbox;display:-webkit-flex;display:flex;-ms-flex-flow:row wrap;-webkit-flex-flow:row wrap;flex-flow:row wrap}#header .details span:first-child{margin-left:-.125em}#header .details span.email a{color:rgba(0,0,0,.85)}#header .details br{display:none}#header .details br+span:before{content:"\00a0\2013\00a0"}#header .details br+span.author:before{content:"\00a0\22c5\00a0";color:rgba(0,0,0,.85)}#header .details br+span#revremark:before{content:"\00a0|\00a0"}#header #revnumber{text-transform:capitalize}#header #revnumber:after{content:"\00a0"}#content>h1:first-child:not([class]){color:rgba(0,0,0,.85);border-bottom:1px solid #ddddd8;padding-bottom:8px;margin-top:0;padding-top:1rem;margin-bottom:1.25rem}#toc{border-bottom:1px solid #efefed;padding-bottom:.5em}#toc>ul{margin-left:.125em;padding-left:1.25em}#toc ul.sectlevel0>li>a{font-style:italic}#toc ul.sectlevel0 ul.sectlevel1{margin:.5em 0}#toc ul{font-family:"Open Sans","DejaVu Sans",sans-serif;list-style-type:none}#toc li{line-height:1.3334;margin-top:.3334em;padding-bottom:4px;padding-top:4px}#toc a{text-decoration:none}#toc a:active{text-decoration:underline}#toctitle{color:#7a2518;font-size:1.2em}@media only screen and (min-width:768px){#toctitle{font-size:1.375em}body.toc2{padding-left:15em;padding-right:0}#toc.toc2{margin-top:0!important;background-color:#f8f8f7;position:fixed;width:15em;left:0;top:0;border-right:1px solid #efefed;border-top-width:0!important;border-bottom-width:0!important;z-index:1000;#padding:1.25em 1em;height:100%;overflow:auto}#toc.toc2 #toctitle{margin-top:0;margin-bottom:.8rem;font-size:1.2em}
#toc.toc2>ul{font-size:.9em;margin-bottom:0}#toc.toc2 ul ul{margin-left:0;padding-left:1em}#toc.toc2 ul.sectlevel0 ul.sectlevel1{padding-left:0;margin-top:.5em;margin-bottom:.5em}body.toc2.toc-right{padding-left:0;padding-right:15em}body.toc2.toc-right #toc.toc2{border-right-width:0;border-left:1px solid #efefed;left:auto;right:0}}@media only screen and (min-width:1280px){body.toc2{padding-left:20em;padding-right:0}#toc.toc2{width:20em}#toc.toc2 #toctitle{font-size:1.375em;border-bottom:1px solid rgba(0,0,0,.07);padding-top:20px;padding-bottom:15px}#toc.toc2 #toctitle span{padding-left:1.25em;padding-bottom:15px}#toc.toc2>ul{font-size:.95em}#toc.toc2 ul ul{padding-left:1.25em}body.toc2.toc-right{padding-left:0;padding-right:20em}}#content #toc{border-style:solid;border-width:1px;border-color:#e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;-webkit-border-radius:4px;border-radius:4px}#content #toc>:first-child{margin-top:0}#content #toc>:last-child{margin-bottom:0}#footer{max-width:100%;background-color:rgba(0,0,0,.8);padding:1.25em}#footer-text{color:rgba(255,255,255,.8);line-height:1.44}.sect1{padding-bottom:.625em}@media only screen and (min-width:768px){.sect1{padding-bottom:1.25em}}.sect1+.sect1{border-top:1px solid #efefed}#content h1>a.anchor,h2>a.anchor,h3>a.anchor,#toctitle>a.anchor,.sidebarblock>.content>.title>a.anchor,h4>a.anchor,h5>a.anchor,h6>a.anchor{position:absolute;z-index:1001;width:1.5ex;margin-left:-1.5ex;display:block;text-decoration:none!important;visibility:hidden;text-align:center;font-weight:400}#content h1>a.anchor:before,h2>a.anchor:before,h3>a.anchor:before,#toctitle>a.anchor:before,.sidebarblock>.content>.title>a.anchor:before,h4>a.anchor:before,h5>a.anchor:before,h6>a.anchor:before{content:"\00A7";font-size:.85em;display:block;padding-top:.1em}#content h1:hover>a.anchor,#content h1>a.anchor:hover,h2:hover>a.anchor,h2>a.anchor:hover,h3:hover>a.anchor,#toctitle:hover>a.anchor,.sidebarblock>.content>.title:hover>a.anchor,h3>a.anchor:hover,#toctitle>a.anchor:hover,.sidebarblock>.content>.title>a.anchor:hover,h4:hover>a.anchor,h4>a.anchor:hover,h5:hover>a.anchor,h5>a.anchor:hover,h6:hover>a.anchor,h6>a.anchor:hover{visibility:visible}#content h1>a.link,h2>a.link,h3>a.link,#toctitle>a.link,.sidebarblock>.content>.title>a.link,h4>a.link,h5>a.link,h6>a.link{color:#ba3925;text-decoration:none}#content h1>a.link:hover,h2>a.link:hover,h3>a.link:hover,#toctitle>a.link:hover,.sidebarblock>.content>.title>a.link:hover,h4>a.link:hover,h5>a.link:hover,h6>a.link:hover{color:#a53221}.audioblock,.imageblock,.literalblock,.listingblock,.stemblock,.videoblock{margin-bottom:1.25em}.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{text-rendering:optimizeLegibility;text-align:left;font-family:"Noto Serif","DejaVu Serif",serif;font-size:1rem;font-style:italic}table.tableblock>caption.title{white-space:nowrap;overflow:visible;max-width:0}.paragraph.lead>p,#preamble>.sectionbody>.paragraph:first-of-type p{color:rgba(0,0,0,.85)}table.tableblock #preamble>.sectionbody>.paragraph:first-of-type p{font-size:inherit}.admonitionblock>table{border-collapse:separate;border:0;background:0;width:100%}.admonitionblock>table td.icon{text-align:center;width:80px}.admonitionblock>table td.icon img{max-width:initial}.admonitionblock>table td.icon .title{font-weight:bold;font-family:"Open Sans","DejaVu Sans",sans-serif;text-transform:uppercase}.admonitionblock>table td.content{padding-left:1.125em;padding-right:1.25em;border-left:1px solid #ddddd8;color:rgba(0,0,0,.6)}.admonitionblock>table td.content>:last-child>:last-child{margin-bottom:0}.exampleblock>.content{border-style:solid;border-width:1px;border-color:#e6e6e6;margin-bottom:1.25em;padding:1.25em;background:#fff;-webkit-border-radius:4px;border-radius:4px}.exampleblock>.content>:first-child{margin-top:0}.exampleblock>.content>:last-child{margin-bottom:0}.sidebarblock{border-style:solid;border-width:1px;border-color:#e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;-webkit-border-radius:4px;border-radius:4px}.sidebarblock>:first-child{margin-top:0}.sidebarblock>:last-child{margin-bottom:0}.sidebarblock>.content>.title{color:#7a2518;margin-top:0;text-align:center}.exampleblock>.content>:last-child>:last-child,.exampleblock>.content .olist>ol>li:last-child>:last-child,.exampleblock>.content .ulist>ul>li:last-child>:last-child,.exampleblock>.content .qlist>ol>li:last-child>:last-child,.sidebarblock>.content>:last-child>:last-child,.sidebarblock>.content .olist>ol>li:last-child>:last-child,.sidebarblock>.content .ulist>ul>li:last-child>:last-child,.sidebarblock>.content .qlist>ol>li:last-child>:last-child{margin-bottom:0}
.literalblock pre,.listingblock pre:not(.highlight),.listingblock pre[class="highlight"],.listingblock pre[class^="highlight "],.listingblock pre.CodeRay,.listingblock pre.prettyprint{background:#f7f7f8}.sidebarblock .literalblock pre,.sidebarblock .listingblock pre:not(.highlight),.sidebarblock .listingblock pre[class="highlight"],.sidebarblock .listingblock pre[class^="highlight "],.sidebarblock .listingblock pre.CodeRay,.sidebarblock .listingblock pre.prettyprint{background:#f2f1f1}.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{-webkit-border-radius:4px;border-radius:4px;word-wrap:break-word;padding:1em;font-size:.8125em}.literalblock pre.nowrap,.literalblock pre[class].nowrap,.listingblock pre.nowrap,.listingblock pre[class].nowrap{overflow-x:auto;white-space:pre;word-wrap:normal}@media only screen and (min-width:768px){.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{font-size:.90625em}}@media only screen and (min-width:1280px){.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{font-size:1em}}.literalblock.output pre{color:#f7f7f8;background-color:rgba(0,0,0,.9)}.listingblock pre.highlightjs{padding:0}.listingblock pre.highlightjs>code{padding:1em;-webkit-border-radius:4px;border-radius:4px}.listingblock pre.prettyprint{border-width:0}.listingblock>.content{position:relative}.listingblock code[data-lang]:before{display:none;content:attr(data-lang);position:absolute;font-size:.75em;top:.425rem;right:.5rem;line-height:1;text-transform:uppercase;color:#999}.listingblock:hover code[data-lang]:before{display:block}.listingblock.terminal pre .command:before{content:attr(data-prompt);padding-right:.5em;color:#999}.listingblock.terminal pre .command:not([data-prompt]):before{content:"$"}table.pyhltable{border-collapse:separate;border:0;margin-bottom:0;background:0}table.pyhltable td{vertical-align:top;padding-top:0;padding-bottom:0;line-height:1.45}table.pyhltable td.code{padding-left:.75em;padding-right:0}pre.pygments .lineno,table.pyhltable td:not(.code){color:#999;padding-left:0;padding-right:.5em;border-right:1px solid #ddddd8}pre.pygments .lineno{display:inline-block;margin-right:.25em}table.pyhltable .linenodiv{background:none!important;padding-right:0!important}.quoteblock{margin:0 1em 1.25em 1.5em;display:table}.quoteblock>.title{margin-left:-1.5em;margin-bottom:.75em}.quoteblock blockquote,.quoteblock blockquote p{color:rgba(0,0,0,.85);font-size:1.15rem;line-height:1.75;word-spacing:.1em;letter-spacing:0;font-style:italic;text-align:justify}.quoteblock blockquote{margin:0;padding:0;border:0}.quoteblock blockquote:before{content:"\201c";float:left;font-size:2.75em;font-weight:bold;line-height:.6em;margin-left:-.6em;color:#7a2518;text-shadow:0 1px 2px rgba(0,0,0,.1)}.quoteblock blockquote>.paragraph:last-child p{margin-bottom:0}.quoteblock .attribution{margin-top:.5em;margin-right:.5ex;text-align:right}.quoteblock .quoteblock{margin-left:0;margin-right:0;padding:.5em 0;border-left:3px solid rgba(0,0,0,.6)}.quoteblock .quoteblock blockquote{padding:0 0 0 .75em}.quoteblock .quoteblock blockquote:before{display:none}.verseblock{margin:0 1em 1.25em 1em}.verseblock pre{font-family:"Open Sans","DejaVu Sans",sans;font-size:1.15rem;color:rgba(0,0,0,.85);font-weight:300;text-rendering:optimizeLegibility}.verseblock pre strong{font-weight:400}.verseblock .attribution{margin-top:1.25rem;margin-left:.5ex}.quoteblock .attribution,.verseblock .attribution{font-size:.9375em;line-height:1.45;font-style:italic}.quoteblock .attribution br,.verseblock .attribution br{display:none}.quoteblock .attribution cite,.verseblock .attribution cite{display:block;letter-spacing:-.025em;color:rgba(0,0,0,.6)}.quoteblock.abstract{margin:0 0 1.25em 0;display:block}.quoteblock.abstract blockquote,.quoteblock.abstract blockquote p{text-align:left;word-spacing:0}.quoteblock.abstract blockquote:before,.quoteblock.abstract blockquote p:first-of-type:before{display:none}table.tableblock{max-width:100%;border-collapse:separate}table.tableblock td>.paragraph:last-child p>p:last-child,table.tableblock th>p:last-child,table.tableblock td>p:last-child{margin-bottom:0}table.tableblock,th.tableblock,td.tableblock{border:0 solid #dedede}table.grid-all>thead>tr>.tableblock,table.grid-all>tbody>tr>.tableblock{border-width:0 1px 1px 0}table.grid-all>tfoot>tr>.tableblock{border-width:1px 1px 0 0}table.grid-cols>*>tr>.tableblock{border-width:0 1px 0 0}table.grid-rows>thead>tr>.tableblock,table.grid-rows>tbody>tr>.tableblock{border-width:0 0 1px 0}table.grid-rows>tfoot>tr>.tableblock{border-width:1px 0 0 0}table.grid-all>*>tr>.tableblock:last-child,table.grid-cols>*>tr>.tableblock:last-child{border-right-width:0}table.grid-all>tbody>tr:last-child>.tableblock,table.grid-all>thead:last-child>tr>.tableblock,table.grid-rows>tbody>tr:last-child>.tableblock,table.grid-rows>thead:last-child>tr>.tableblock{border-bottom-width:0}
table.frame-all{border-width:1px}table.frame-sides{border-width:0 1px}table.frame-topbot{border-width:1px 0}th.halign-left,td.halign-left{text-align:left}th.halign-right,td.halign-right{text-align:right}th.halign-center,td.halign-center{text-align:center}th.valign-top,td.valign-top{vertical-align:top}th.valign-bottom,td.valign-bottom{vertical-align:bottom}th.valign-middle,td.valign-middle{vertical-align:middle}table thead th,table tfoot th{font-weight:bold}tbody tr th{display:table-cell;line-height:1.6;background:#f7f8f7}tbody tr th,tbody tr th p,tfoot tr th,tfoot tr th p{color:rgba(0,0,0,.8);font-weight:bold}p.tableblock>code:only-child{background:0;padding:0}p.tableblock{font-size:1em}td>div.verse{white-space:pre}ol{margin-left:1.75em}ul li ol{margin-left:1.5em}dl dd{margin-left:1.125em}dl dd:last-child,dl dd:last-child>:last-child{margin-bottom:0}ol>li p,ul>li p,ul dd,ol dd,.olist .olist,.ulist .ulist,.ulist .olist,.olist .ulist{margin-bottom:.625em}ul.checklist,ul.none,ol.none,ul.no-bullet,ol.no-bullet,ol.unnumbered,ul.unstyled,ol.unstyled{list-style-type:none}ul.no-bullet,ol.no-bullet,ol.unnumbered{margin-left:.625em}ul.unstyled,ol.unstyled{margin-left:0}ul.checklist{margin-left:.625em}ul.checklist li>p:first-child>.fa-square-o:first-child,ul.checklist li>p:first-child>.fa-check-square-o:first-child{width:1.25em;font-size:.8em;position:relative;bottom:.125em}ul.checklist li>p:first-child>input[type="checkbox"]:first-child{margin-right:.25em}ul.inline{margin:0 auto .625em auto;margin-left:-1.375em;margin-right:0;padding:0;list-style:none;overflow:hidden}ul.inline>li{list-style:none;float:left;margin-left:1.375em;display:block}ul.inline>li>*{display:block}.unstyled dl dt{font-weight:400;font-style:normal}ol.arabic{list-style-type:decimal}ol.decimal{list-style-type:decimal-leading-zero}ol.loweralpha{list-style-type:lower-alpha}ol.upperalpha{list-style-type:upper-alpha}ol.lowerroman{list-style-type:lower-roman}ol.upperroman{list-style-type:upper-roman}ol.lowergreek{list-style-type:lower-greek}.hdlist>table,.colist>table{border:0;background:0}.hdlist>table>tbody>tr,.colist>table>tbody>tr{background:0}td.hdlist1,td.hdlist2{vertical-align:top;padding:0 .625em}td.hdlist1{font-weight:bold;padding-bottom:1.25em}.literalblock+.colist,.listingblock+.colist{margin-top:-.5em}.colist>table tr>td:first-of-type{padding:.4em .75em 0 .75em;line-height:1;vertical-align:top}.colist>table tr>td:first-of-type img{max-width:initial}.colist>table tr>td:last-of-type{padding:.25em 0}.thumb,.th{line-height:0;display:inline-block;border:solid 4px #fff;-webkit-box-shadow:0 0 0 1px #ddd;box-shadow:0 0 0 1px #ddd}.imageblock.left,.imageblock[style*="float:left"]{margin:.25em .625em 1.25em 0}.imageblock.right,.imageblock[style*="float:right"]{margin:.25em 0 1.25em .625em}.imageblock>.title{margin-bottom:0}.imageblock.thumb,.imageblock.th{border-width:6px}.imageblock.thumb>.title,.imageblock.th>.title{padding:0 .125em}.image.left,.image.right{margin-top:.25em;margin-bottom:.25em;display:inline-block;line-height:0}.image.left{margin-right:.625em}.image.right{margin-left:.625em}a.image{text-decoration:none;display:inline-block}a.image object{pointer-events:none}sup.footnote,sup.footnoteref{font-size:.875em;position:static;vertical-align:super}sup.footnote a,sup.footnoteref a{text-decoration:none}sup.footnote a:active,sup.footnoteref a:active{text-decoration:underline}#footnotes{padding-top:.75em;padding-bottom:.75em;margin-bottom:.625em}#footnotes hr{width:20%;min-width:6.25em;margin:-.25em 0 .75em 0;border-width:1px 0 0 0}#footnotes .footnote{padding:0 .375em 0 .225em;line-height:1.3334;font-size:.875em;margin-left:1.2em;text-indent:-1.05em;margin-bottom:.2em}#footnotes .footnote a:first-of-type{font-weight:bold;text-decoration:none}#footnotes .footnote:last-of-type{margin-bottom:0}#content #footnotes{margin-top:-.625em;margin-bottom:0;padding:.75em 0}.gist .file-data>table{border:0;background:#fff;width:100%;margin-bottom:0}.gist .file-data>table td.line-data{width:99%}div.unbreakable{page-break-inside:avoid}.big{font-size:larger}.small{font-size:smaller}.underline{text-decoration:underline}.overline{text-decoration:overline}.line-through{text-decoration:line-through}.aqua{color:#00bfbf}.aqua-background{background-color:#00fafa}.black{color:#000}.black-background{background-color:#000}.blue{color:#0000bf}.blue-background{background-color:#0000fa}.fuchsia{color:#bf00bf}.fuchsia-background{background-color:#fa00fa}.gray{color:#606060}.gray-background{background-color:#7d7d7d}.green{color:#006000}.green-background{background-color:#007d00}.lime{color:#00bf00}.lime-background{background-color:#00fa00}.maroon{color:#600000}.maroon-background{background-color:#7d0000}.navy{color:#000060}.navy-background{background-color:#00007d}.olive{color:#606000}.olive-background{background-color:#7d7d00}.purple{color:#600060}.purple-background{background-color:#7d007d}.red{color:#bf0000}
.red-background{background-color:#fa0000}.silver{color:#909090}.silver-background{background-color:#bcbcbc}.teal{color:#006060}.teal-background{background-color:#007d7d}.white{color:#bfbfbf}.white-background{background-color:#fafafa}.yellow{color:#bfbf00}.yellow-background{background-color:#fafa00}span.icon>.fa{cursor:default}a span.icon>.fa{cursor:inherit}.admonitionblock td.icon [class^="fa icon-"]{font-size:2.5em;text-shadow:1px 1px 2px rgba(0,0,0,.5);cursor:default}.admonitionblock td.icon .icon-note:before{content:"\f05a";color:#19407c}.admonitionblock td.icon .icon-tip:before{content:"\f0eb";text-shadow:1px 1px 2px rgba(155,155,0,.8);color:#111}.admonitionblock td.icon .icon-warning:before{content:"\f071";color:#bf6900}.admonitionblock td.icon .icon-caution:before{content:"\f06d";color:#bf3400}.admonitionblock td.icon .icon-important:before{content:"\f06a";color:#bf0000}.conum[data-value]{display:inline-block;color:#fff!important;background-color:rgba(0,0,0,.8);-webkit-border-radius:100px;border-radius:100px;text-align:center;font-size:.75em;width:1.67em;height:1.67em;line-height:1.67em;font-family:"Open Sans","DejaVu Sans",sans-serif;font-style:normal;font-weight:bold}.conum[data-value] *{color:#fff!important}.conum[data-value]+b{display:none}.conum[data-value]:after{content:attr(data-value)}pre .conum[data-value]{position:relative;top:-.125em}b.conum *{color:inherit!important}.conum:not([data-value]):empty{display:none}dt,th.tableblock,td.content,div.footnote{text-rendering:optimizeLegibility}h1,h2,p,td.content,span.alt{letter-spacing:-.01em}p strong,td.content strong,div.footnote strong{letter-spacing:-.005em}p,blockquote,dt,td.content,span.alt{font-size:1.0625rem}p{margin-bottom:1.25rem}.sidebarblock p,.sidebarblock dt,.sidebarblock td.content,p.tableblock{font-size:1em}.exampleblock>.content{background-color:#fffef7;border-color:#e0e0dc;-webkit-box-shadow:0 1px 4px #e0e0dc;box-shadow:0 1px 4px #e0e0dc}.print-only{display:none!important}@media print{@page{margin:1.25cm .75cm}*{-webkit-box-shadow:none!important;box-shadow:none!important;text-shadow:none!important}a{color:inherit!important;text-decoration:underline!important}a.bare,a[href^="#"],a[href^="mailto:"]{text-decoration:none!important}a[href^="http:"]:not(.bare):after,a[href^="https:"]:not(.bare):after{content:"(" attr(href) ")";display:inline-block;font-size:.875em;padding-left:.25em}abbr[title]:after{content:" (" attr(title) ")"}pre,blockquote,tr,img,object,svg{page-break-inside:avoid}thead{display:table-header-group}svg{max-width:100%}p,blockquote,dt,td.content{font-size:1em;orphans:3;widows:3}h2,h3,#toctitle,.sidebarblock>.content>.title{page-break-after:avoid}#toc,.sidebarblock,.exampleblock>.content{background:none!important}#toc{border-bottom:1px solid #ddddd8!important;padding-bottom:0!important}.sect1{padding-bottom:0!important}.sect1+.sect1{border:0!important}#header>h1:first-child{margin-top:1.25rem}body.book #header{text-align:center}body.book #header>h1:first-child{border:0!important;margin:2.5em 0 1em 0}body.book #header .details{border:0!important;display:block;padding:0!important}body.book #header .details span:first-child{margin-left:0!important}body.book #header .details br{display:block}body.book #header .details br+span:before{content:none!important}body.book #toc{border:0!important;text-align:left!important;padding:0!important;margin:0!important}body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{page-break-before:always}.listingblock code[data-lang]:before{display:block}#footer{background:none!important;padding:0 .9375em}#footer-text{color:rgba(0,0,0,.6)!important;font-size:.9em}.hide-on-print{display:none!important}.print-only{display:block!important}.hide-for-print{display:none!important}.show-for-print{display:inherit!important}}#content .page-footer{height:100px;border-top:1px solid #ccc;overflow:hidden;padding:10px 0;font-size:14px;color:gray}#content .footer-modification{float:right}#content .footer-modification a{text-decoration:none}.sectlevel2{display:none}.submenu{background:#e7e7e6}.submenu li{border:0}.submenu a{color:#555}.copyright{text-align:right;padding-top:1.25em}#toTop{display:none;position:fixed;bottom:10px;right:0;width:44px;height:44px;border-radius:50%;background-color:#ced4ce;cursor:pointer;text-align:center}#upArrow{position:absolute;left:24%;right:0;bottom:19%;transition:.3s ease-in-out;display:block}#upText{position:absolute;left:0;right:0;bottom:0;font-size:16px;font-weight: 600;line-height:45px;display:none;transition:.3s ease-in-out;-webkit-box-align:center}
File diff suppressed because it is too large Load Diff
+393
View File
@@ -0,0 +1,393 @@
$(function () {
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;
const $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);
hljs.highlightAll();
});
$("[contenteditable=plaintext-only]").on('blur', function (e) {
e.preventDefault();
const $this = $(this);
const data = $this.text();
let content = "";
if (undefined === e.originalEvent.clipboardData) {
content = data;
} else {
content = e.originalEvent.clipboardData.getData('text/plain')
}
content = JSON.stringify(JSON.parse(content), null, 4);
const highlightedCode = hljs.highlight('json', content).value;
$this.html(highlightedCode);
})
$("button").on("click", function () {
const $this = $(this);
const id = $this.data("id");
console.log("method-id=>" + id);
let body = $("#" + id + "-body").text();
// header
const $headerElement = $("#" + id + "-header");
const headersData = getInputData($headerElement);
// body param
const $paramElement = $("#" + id + "-param");
let bodyParamData = getInputData($paramElement)
// path param
const $pathElement = $("#" + id + "-path-params")
const pathParamData = getInputData($pathElement)
// query param
const $queryElement = $("#" + id + "-query-params")
const url = $("#" + id + "-url").data("url");
const isDownload = $("#" + id + "-url").data("download");
const page = $("#" + id + "-url").data("page");
const method = $("#" + id + "-method").data("method");
const contentType = $("#" + id + "-content-type").data("content-type");
console.log("request-headers=>" + JSON.stringify(headersData))
console.log("path-params=>" + JSON.stringify(pathParamData))
console.log("body-params=>" + JSON.stringify(bodyParamData))
console.log("json-body=>" + body);
let finalUrl = "";
let queryParamData = "";
if (!isEmpty(page)) {
queryParamData = getInputData($queryElement)
finalUrl = castToGetUri(page, pathParamData, queryParamData)
window.open(finalUrl, "_blank");
return;
}
if (isDownload) {
queryParamData = getInputData($queryElement);
download(url, headersData, pathParamData, queryParamData, bodyParamData,
method, contentType);
return;
}
const ajaxOptions = {};
if ("multipart/form-data" == contentType) {
finalUrl = castToGetUri(url, pathParamData);
queryParamData = getInputData($queryElement, true)
body = queryParamData;
ajaxOptions.processData = false;
ajaxOptions.contentType = false;
} else if ("POST" == method && contentType !== "multipart/form-data"
&& contentType !== "application/json; charset=utf-8") {
finalUrl = castToGetUri(url, pathParamData);
queryParamData = getInputData($queryElement)
body = queryParamData;
} else {
queryParamData = getInputData($queryElement)
finalUrl = castToGetUri(url, pathParamData, queryParamData)
ajaxOptions.contentType = contentType;
}
console.log("query-params=>" + JSON.stringify(queryParamData));
console.log("url=>" + finalUrl)
ajaxOptions.headers = headersData
ajaxOptions.url = finalUrl
ajaxOptions.type = method
ajaxOptions.data = body;
const $responseEle = $("#" + id + "-response").find("pre code");
const ajaxTime = new Date().getTime();
$.ajax(ajaxOptions).done(function (result, textStatus, jqXHR) {
const totalTime = new Date().getTime() - ajaxTime;
$this.css("background", "#5cb85c");
$("#" + id + "-resp-status").html(
"&nbsp;Status:&nbsp;" + jqXHR.status + "&nbsp;&nbsp;" + jqXHR.statusText
+ "&nbsp;&nbsp;&nbsp;&nbsp;Time:&nbsp;" + totalTime + "&nbsp;ms");
const highlightedCode = hljs.highlight('json',
JSON.stringify(result, null, 4)).value;
$responseEle.html(highlightedCode);
}).fail(function (jqXHR) {
const totalTime = new Date().getTime() - ajaxTime;
$this.css("background", "#D44B47");
if (jqXHR.status === 0 && jqXHR.readyState === 0) {
$("#" + id + "-resp-status").html(
"Connection refused, please check the server.");
} else {
$("#" + id + "-resp-status").html(
"&nbsp;Status:&nbsp;" + jqXHR.status + "&nbsp;&nbsp;"
+ jqXHR.statusText + "&nbsp;&nbsp;&nbsp;&nbsp;Time:&nbsp;" + totalTime
+ "&nbsp;ms");
}
if (undefined !== jqXHR.responseJSON) {
const highlightedCode = hljs.highlight('json',
JSON.stringify(jqXHR.responseJSON, null, 4)).value;
$responseEle.html(highlightedCode);
}
}).always(function () {
});
const curlCmd = toCurl(ajaxOptions);
const highlightedCode = hljs.highlight('bash', curlCmd).value;
$("#" + id + "-curl").find("pre code").html(highlightedCode);
})
$(".check-all").on("click", function () {
const checkboxName = $(this).prop("name");
const checked = $(this).is(':checked');
if (!checked) {
$(this).removeAttr("checked");
} else {
$(this).prop("checked", true);
}
$('input[name="' + checkboxName + '"]').each(function () {
if (!checked) {
$(this).removeAttr("checked");
} else {
$(this).prop("checked", true);
}
})
})
function castToGetUri(url, pathParams, params) {
if (pathParams instanceof Object && !(pathParams instanceof Array)) {
url = url.format(pathParams)
}
if (params instanceof Object && !(params instanceof Array)) {
const pm = params || {};
const arr = [];
arr.push(url);
let j = 0;
for (const i in pm) {
if (j === 0) {
arr.push("?");
arr.push(i + "=" + pm[i]);
} else {
arr.push("&" + i + "=" + pm[i]);
}
j++;
}
return arr.join("");
} else {
return url;
}
}
function getInputData(element, returnFormDate) {
const formData = new FormData();
$(element).find("tr").each(function (i) {
const checked = $(this).find('td:eq(0)').children(".checkbox").children(
"input").is(':checked');
if (checked) {
const input = $(this).find('td:eq(2) input');
console.log("input type:" + $(input).attr("type"))
const name = $(input).attr("name");
if ($(input).attr("type") == "file") {
formData.append(name, $(input)[0].files[0]);
} else {
const val = $(input).val();
if (isValidUrl(val)) {
formData.append(name, encodeURI(val));
} else {
// support chinese
if (hasChinese(val)) {
formData.append(name, encodeURIComponent(val));
} else {
formData.append(name, val);
}
}
}
}
});
if (returnFormDate) {
return formData;
}
const headersData = {};
formData.forEach((value, key) => headersData[key] = value);
return headersData;
}
String.prototype.format = function (args) {
let reg;
if (arguments.length > 0) {
let result = this;
if (arguments.length == 1 && typeof (args) == "object") {
for (const key in args) {
reg = new RegExp("({" + key + "})", "g");
result = result.replace(reg, args[key]);
}
} else {
for (let i = 0; i < arguments.length; i++) {
if (arguments[i] == undefined) {
return "";
} else {
reg = new RegExp("({[" + i + "]})", "g");
result = result.replace(reg, arguments[i]);
}
}
}
return result;
} else {
return this;
}
}
function download(url, headersData, pathParamData, queryParamData,
bodyParamData, method, contentType) {
url = castToGetUri(url, pathParamData, queryParamData)
const xmlRequest = new XMLHttpRequest();
xmlRequest.open(method, url, true);
xmlRequest.setRequestHeader("Content-type", contentType);
for (let key in headersData) {
xmlRequest.setRequestHeader(key, headersData[key])
}
xmlRequest.responseType = "blob";
xmlRequest.onload = function () {
if (this.status === 200) {
let fileName = "";
const disposition = xmlRequest.getResponseHeader('Content-Disposition');
if (disposition && disposition.indexOf('attachment') !== -1) {
const filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
const matches = filenameRegex.exec(disposition);
if (matches != null && matches[1]) {
fileName = decodeURI(matches[1].replace(/['"]/g, ''));
}
}
console.log("download filename:" + fileName);
const blob = this.response;
if (navigator.msSaveBlob) {
// IE10 can't do a[download], only Blobs:
window.navigator.msSaveBlob(blob, fileName);
return;
}
if (window.URL) { // simple fast and modern way using Blob and URL:
const a = document.createElement("a");
document.body.appendChild(a);
const url = window.URL.createObjectURL(blob);
a.href = url;
a.download = fileName;
a.click();
window.URL.revokeObjectURL(url);
}
console.log(fileName);
} else {
console.log("download failed");
}
};
try {
xmlRequest.send(bodyParamData);
} catch (e) {
console.error("Failed to send data", e);
}
}
function toCurl(request) {
if (typeof request !== 'object') {
throw "Request is not an object";
}
// default is a GET request
const cmd = ["curl", "-X", request.type || "GET"];
if (request.url.indexOf('https') == 0) {
cmd.push("-k");
}
// append Content-Type
if (request.contentType) {
cmd.push("-H");
cmd.push("'Content-Type:");
cmd.push(request.contentType + "'");
}
// append request headers
let headerValue;
if (typeof request.headers == 'object') {
for (let key in request.headers) {
if (Object.prototype.hasOwnProperty.call(request.headers, key)) {
cmd.push("-H");
headerValue = request.headers[key];
if (headerValue.value == '') {
cmd.push("'" + key + "'");
} else {
cmd.push("'" + key + ':' + headerValue + "'");
}
}
}
}
// display the response headers
cmd.push("-i");
// append request url
let url = request.url;
if (!url.startsWith("http")) {
const protocol = window.document.location.protocol;
const domain = window.document.location.hostname;
const port = window.document.location.port;
url = protocol + "//" + domain + ":" + port + url;
}
cmd.push(url);
// append data
if (typeof request.data == 'object') {
let index = 0;
const bodyData = [];
bodyData.push("\"")
for (let key in request.data) {
if (Object.prototype.hasOwnProperty.call(request.data, key)) {
if (index === 0) {
bodyData.push(key);
bodyData.push("=");
bodyData.push(request.data[key]);
} else {
bodyData.push("&")
bodyData.push(key);
bodyData.push("=");
bodyData.push(request.data[key]);
}
}
index++;
}
bodyData.push("\"");
let bodyStr = ""
bodyData.forEach(function (item) {
bodyStr += item;
});
cmd.push("--data");
cmd.push(bodyStr);
} else if (request.data && request.data.length > 0) {
// append json data
cmd.push("--data");
cmd.push("'" + request.data + "'");
}
let curlCmd = "";
cmd.forEach(function (item) {
curlCmd += item + " ";
});
console.log(curlCmd);
return curlCmd;
}
function isEmpty(obj) {
return obj === undefined || obj === null || new String(obj).trim() === '';
}
function hasChinese(_string) {
const chineseReg = /[\u4e00-\u9fa5]/g
return chineseReg.test(_string);
}
function isValidUrl(_string) {
let urlString;
try {
urlString = new URL(_string);
} catch (_) {
return false;
}
return urlString.protocol === "http:" || urlString.protocol === "https:";
}
+5
View File
@@ -0,0 +1,5 @@
@font-face{font-family:'Droid Sans Mono';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/droidsansmono/v14/6NUO8FuJNQ2MbkrZ5-J8lKFrp7pRef2r.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:'Noto Serif';font-style:italic;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/notoserif/v11/ga6Kaw1J5X9T9RW6j9bNfFImZzC7TMQ.woff2) format('woff2');unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:'Noto Serif';font-style:italic;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/notoserif/v11/ga6Kaw1J5X9T9RW6j9bNfFImbjC7TMQ.woff2) format('woff2');unicode-range:U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:'Noto Serif';font-style:italic;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/notoserif/v11/ga6Kaw1J5X9T9RW6j9bNfFImZjC7TMQ.woff2) format('woff2');unicode-range:U+1F00-1FFF}@font-face{font-family:'Noto Serif';font-style:italic;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/notoserif/v11/ga6Kaw1J5X9T9RW6j9bNfFImaTC7TMQ.woff2) format('woff2');unicode-range:U+0370-03FF}@font-face{font-family:'Noto Serif';font-style:italic;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/notoserif/v11/ga6Kaw1J5X9T9RW6j9bNfFImZTC7TMQ.woff2) format('woff2');unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+1EA0-1EF9,U+20AB}@font-face{font-family:'Noto Serif';font-style:italic;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/notoserif/v11/ga6Kaw1J5X9T9RW6j9bNfFImZDC7TMQ.woff2) format('woff2');unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'Noto Serif';font-style:italic;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/notoserif/v11/ga6Kaw1J5X9T9RW6j9bNfFImajC7.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:'Noto Serif';font-style:italic;font-weight:700;src:url(https://fonts.gstatic.cnpmjs.org/s/notoserif/v11/ga6Vaw1J5X9T9RW6j9bNfFIu0RWufuVMCoY.woff2) format('woff2');unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:'Noto Serif';font-style:italic;font-weight:700;src:url(https://fonts.gstatic.cnpmjs.org/s/notoserif/v11/ga6Vaw1J5X9T9RW6j9bNfFIu0RWud-VMCoY.woff2) format('woff2');unicode-range:U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:'Noto Serif';font-style:italic;font-weight:700;src:url(https://fonts.gstatic.cnpmjs.org/s/notoserif/v11/ga6Vaw1J5X9T9RW6j9bNfFIu0RWuf-VMCoY.woff2) format('woff2');unicode-range:U+1F00-1FFF}@font-face{font-family:'Noto Serif';font-style:italic;font-weight:700;src:url(https://fonts.gstatic.cnpmjs.org/s/notoserif/v11/ga6Vaw1J5X9T9RW6j9bNfFIu0RWucOVMCoY.woff2) format('woff2');unicode-range:U+0370-03FF}@font-face{font-family:'Noto Serif';font-style:italic;font-weight:700;src:url(https://fonts.gstatic.cnpmjs.org/s/notoserif/v11/ga6Vaw1J5X9T9RW6j9bNfFIu0RWufOVMCoY.woff2) format('woff2');unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+1EA0-1EF9,U+20AB}@font-face{font-family:'Noto Serif';font-style:italic;font-weight:700;src:url(https://fonts.gstatic.cnpmjs.org/s/notoserif/v11/ga6Vaw1J5X9T9RW6j9bNfFIu0RWufeVMCoY.woff2) format('woff2');unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'Noto Serif';font-style:italic;font-weight:700;src:url(https://fonts.gstatic.cnpmjs.org/s/notoserif/v11/ga6Vaw1J5X9T9RW6j9bNfFIu0RWuc-VM.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:'Noto Serif';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/notoserif/v11/ga6Iaw1J5X9T9RW6j9bNfFoWaCi_.woff2) format('woff2');unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}
@font-face{font-family:'Noto Serif';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/notoserif/v11/ga6Iaw1J5X9T9RW6j9bNfFMWaCi_.woff2) format('woff2');unicode-range:U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:'Noto Serif';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/notoserif/v11/ga6Iaw1J5X9T9RW6j9bNfFsWaCi_.woff2) format('woff2');unicode-range:U+1F00-1FFF}@font-face{font-family:'Noto Serif';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/notoserif/v11/ga6Iaw1J5X9T9RW6j9bNfFQWaCi_.woff2) format('woff2');unicode-range:U+0370-03FF}@font-face{font-family:'Noto Serif';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/notoserif/v11/ga6Iaw1J5X9T9RW6j9bNfFgWaCi_.woff2) format('woff2');unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+1EA0-1EF9,U+20AB}@font-face{font-family:'Noto Serif';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/notoserif/v11/ga6Iaw1J5X9T9RW6j9bNfFkWaCi_.woff2) format('woff2');unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'Noto Serif';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/notoserif/v11/ga6Iaw1J5X9T9RW6j9bNfFcWaA.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:'Noto Serif';font-style:normal;font-weight:700;src:url(https://fonts.gstatic.cnpmjs.org/s/notoserif/v11/ga6Law1J5X9T9RW6j9bNdOwzfRqecf1I.woff2) format('woff2');unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:'Noto Serif';font-style:normal;font-weight:700;src:url(https://fonts.gstatic.cnpmjs.org/s/notoserif/v11/ga6Law1J5X9T9RW6j9bNdOwzfROecf1I.woff2) format('woff2');unicode-range:U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:'Noto Serif';font-style:normal;font-weight:700;src:url(https://fonts.gstatic.cnpmjs.org/s/notoserif/v11/ga6Law1J5X9T9RW6j9bNdOwzfRuecf1I.woff2) format('woff2');unicode-range:U+1F00-1FFF}@font-face{font-family:'Noto Serif';font-style:normal;font-weight:700;src:url(https://fonts.gstatic.cnpmjs.org/s/notoserif/v11/ga6Law1J5X9T9RW6j9bNdOwzfRSecf1I.woff2) format('woff2');unicode-range:U+0370-03FF}@font-face{font-family:'Noto Serif';font-style:normal;font-weight:700;src:url(https://fonts.gstatic.cnpmjs.org/s/notoserif/v11/ga6Law1J5X9T9RW6j9bNdOwzfRiecf1I.woff2) format('woff2');unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+1EA0-1EF9,U+20AB}@font-face{font-family:'Noto Serif';font-style:normal;font-weight:700;src:url(https://fonts.gstatic.cnpmjs.org/s/notoserif/v11/ga6Law1J5X9T9RW6j9bNdOwzfRmecf1I.woff2) format('woff2');unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'Noto Serif';font-style:normal;font-weight:700;src:url(https://fonts.gstatic.cnpmjs.org/s/notoserif/v11/ga6Law1J5X9T9RW6j9bNdOwzfReecQ.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:'Open Sans';font-style:italic;font-weight:300;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/memnYaGs126MiZpBA-UFUKWyV9hmIqOjjg.woff2) format('woff2');unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:'Open Sans';font-style:italic;font-weight:300;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/memnYaGs126MiZpBA-UFUKWyV9hvIqOjjg.woff2) format('woff2');unicode-range:U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:'Open Sans';font-style:italic;font-weight:300;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/memnYaGs126MiZpBA-UFUKWyV9hnIqOjjg.woff2) format('woff2');unicode-range:U+1F00-1FFF}@font-face{font-family:'Open Sans';font-style:italic;font-weight:300;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/memnYaGs126MiZpBA-UFUKWyV9hoIqOjjg.woff2) format('woff2');unicode-range:U+0370-03FF}
@font-face{font-family:'Open Sans';font-style:italic;font-weight:300;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/memnYaGs126MiZpBA-UFUKWyV9hkIqOjjg.woff2) format('woff2');unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+1EA0-1EF9,U+20AB}@font-face{font-family:'Open Sans';font-style:italic;font-weight:300;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/memnYaGs126MiZpBA-UFUKWyV9hlIqOjjg.woff2) format('woff2');unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'Open Sans';font-style:italic;font-weight:300;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/memnYaGs126MiZpBA-UFUKWyV9hrIqM.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:'Open Sans';font-style:italic;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/mem6YaGs126MiZpBA-UFUK0Udc1UAw.woff2) format('woff2');unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:'Open Sans';font-style:italic;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/mem6YaGs126MiZpBA-UFUK0ddc1UAw.woff2) format('woff2');unicode-range:U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:'Open Sans';font-style:italic;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/mem6YaGs126MiZpBA-UFUK0Vdc1UAw.woff2) format('woff2');unicode-range:U+1F00-1FFF}@font-face{font-family:'Open Sans';font-style:italic;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/mem6YaGs126MiZpBA-UFUK0adc1UAw.woff2) format('woff2');unicode-range:U+0370-03FF}@font-face{font-family:'Open Sans';font-style:italic;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/mem6YaGs126MiZpBA-UFUK0Wdc1UAw.woff2) format('woff2');unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+1EA0-1EF9,U+20AB}@font-face{font-family:'Open Sans';font-style:italic;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/mem6YaGs126MiZpBA-UFUK0Xdc1UAw.woff2) format('woff2');unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'Open Sans';font-style:italic;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/mem6YaGs126MiZpBA-UFUK0Zdc0.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:'Open Sans';font-style:italic;font-weight:600;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/memnYaGs126MiZpBA-UFUKXGUdhmIqOjjg.woff2) format('woff2');unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:'Open Sans';font-style:italic;font-weight:600;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/memnYaGs126MiZpBA-UFUKXGUdhvIqOjjg.woff2) format('woff2');unicode-range:U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:'Open Sans';font-style:italic;font-weight:600;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/memnYaGs126MiZpBA-UFUKXGUdhnIqOjjg.woff2) format('woff2');unicode-range:U+1F00-1FFF}@font-face{font-family:'Open Sans';font-style:italic;font-weight:600;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/memnYaGs126MiZpBA-UFUKXGUdhoIqOjjg.woff2) format('woff2');unicode-range:U+0370-03FF}@font-face{font-family:'Open Sans';font-style:italic;font-weight:600;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/memnYaGs126MiZpBA-UFUKXGUdhkIqOjjg.woff2) format('woff2');unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+1EA0-1EF9,U+20AB}@font-face{font-family:'Open Sans';font-style:italic;font-weight:600;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/memnYaGs126MiZpBA-UFUKXGUdhlIqOjjg.woff2) format('woff2');unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}
@font-face{font-family:'Open Sans';font-style:italic;font-weight:600;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/memnYaGs126MiZpBA-UFUKXGUdhrIqM.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:'Open Sans';font-style:normal;font-weight:300;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/mem5YaGs126MiZpBA-UN_r8OX-hpOqc.woff2) format('woff2');unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:'Open Sans';font-style:normal;font-weight:300;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/mem5YaGs126MiZpBA-UN_r8OVuhpOqc.woff2) format('woff2');unicode-range:U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:'Open Sans';font-style:normal;font-weight:300;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/mem5YaGs126MiZpBA-UN_r8OXuhpOqc.woff2) format('woff2');unicode-range:U+1F00-1FFF}@font-face{font-family:'Open Sans';font-style:normal;font-weight:300;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/mem5YaGs126MiZpBA-UN_r8OUehpOqc.woff2) format('woff2');unicode-range:U+0370-03FF}@font-face{font-family:'Open Sans';font-style:normal;font-weight:300;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/mem5YaGs126MiZpBA-UN_r8OXehpOqc.woff2) format('woff2');unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+1EA0-1EF9,U+20AB}@font-face{font-family:'Open Sans';font-style:normal;font-weight:300;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/mem5YaGs126MiZpBA-UN_r8OXOhpOqc.woff2) format('woff2');unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'Open Sans';font-style:normal;font-weight:300;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/mem5YaGs126MiZpBA-UN_r8OUuhp.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:'Open Sans';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/mem8YaGs126MiZpBA-UFWJ0bbck.woff2) format('woff2');unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:'Open Sans';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/mem8YaGs126MiZpBA-UFUZ0bbck.woff2) format('woff2');unicode-range:U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:'Open Sans';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/mem8YaGs126MiZpBA-UFWZ0bbck.woff2) format('woff2');unicode-range:U+1F00-1FFF}@font-face{font-family:'Open Sans';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/mem8YaGs126MiZpBA-UFVp0bbck.woff2) format('woff2');unicode-range:U+0370-03FF}@font-face{font-family:'Open Sans';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/mem8YaGs126MiZpBA-UFWp0bbck.woff2) format('woff2');unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+1EA0-1EF9,U+20AB}@font-face{font-family:'Open Sans';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/mem8YaGs126MiZpBA-UFW50bbck.woff2) format('woff2');unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'Open Sans';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/mem8YaGs126MiZpBA-UFVZ0b.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:'Open Sans';font-style:normal;font-weight:600;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/mem5YaGs126MiZpBA-UNirkOX-hpOqc.woff2) format('woff2');unicode-range:U+0460-052F,U+1C80-1C88,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}
@font-face{font-family:'Open Sans';font-style:normal;font-weight:600;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/mem5YaGs126MiZpBA-UNirkOVuhpOqc.woff2) format('woff2');unicode-range:U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:'Open Sans';font-style:normal;font-weight:600;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/mem5YaGs126MiZpBA-UNirkOXuhpOqc.woff2) format('woff2');unicode-range:U+1F00-1FFF}@font-face{font-family:'Open Sans';font-style:normal;font-weight:600;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/mem5YaGs126MiZpBA-UNirkOUehpOqc.woff2) format('woff2');unicode-range:U+0370-03FF}@font-face{font-family:'Open Sans';font-style:normal;font-weight:600;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/mem5YaGs126MiZpBA-UNirkOXehpOqc.woff2) format('woff2');unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+1EA0-1EF9,U+20AB}@font-face{font-family:'Open Sans';font-style:normal;font-weight:600;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/mem5YaGs126MiZpBA-UNirkOXOhpOqc.woff2) format('woff2');unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'Open Sans';font-style:normal;font-weight:600;src:url(https://fonts.gstatic.cnpmjs.org/s/opensans/v23/mem5YaGs126MiZpBA-UNirkOUuhp.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
File diff suppressed because one or more lines are too long
+276
View File
@@ -0,0 +1,276 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<meta content="smart-doc" name="generator">
<title>chinaport-data-signature</title>
<link href="font.css" rel="stylesheet">
<link href="AllInOne.css?v=1644762480925" rel="stylesheet"/>
<link href="xt256.min.css" rel="stylesheet">
<style>.literalblock pre, .listingblock pre:not(.highlight), .listingblock pre[class="highlight"], .listingblock pre[class^="highlight "], .listingblock pre.CodeRay, .listingblock pre.prettyprint {
background: #000
}
.hljs {
padding: 0
}</style>
<script src="highlight.min.js"></script>
<script src="jquery.min.js"></script>
</head>
<body class="book toc2 toc-left">
<div id="header"><h1>chinaport-data-signature</h1>
<div class="toc2" id="toc">
<div id="book-search-input"><input id="search" placeholder="Type to search" type="text"></div>
<div id="toctitle"><span>API Reference</span></div>
<ul class="sectlevel1" id="accordion">
<li class="open"><a class="dd" href="#_1_1_电子口岸报文加签Controller">1.&nbsp;电子口岸报文加签Controller</a>
<ul class="sectlevel2">
<li><a href="#_1_1_1_CEBXxxMessage.xml加签">1.1.&nbsp;CEBXxxMessage.xml加签</a></li>
</ul>
</li>
</ul>
</div>
</div>
<div id="content">
<div id="preamble">
<div class="sectionbody">
<table class="tableblock frame-all grid-all spread">
<colgroup>
<col style="width: 20%;">
<col style="width: 20%;">
<col style="width: 20%;">
<col style="width: 20%;">
<col style="width: 20%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Version</th>
<th class="tableblock halign-left valign-top">Update Time</th>
<th class="tableblock halign-left valign-top">Status</th>
<th class="tableblock halign-left valign-top">Author</th>
<th class="tableblock halign-left valign-top">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">1.0</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">2022-02-12 10:30</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">创建</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Weasley J</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">创建api文档</p></td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="sect1"><h2 id="_1_1_电子口岸报文加签Controller"><a class="anchor" href="#_1_1_电子口岸报文加签Controller"></a><a
class="link" href="#_1_1_电子口岸报文加签Controller">1.&nbsp;电子口岸报文加签Controller</a></h2>
<div class="sectionbody">
<div class="sect2" id="cd4a9d8b61452c6393010d5e007607df"><h3 id="_1_1_1_CEBXxxMessage.xml加签"><a
class="anchor" href="#_1_1_1_CEBXxxMessage.xml加签"></a><a class="link"
href="#_1_1_1_CEBXxxMessage.xml加签">1.1.&nbsp;CEBXxxMessage.xml加签</a>
</h3>
<div class="paragraph" data-download="false" data-page=""
data-url="http://localhost:8080/rpc/eport/signature" id="cd4a9d8b61452c6393010d5e007607df-url"><p>
<strong>URL:</strong><a class="bare" href="http://localhost:8080/rpc/eport/signature">&nbsp;http://localhost:8080/rpc/eport/signature</a>
</p></div>
<div class="paragraph" data-method="POST" id="cd4a9d8b61452c6393010d5e007607df-method"><p>
<strong>Type:</strong>&nbsp;POST</p></div>
<div class="paragraph"><p><strong>Author:</strong>&nbsp;lwj</p></div>
<div class="paragraph" data-content-type="application/json; charset=utf-8"
id="cd4a9d8b61452c6393010d5e007607df-content-type"><p><strong>Content-Type:</strong>&nbsp;application/json;
charset=utf-8</p></div>
<div class="paragraph"><p><strong>Description:</strong>&nbsp;CEBXxxMessage.xml加签</p></div>
<div class="paragraph"><p><strong>Body-parameters:</strong></p></div>
<table class="tableblock frame-all grid-all spread">
<colgroup>
<col style="width: 20%;">
<col style="width: 20%;">
<col style="width: 20%;">
<col style="width: 20%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Parameter</th>
<th class="tableblock halign-left valign-top">Type</th>
<th class="tableblock halign-left valign-top">Required</th>
<th class="tableblock halign-left valign-top">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">id</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">int32</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">唯一id,
用来区分是哪一次发送的消息,默认值=1,从1开始,{@code int} 最大, 2<sup>31</sup>-1.</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">sourceXml</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">true</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">
不带ds:Signature节点的CEbXXXMessage.xml原文</p></td>
</tr>
</tbody>
</table>
<div class="paragraph"><p><strong>Request-body:</strong></p></div>
<div class="listingblock">
<div class="content"><pre><code contenteditable="plaintext-only"
id="cd4a9d8b61452c6393010d5e007607df-body">{
"id": 399,
"sourceXml": "j5z71j"
}</code></pre>
</div>
</div>
<div class="paragraph"><p><strong>Response-fields:</strong></p></div>
<table class="tableblock frame-all grid-all spread">
<colgroup>
<col style="width: 25%;">
<col style="width: 25%;">
<col style="width: 25%;">
<col style="width: 25%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Field</th>
<th class="tableblock halign-left valign-top">Type</th>
<th class="tableblock halign-left valign-top">Description</th>
<th class="tableblock halign-left valign-top">Since</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">message</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">返回消息</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">-</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">success</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">boolean</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">是否成功</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">-</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">timestamp</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">响应时间戳</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">-</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">code</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">int32</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">状态码</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">-</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">data</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">object</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">响应数据</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">-</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">└─success</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">boolean</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">本次是否加签成功</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">-</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">└─certNo</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">签名的ukey的卡序列号,需要用专用的工具导出</p>
</td>
<td class="tableblock halign-left valign-top"><p class="tableblock">-</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">└─x509Certificate</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">签名的ukey证书,需要用专用工具导出</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">-</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">└─digestValue</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">
xml的数字摘要,先对不包含Signature节点的原始报文,进行C14n格式化,然后取shal二进制摘要,然后对sha1的值进行base64编码<br>
<ul><br>
<li><a href='http://tool.qdhuaxun.cn/ceb/CEB311Message.xml'> 待加签报文样例</a></li>
<br></ul>
</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">-</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">└─signatureValue</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">调用ukey获取的签名值</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">-</p></td>
</tr>
</tbody>
</table>
<div class="paragraph">
<p><strong>
<button class="send-button" data-id="cd4a9d8b61452c6393010d5e007607df">Send Request</button>
</strong><span id="cd4a9d8b61452c6393010d5e007607df-resp-status"></span></p>
</div>
<div class="paragraph"><p><strong>Response-example:</strong></p></div>
<div class="listingblock">
<div class="content" id="cd4a9d8b61452c6393010d5e007607df-response"><pre><code class="json">{
"message": "success",
"success": true,
"timestamp": "2022-02-13 22:28:01",
"code": 633,
"data": {
"success": true,
"certNo": "l2nbfn",
"x509Certificate": "6y72t7",
"digestValue": "s8e5nj",
"signatureValue": "gvl2bx"
}
}</code></pre>
</div>
</div>
<div class="paragraph"><p><strong>Curl-example:</strong></p></div>
<div class="listingblock">
<div class="content" id="cd4a9d8b61452c6393010d5e007607df-curl"><pre><code class="bash">curl -X POST -H 'Content-Type: application/json; charset=utf-8' -i http://localhost:8080/rpc/eport/signature --data '{
"id": 399,
"sourceXml": "j5z71j"
}'</code></pre>
</div>
</div>
</div>
</div>
</div>
<footer class="page-footer"><span class="copyright">Generated by smart-doc at 2022-02-13 22:28:00</span><span
class="footer-modification">Suggestions,contact,support and error reporting on<a
href="https://gitee.com/smart-doc-team/smart-doc" target="_blank">&nbsp;Gitee</a>&nbsp;or<a
href="https://github.com/smart-doc-group/smart-doc.git" target="_blank">&nbsp;Github</a></span></footer>
<div href="javascript:void(0)" id="toTop"><img id="upArrow"
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAABlUlEQVRIS+2UvUvDQBiH398Rly4udnARwUXs4qAIOigI4iL30dTZ2T9AcNPVvUsXF7uYttdScNDFRRAnB11cFFwKxcXBJTQnJ6lEbRI/CIiY9e6e5/e+9+ZAGX/ImE9/QKCU2jfGbGTQqq4xZgtSyisiKmQgIAAVCCFWAGxnIOhqrdd/xyUrpRZsP40xSwA6AI57vd5eq9W6T6s8tQIppSKi+gDQNREprfVNkiRRwDlfY4xZ+FAIuSOi8Qjw0nEc5XnebZwkViClXA2T5+xhY8xus9ncEUJMAziITN5FEARuXLsGCoQQywBs8uEovJ+Scz7FGDuMSM4cx3E9z+u8r+SDQEq5SEQ1IhoZBE+QnBKRq7V+iEreCDjn84wxCx9NgidITnK5nFutVh/7e14FSqnZIAhqAMY+A4+TADjyfb/Ubref7J4XQXhxNvnEV+AJlbTy+XypUqn4KBaLBZuciCa/A0+opN5oNFz7FpUBbP4EHicxxsyAcz7HGDvvz3nar5+2Ho5wOQwsU5+KNGDa+r8grUP0DBLjtRtNKEliAAAAAElFTkSuQmCC"><span
id="upText">Top</span></div>
</div>
<script src="debug.js?v=1644762480925"></script>
<script src="search.js?v=1644762480925"></script>
<script>$(function () {
$(window).scroll(function () {
if ($(window).scrollTop() > 100) {
$("#toTop").fadeIn(1500);
$("#toTop").hover(function () {
$("#upArrow").hide();
$("#upText").show()
}, function () {
$("#upArrow").show();
$("#upText").hide()
})
} else {
$("#toTop").fadeOut(1500)
}
});
$("#toTop").click(function () {
$("body, html").animate({scrollTop: 0}, 1000);
return false
})
});</script>
</body>
</html>
File diff suppressed because one or more lines are too long
+171
View File
@@ -0,0 +1,171 @@
let api = [];
const apiDocListSize = 1
api.push({
name: 'default',
order: '1',
list: []
})
api[0].list.push({
alias: 'EportSignController',
order: '1',
link: '电子口岸报文加签controller',
desc: '电子口岸报文加签Controller',
list: []
})
api[0].list[0].list.push({
order: '1',
deprecated: 'false',
url: 'http://localhost:8080/rpc/eport/signature',
desc: 'CEBXxxMessage.xml加签',
});
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;
$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 = "";
let doc;
if (apiGroups.length > 0) {
if (apiDocListSize == 1) {
let apiData = apiGroups[0].list;
for (let j = 0; j < apiData.length; j++) {
html += '<li class="' + liClass + '">';
html += '<a class="dd" href="#_' + apiData[j].link + '">' + apiData[j].order + '.&nbsp;' + apiData[j].desc + '</a>';
html += '<ul class="sectlevel2" style="' + display + '">';
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="#_1_' + apiData[j].order + '_' + doc[m].order + '_' + doc[m].desc + '">' + apiData[j].order + '.' + doc[m].order + '.&nbsp;' + 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.name + '">' + apiGroup.order + '.&nbsp;' + 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 + '.&nbsp;' + apiData[j].desc + '</a>';
html += '<ul class="sectlevel2" style="' + display + '">';
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 + '.&nbsp;' + spanString + doc[m].desc + '<span></a> </li>';
}
html += '</ul>';
html += '</li>';
}
html += '</ul>';
html += '</li>';
}
}
}
return html;
}
+709
View File
@@ -0,0 +1,709 @@
/**
* 用户是否已经点击登陆按钮
*/
var isLoginButtonPressed;
/**
* 密码节点
*/
var upnode = document.getElementById("password");
/**
* 显示提示信息的节点
*/
var infonode;
/**
* 初始化函数,做一些页面初始信息的获取
*/
function init(i) {
var steupinfo = document.getElementById("setupinfo");
if (steupinfo != undefined) {
document.body.removeChild(steupinfo);
}
isLoginButtonPressed = false;
syncStatus();//同步状态信息
if (!isInstalled()) {
return;
}
upnode = document.getElementById("password");
try {
upnode.focus();
} catch (e) {
}
}
/**
* 封装获取元素节点的value属性值
* 输入参数可以是1-n个节点的id属性值
* 返回值是一个元素节点或一个包含所有元素节点的value值的数组
*/
function $V() {
var values = new Array();
for (var i = 0; i < arguments.length; i++) {
var value = arguments[i];
if (typeof value == 'string') {
var tempnode = document.getElementById(value);
if (tempnode == null || !(tempnode.value)) {
value = null;
} else {
value = document.getElementById(value).value;
}
}
if (arguments.length == 1) {
return value;
}
values.push(value);
}
return values;
}
/**
* 新建一个文本节点
* @param content 文本节点内容
* @return 文本节点
*/
function $CT(content) {
return document.createTextNode(content);
}
/**
* 封装获取元素节点中包含的唯一文本节点内容的方法
* 输入参数可以是1-n个节点的id属性值
* 返回值是一个元素节点或一个包含所有元素节点内部的唯一为本节点内容的数据
*/
function $T() {
var texts = new Array();
for (var i = 0; i < arguments.length; i++) {
var text = arguments[i];
if (typeof text == 'string') {
var temp = document.getElementById(text);
if (temp == null) {
text = null;
} else {
var tempnode = document.getElementById(text).firstChild;
if (tempnode == null) {
text = null;
} else {
text = tempnode.nodeValue;
}
}
}
if (arguments.length == 1) {
return text;
}
texts.push(text);
}
return texts;
}
/**
* 键盘按下时处理事件
*/
function keyp(event) {
upnode = document.getElementById("password");
try {
wt(document.getElementById("password"));
} catch (e) {
}
var ev;
if (event) {
ev = event;
} else {
ev = window.event;
}
isLoginButtonPressed == false;
if (ev.keyCode == 13) {
doCAIdentify();
}
}
function keypNoCa(event) {
var ev;
if (event) {
ev = event;
} else {
ev = window.event;
}
if (ev.keyCode == 13) {
doIdentify();
}
}
function doIdentify() {
if (isLoginButtonPressed == true) {
return;
}
isLoginButtonPressed = true;
infonode = document.getElementById("infoNoCa");
if (infonode != null) {
infonode.style.display = "none";
infonode.style.backgroundColor = "";
//infonode.css("display","none");
//infonode.css("backgroundColor","")
infonode.innerHTML = "";
}
//showinfo();
username = $V("usernameNoCa");
if (username == null || username.length < 1 || username == "请输入登录账号") {
clearinfo();
alert("用户名不允许为空");
$("#username").focus();
isLoginButtonPressed = false;
return;
}
userpassword = $V("passwordNoCa");
if (userpassword == null || userpassword.length < 1) {
clearinfo();
alert("密码不允许为空");
$("#passwordNoCa").focus();
isLoginButtonPressed = false;
return;
}
var verifyCode = $V("verifyCode");
if (verifyCode == null || verifyCode.length < 1) {
clearinfo();
alert("验证码不允许为空");
$("#verifyCode").focus();
isLoginButtonPressed = false;
return;
}
$("#swy").val(username);
if (plainCode == null) {
$("#swm").val(hex_md5(userpassword));
} else {
$("#swm").val(userpassword);
}
try {
chkmm(userpassword);
} catch (exception) {
}
//$("#fmNoCa").submit();
document.getElementById("fmNoCa").submit();
}
/**
* 用户提交登陆认证请求入口
*/
function doCAIdentify() {
if (isLoginButtonPressed == true) {
return;
}
isLoginButtonPressed = true;
infonode = document.getElementById("info");
if (infonode != null) {
infonode.style.display = "none";
infonode.style.backgroundColor = "";
infonode.innerHTML = "";
}
showinfo();
userpassword = $V("password");
if (userpassword == null || userpassword.length < 1) {
clearinfo();
alert("IC卡/Key密码不允许为空");
upnode = $("password");
upnode.value = "";
upnode.focus();
isLoginButtonPressed = false;
return;
}
// setStatus("开始进行密码验证...");
checkPwd(userpassword);
}
/**
* 清除登录提示信息
*/
function clearinfo() {
if (infonode != null) {
infonode.style.display = "none";
infonode.style.backgroundColor = "";
infonode.innerHTML = "";
}
}
//校验密码
function checkPwd(strPwd) {
var cPw = new Array("0", "1", "2", "3", "4", "5", "6", "7", "8"); //密码组
var Max = 8;
if (strPwd == "") {
alert("您输入的IC卡/Key密码不能为空");
isLoginButtonPressed = false;
return -1;
}
for (i = 0; i <= Max; i++) {
if (strPwd == cPw[i]) {
alert("请正确输入8位IC卡/Key密码");
clearinfo();
upnode = $("password");
upnode.value = "";
upnode.focus();
isLoginButtonPressed = false;
return -2;
}
}
//判读读卡器类型
EportClient.isInstalledTest(EportClient.raIs903Reader, function (msg, msgJson) {
RAIs903Reader(msg, msgJson, strPwd);
});
//初始化环境
//EportClient.spcInitEnvEx(HandleCheckInsCardResult);
//新版本:只用一个接口 xiejh
//EportClient.isInstalledTest(EportClient.spcSignRandom, strPwd, document.getElementById("random").value, function (msg) {
//if (msg.Result) {
// document.getElementById("Textarea11").value = JSON.stringify(msg);
//} else {
// document.getElementById("Textarea11").value = "";
// alertErrMsg(msg);
//}
//HandleSignResult(msg);
//});
}
//检查是否插卡了,使用取签名证书来验证
function HandleCheckInsCardResult(msg) {
if (msg.Result) {
EportClient.spcGetSignCert(HandleCheckInsCardStausResult);
} else {
if (JSON.stringify(msg.Error).indexOf("51590") > 0
|| JSON.stringify(msg.Error).indexOf("50200") > 0) {
alert("请插入IC卡或者Key!");
} else {
alert(msg.Error);
}
isLoginButtonPressed = false;
EportClient.spcClearEnv();
}
}
//检查是否插卡了,结果处理
function HandleCheckInsCardStausResult(msg) {
if (msg.Result) {
HandleVerifyPINResult(msg);
} else {
if (msg.Error && msg.Error != "") {
if (JSON.stringify(msg.Error).indexOf("51590") > 0
|| JSON.stringify(msg.Error).indexOf("50200") > 0) {
alert("请插入IC卡或者Key");
} else {
alert(msg.Error);
}
clearinfo();
isLoginButtonPressed = false;
EportClient.spcClearEnv();
}
}
}
//检查Pin密码
function HandleVerifyPINResult(msg) {
var strPwd = $V("password");
EportClient.spcVerifyPIN(strPwd, HandleCheckPwdResult);
}
//处理验证密码结果
function HandleCheckPwdResult(msg) {
if (msg.Result) {
EportClient.spcSignPemDataAsPEM($V("random"), HandleSignResult)
} else {
if (msg.Error && msg.Error != "") {
alert(msg.Error);
clearinfo();
upnode.value = "";
upnode.focus();
isLoginButtonPressed = false;
return;
} else {
window.opener = null;
window.open('', '_self', '');
window.close();
}
}
}
//处理签名结果
function HandleSignResult(msg) {
if (msg.Result) {
//$("signData").value=(msg.Data)[0];
document.getElementById("signData").value = (msg.Data)[0];
//EportClient.spcGetCardUserInfo(HandleCardUerInfoResult);
EportClient.getGetCardUserInfoAll($V("password"), HandleCardUerInfoResult);
} else {
if (msg.Error && msg.Error != "") {
alert(msg.Error);
clearinfo();
isLoginButtonPressed = false;
upnode.value = "";
upnode.focus();
return;
} else {
window.opener = null;
window.open('', '_self', '');
window.close();
}
}
}
//处理用户信息
function HandleCardUerInfoResult(msg) {
if (msg.Result) {
//密码验证通过后,查看证书是否过期
getValidTimeFromCert(msg);
} else {
if (msg.Error && msg.Error != "") {
alert(msg.Error);
clearinfo();
upnode.value = "";
upnode.focus();
isLoginButtonPressed = false;
return;
} else {
window.opener = null;
window.open('', '_self', '');
window.close();
}
}
}
/**
* 显示登录提示信息
*/
function showinfo() {
//infonode = $("info");
infonode = document.getElementById("info");
if (infonode != null) {
try {
infonode.style.display = "block";
infonode.style.backgroundColor = "yellow";
var text = $CT("登录中,请稍等");
infonode.appendChild(text);
} catch (ex) {
//alert(ex);
}
}
}
/**
* 检查是否安装控件
*/
function isInstalled() {
res = false;
EportClient.isInstalled("iKey", function (msg) {
if (msg.Result) {
//已经安装完成了控件
//document.getElementById("download_widget").style.display = "none";
res = true;
} else {
if (msg.Error && msg.Error != "") {
document.getElementById("password").value = ""
document.getElementById("password").disabled = "true";
isLoginButtonPressed = false;
if ("undefined" == typeof delayAlertSetupControl) {
if (confirm("请下载安装IC卡/Key客户端控件,否则,将无法使用IC卡/Key")) {
downEportIkeySetup();
}
} else {
delayAlertSetupControl();
}
} else {
window.opener = null;
window.open('', '_self', '');
window.close();
}
res = false;
}
}, "http://iKey下载地址")
return res;
}
/**
* 单向认证标志
*/
var UNILATERAL_TYPE = "1";
/**
* 同步状态信息:将$("status")展现到 $("info")里
*/
function syncStatus() {
var errmsg = document.getElementById("status");
if (errmsg != null) {
infonode = document.getElementById("info");
infonode.style.display = "block";
var errmsgValue = $T("status");
var err0 = errmsgValue.split("|")[0];
var err1 = errmsgValue.split("|")[1];
if (err0 == "5") {
errmsgValue = "卡信息异常,请尝试用证书延期方式更正!";
if (document.getElementById('error_attach_info') != null) {
errmsgValue = errmsgValue + "\n("
+ document.getElementById('error_attach_info').innerText + ")";
}
} else if (err0 == "4") {
errmsgValue = errmsgValue.substring(2) + document.getElementById('error_black_info').innerText;
} else if (err0 == "0") {
errmsgValue = document.getElementById('server_error_para').innerText;
} else if (err0 == "6") {
errmsgValue = err1 + "(" + document.getElementById('server_error_para').innerText + ")";
}
var text = $CT(errmsgValue);
infonode.appendChild(text);
infonode.style.display = "block";
infonode.style.backgroundColor = "yellow";
}
}
/**
* 下载控件
*/
function downEportIkeySetup() {
window.open(SwVersion.getIkeyDownloadUrl(), '_blank');
}
//开始登录
function startLogin(msg) {
var ary = new Array();
var user = (msg.Data)[0];
ary = user.split("||");
//$("icCard").value=ary[5];
//$("certNo").value=ary[0];
//$("fm1").submit();
document.getElementById("icCard").value = ary[5];
document.getElementById("certNo").value = ary[0];
try {
userpassword = tomf(userpassword);
} catch (exception) {
}
try {
sbPre();
} catch (e) {
}
document.getElementById("userPin").value = userpassword;
document.getElementById("fm1").submit();
//EportClient.spcClearEnv();
}
function tomf(value) {
//var stkey = document.getElementById("lt").value;
//stkey= stkey.substring(0,16);
//var ivec = stkey;
//var s4=new SM4Util(stkey,ivec);
//return s4.encryptData_CBC(value);
var userSmDt = "";
var objSm = document.getElementById("user_sm_dt");
if (objSm != null && typeof (objSm) != undefined) {
userSmDt = objSm.innerText;
}
var res = value;
if (userSmDt != "") {
res = CaData_KXC(value, userSmDt); //20211022
setFormValue("sgm", "1");
}
return res;
}
function setFormValue(name, val) {
var obj = document.getElementsByName(name);
if (obj.length == 1) {
document.getElementById(name).value = val;
} else {
var n = 0;
for (n = 0; n < obj.length; n++) {
obj[n].value = val;
}
}
}
var errorTipDate = 30;
var symbol1 = "/";
var symbol2 = ":";
var symbol3 = " ";
/**
* 查看证书有效期
*
*/
function getValidTimeFromCert(param) {
EportClient.isInstalledTest(EportClient.cusSpcGetValidTimeFromCert, function (msg, msgJson) {
if (msg && !msg.Result) {
clearinfo();
return;
}
var szEndTime = msg.Data[0].szEndTime;//20190120195948
var endTime = changeDateForm(szEndTime);//"2019/01/20 10:15:00";
if (!endTime) {
clearinfo();
return;
}
var overDueDate = getOverDueDate(endTime);
if (overDueDate > 0 && overDueDate <= errorTipDate) {
alert("您的IC卡还有" + overDueDate + "天到期,请尽快延期!");
startLogin(param);
return;
}
if (overDueDate <= 0) {
alert("您的IC卡已过期!");
clearinfo();
return;
}
startLogin(param);
});
}
/**
* 改变日期格式
*"2019/01/20 10:15:00";
*/
function changeDateForm(endTime) {//20190114195948
if (endTime.length != 14) {
return "";
}
var year = endTime.substring(0, 4);
var month = endTime.substring(4, 6);
var date = endTime.substring(6, 8);
var hour = endTime.substring(8, 10);
var minute = endTime.substring(10, 12);
var second = endTime.substring(12, 14);
return year + symbol1 + month + symbol1 + date + symbol3 + hour + symbol2 + minute + symbol2 + second;
}
/**
* 获取过期 天数
*/
function getOverDueDate(endTime) {//"2018-01-20 10:15:00"
//结束时间
// 2019/01/20 10:15:00
endStr = (endTime).replace(/-/g, "/");//一般得到的时间的格式都是:yyyy-MM-dd hh24:mi:ss,所以我就用了这个做例子,是/的格式,就不用replace了。
var endDate = new Date(endStr);//将字符串转化为时间
//开始时间
var staDate = new Date();
var num = (endDate - staDate) / (1000 * 3600 * 24);//求出两个时间的时间差,这个是天数
var days = parseInt(Math.ceil(num));//转化为整天(小于零的话就不用转了)
return days;
}
/**
*20191210 添加判断白读卡器新增接口 by lyy
*/
/**
* 判读读卡器类型
*/
function RAIs903Reader(msg, msgJson, strPwd) {
if (msg.Result) {
if ((msg.Data)[0] == "1") {
identifyFirstStep(strPwd);
} else if ((msg.Data)[0] == "0") {
identifyOldStep(strPwd);
}
} else {
identifyOldStep(strPwd);
}
}
/**
* 原流程
*/
function identifyOldStep(strPwd) {
EportClient.isInstalledTest(EportClient.spcSignRandom, strPwd, document.getElementById("random").value, function (msg) {
HandleSignResult(msg);
});
}
/**
* 白读卡器身份认证第一步
*/
function identifyFirstStep(strPwd) {
//身份认证第一步
EportClient.isInstalledTest(EportClient.raidentifyFirstStep, document.getElementById("random").value, strPwd, function (msg, msgJson) {
RAIs903Result(msg, msgJson);
});
}
function RAIs903Result(msg, msgJson) {
if (msg.Result) {
document.getElementById("signData").value = (msg.Data)[1];
VerifyPinAndGetCardUserInfo();
} else {
isLoginButtonPressed = false;
alert(msg.Error);
}
}
function VerifyPinAndGetCardUserInfo() {
//校验密码获取当前卡信息
EportClient.isInstalledTest(EportClient.raVerifyPinAndGetCardUserInfo, "1", $V("password"), function (msg, msgJson) {
RAIs903GetCert(msg, msgJson);
});
}
/**
* 903获取证书
*/
function RAIs903GetCert(msg, msgJson) {
if (msg.Result) {
if ((msg.Data)[1].VerifyPINSuccess) {
var ary = new Array();
var user = (msg.Data)[0];
ary = user.split("||");
var szEndTime = ary[12];//20190120195948
var endTime = changeDateForm(szEndTime);//"2019/01/20 10:15:00";
if (!endTime) {
clearinfo();
return;
}
var overDueDate = getOverDueDate(endTime);
if (overDueDate > 0 && overDueDate <= errorTipDate) {
alert("您的IC卡还有" + overDueDate + "天到期,请尽快延期!");
startLogin(msg);
return;
}
if (overDueDate <= 0) {
alert("您的IC卡已过期!");
clearinfo();
return;
}
startLogin(msg);
} else {
isLoginButtonPressed = false;
alert((msg.Data)[1].Error);
}
} else {
isLoginButtonPressed = false;
alert(msg.Error);
}
}
+1
View File
@@ -0,0 +1 @@
.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}
+65
View File
@@ -0,0 +1,65 @@
<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>
@@ -0,0 +1,115 @@
<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>
@@ -0,0 +1,156 @@
package cn.alphahub.eport.signature;
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
public 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\"\n" +
" version=\"1.0\">\n" +
" <link type=\"text/css\" id=\"dark-mode\" rel=\"stylesheet\" href=\"\"/>\n" +
" <ceb:Order>\n" +
" <ceb:OrderHead>\n" +
" <ceb:guid>4CDE1CFD-EDED-46B1-946C-B8022E42FC94</ceb:guid>\n" +
" <ceb:appType>1</ceb:appType>\n" +
" <ceb:appTime>20160308112701</ceb:appTime>\n" +
" <ceb:appStatus>2</ceb:appStatus>\n" +
" <ceb:orderType>I</ceb:orderType>\n" +
" <ceb:orderNo>order20160321116421002</ceb:orderNo>\n" +
" <ceb:ebpCode>1105910159</ceb:ebpCode>\n" +
" <ceb:ebpName>东方物通科技(北京)有限公司</ceb:ebpName>\n" +
" <ceb:ebcCode>1105910159</ceb:ebcCode>\n" +
" <ceb:ebcName>东方物通科技(北京)有限公司</ceb:ebcName>\n" +
" <ceb:goodsValue>14000</ceb:goodsValue>\n" +
" <ceb:freight>5000</ceb:freight>\n" +
" <ceb:discount>0</ceb:discount>\n" +
" <ceb:taxTotal>50</ceb:taxTotal>\n" +
" <ceb:acturalPaid>19050</ceb:acturalPaid>\n" +
" <ceb:currency>142</ceb:currency>\n" +
" <ceb:buyerRegNo>ID20160001</ceb:buyerRegNo>\n" +
" <ceb:buyerName>aa</ceb:buyerName>\n" +
" <ceb:buyerTelephone>18813025940</ceb:buyerTelephone>\n" +
" <ceb:buyerIdType>1</ceb:buyerIdType>\n" +
" <ceb:buyerIdNumber>130681136250023332</ceb:buyerIdNumber>\n" +
" <ceb:payCode>1105910159</ceb:payCode>\n" +
" <ceb:payName>东方物通科技(北京)有限公司</ceb:payName>\n" +
" <ceb:payTransactionId>20160001634226001</ceb:payTransactionId>\n" +
" <ceb:batchNumbers>20160317</ceb:batchNumbers>\n" +
" <ceb:consignee>焦洪宇</ceb:consignee>\n" +
" <ceb:consigneeTelephone>13522652231</ceb:consigneeTelephone>\n" +
" <ceb:consigneeAddress>北京市海淀区</ceb:consigneeAddress>\n" +
" <ceb:consigneeDistrict>072750</ceb:consigneeDistrict>\n" +
" <ceb:note>test</ceb:note>\n" +
" </ceb:OrderHead>\n" +
" <ceb:OrderList>\n" +
" <ceb:gnum>1</ceb:gnum>\n" +
" <ceb:itemNo>AF001-001</ceb:itemNo>\n" +
" <ceb:itemName>b</ceb:itemName>\n" +
" <ceb:gmodel>33mm</ceb:gmodel>\n" +
" <ceb:itemDescribe>v</ceb:itemDescribe>\n" +
" <ceb:barCode>2345123</ceb:barCode>\n" +
" <ceb:unit>007</ceb:unit>\n" +
" <ceb:qty>100</ceb:qty>\n" +
" <ceb:price>20</ceb:price>\n" +
" <ceb:totalPrice>2000</ceb:totalPrice>\n" +
" <ceb:currency>142</ceb:currency>\n" +
" <ceb:country>116</ceb:country>\n" +
" <ceb:note/>\n" +
" </ceb:OrderList>\n" +
" </ceb:Order>\n" +
" <ceb:BaseTransfer>\n" +
" <ceb:copCode>1101180326</ceb:copCode>\n" +
" <ceb:copName>物流企业</ceb:copName>\n" +
" <ceb:dxpMode>DXP</ceb:dxpMode>\n" +
" <ceb:dxpId>EXP2016522002580001</ceb:dxpId>\n" +
" <ceb:note>test</ceb:note>\n" +
" </ceb:BaseTransfer>\n" +
"</ceb:CEB311Message>";
String messageWithC14n = SignatureHandler.getDigestValueOfCEBXxxMessage(sourceXml);
System.err.println(messageWithC14n);
assert "vDf4oKPVFtRft5U57TNW85kJT18=".equals(messageWithC14n);
}
@Test
void testSha1Hex() {
String xml = "<ceb:CEB621Message xmlns:ceb=\"http://www.chinaport.gov.cn/ceb\" guid=\"CEB621_HNZB_FXJK_20220208175054_0034\"\n" +
" version=\"v1.0\">\n" +
" <ceb:Inventory>\n" +
" <ceb:InventoryHead>\n" +
" <ceb:guid>CEB621_HNZB_FXJK_20220208175055_0035</ceb:guid>\n" +
" <ceb:appType>1</ceb:appType>\n" +
" <ceb:appTime>20220208175055</ceb:appTime>\n" +
" <ceb:appStatus>2</ceb:appStatus>\n" +
" <ceb:orderNo>48037605991776256</ceb:orderNo>\n" +
" <ceb:ebpCode>46121601BC</ceb:ebpCode>\n" +
" <ceb:ebpName>海南星创互联网医药有限公司</ceb:ebpName>\n" +
" <ceb:ebcCode>46121601BC</ceb:ebcCode>\n" +
" <ceb:ebcName>海南星创互联网医药有限公司</ceb:ebcName>\n" +
" <ceb:logisticsNo>2222222</ceb:logisticsNo>\n" +
" <ceb:logisticsCode>4101986180</ceb:logisticsCode>\n" +
" <ceb:logisticsName>圆通</ceb:logisticsName>\n" +
" <ceb:copNo>4101W68006</ceb:copNo>\n" +
" <ceb:assureCode>4101960AGP</ceb:assureCode>\n" +
" <ceb:ieFlag>I</ceb:ieFlag>\n" +
" <ceb:declTime>20220208</ceb:declTime>\n" +
" <ceb:customsCode>4601</ceb:customsCode>\n" +
" <ceb:portCode>4601</ceb:portCode>\n" +
" <ceb:ieDate>20220208</ceb:ieDate>\n" +
" <ceb:buyerIdType>1</ceb:buyerIdType>\n" +
" <ceb:buyerIdNumber>612326199005306719</ceb:buyerIdNumber>\n" +
" <ceb:buyerName>用户_15150368</ceb:buyerName>\n" +
" <ceb:buyerTelephone>18209182500</ceb:buyerTelephone>\n" +
" <ceb:consigneeAddress>哈布斯堡</ceb:consigneeAddress>\n" +
" <ceb:agentCode>46121601BC</ceb:agentCode>\n" +
" <ceb:agentName>海南星创互联网医药有限公司</ceb:agentName>\n" +
" <ceb:tradeMode>9610</ceb:tradeMode>\n" +
" <ceb:trafMode>W</ceb:trafMode>\n" +
" <ceb:country>142</ceb:country>\n" +
" <ceb:freight>0.00</ceb:freight>\n" +
" <ceb:insuredFee>0</ceb:insuredFee>\n" +
" <ceb:currency>142</ceb:currency>\n" +
" <ceb:packNo>1</ceb:packNo>\n" +
" <ceb:grossWeight>0.07</ceb:grossWeight>\n" +
" <ceb:netWeight>0.06</ceb:netWeight>\n" +
" </ceb:InventoryHead>\n" +
" <ceb:InventoryList>\n" +
" <ceb:gnum>1</ceb:gnum>\n" +
" <ceb:itemRecordNo>0797776159482</ceb:itemRecordNo>\n" +
" <ceb:itemNo>HNGGKJ00000057</ceb:itemNo>\n" +
" <ceb:itemName>通用铁剂胶囊</ceb:itemName>\n" +
" <ceb:gcode>2106909090</ceb:gcode>\n" +
" <ceb:gname>ACTIVE IRON</ceb:gname>\n" +
" <ceb:gmodel>4|3|乳清蛋白(牛奶)65%,胶囊壳(羟苯基甲基纤维素)21%,硫酸亚铁1.2%,酸度调节剂2%,填充剂 10.8%|30粒/盒|无中文名称/ACTIVE IRON</ceb:gmodel>\n" +
" <ceb:country>306</ceb:country>\n" +
" <ceb:currency>142</ceb:currency>\n" +
" <ceb:qty>1</ceb:qty>\n" +
" <ceb:unit>140</ceb:unit>\n" +
" <ceb:qty1>0.06</ceb:qty1>\n" +
" <ceb:unit1>035</ceb:unit1>\n" +
" <ceb:price>0.01</ceb:price>\n" +
" <ceb:totalPrice>0.01</ceb:totalPrice>\n" +
" </ceb:InventoryList>\n" +
" </ceb:Inventory>\n" +
" <ceb:BaseTransfer>\n" +
" <ceb:copCode>46121601BC</ceb:copCode>\n" +
" <ceb:copName>海南星创互联网医药有限公司</ceb:copName>\n" +
" <ceb:dxpMode>DXP</ceb:dxpMode>\n" +
" <ceb:dxpId>DXPENT0000458763</ceb:dxpId>\n" +
" </ceb:BaseTransfer>\n" +
"</ceb:CEB621Message>";
}
}
@@ -0,0 +1,13 @@
package cn.alphahub.eport.signature;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class ChinaEportSignatureApplicationTests {
@Test
void contextLoads() {
}
}
@@ -0,0 +1,43 @@
package cn.alphahub.eport.signature.client;
import cn.alphahub.eport.signature.config.InitialConfig;
import cn.alphahub.eport.signature.config.InitialConfigTest;
import cn.alphahub.eport.signature.core.EportSignHandler;
import cn.alphahub.eport.signature.entity.SignRequest;
import cn.alphahub.eport.signature.entity.SignResult;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
/**
* WebSocketClient测试类
*
* @author weasley
* @version 1.0
* @date 2022/2/12
*/
@Slf4j
@SpringBootTest
class StandardEportSignHandlerTest {
@Test
void sendInitDataAsPEMWithHashPTest() {
SignRequest request = new SignRequest(null, InitialConfigTest.CEB621Message);
String parameter = InitialConfig.getSignDataAsPEMParameter(request);
EportSignHandler client = new EportSignHandler();
SignResult result = client.sign(request, parameter);
System.out.println(JSONUtil.toJsonPrettyStr(result));
}
@Test
void sendInitDataNoHashAsPEMParameterTest() {
SignRequest request = new SignRequest(null, InitialConfigTest.CEB621Message);
String parameter = InitialConfig.getSignDataNoHashAsPEMParameter(request);
EportSignHandler client = new EportSignHandler();
SignResult result = client.sign(request, parameter);
System.out.println(JSONUtil.toJsonPrettyStr(result));
}
}
@@ -0,0 +1,70 @@
package cn.alphahub.eport.signature.config;
import cn.alphahub.eport.signature.entity.SignRequest;
import cn.hutool.core.io.IoUtil;
import cn.hutool.crypto.digest.DigestUtil;
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;
import org.springframework.util.ResourceUtils;
import java.io.File;
import java.io.FileNotFoundException;
import java.nio.charset.StandardCharsets;
@SpringBootTest
public class InitialConfigTest {
/**
* CEB621Message
*/
public static String CEB621Message;
static {
try {
File CEB621MessageFile = ResourceUtils.getFile("classpath:xml/CEB621Message.xml");
CEB621Message = IoUtil.read(IoUtil.toStream(CEB621MessageFile), StandardCharsets.UTF_8);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
@BeforeEach
void setUp() {
}
@AfterEach
void tearDown() {
}
@Test
void getX509CertificateParameter() {
System.err.println(InitialConfig.getX509CertificateParameter());
}
@Test
void getDigestValueParameter() {
System.err.println(InitialConfig.getDigestValueParameter(CEB621Message, 1));
}
@Test
void getSignatureValueParameter() {
System.err.println(InitialConfig.getSignDataAsPEMParameter(new SignRequest(1, CEB621Message)));
}
@Test
void getSignDataNoHashAsPEMParameter() {
System.err.println(InitialConfig.getSignDataNoHashAsPEMParameter(new SignRequest(1, CEB621Message)));
}
@Test
void getVerifySignDataNoHashParameter() {
String sha1DigestOfSourceXml = DigestUtil.sha1Hex(CEB621Message);
System.err.println(sha1DigestOfSourceXml.length() + " " + sha1DigestOfSourceXml + " " + InitialConfig.getVerifySignDataNoHashParameter(CEB621Message, "sv", null, 1));
sha1DigestOfSourceXml = org.apache.commons.codec.digest.DigestUtils.sha1Hex(CEB621Message);
System.err.println(sha1DigestOfSourceXml.length() + " " + sha1DigestOfSourceXml + " " + InitialConfig.getVerifySignDataNoHashParameter(CEB621Message, "sv", null, 1));
}
}
@@ -0,0 +1,4 @@
package cn.alphahub.eport.signature.core;
public class Generate {
}
@@ -0,0 +1,39 @@
package cn.alphahub.eport.signature.core;
import cn.hutool.core.io.IoUtil;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.util.ResourceUtils;
import java.io.File;
import java.io.FileNotFoundException;
import java.nio.charset.StandardCharsets;
/**
* 核心方法测试
*/
@SpringBootTest
class SignatureValueHandlerTest {
/**
* CEB621Message
*/
public static String CEB621Message;
static {
try {
File CEB621MessageFile = ResourceUtils.getFile("classpath:xml/CEB621Message.xml");
CEB621Message = IoUtil.read(IoUtil.toStream(CEB621MessageFile), StandardCharsets.UTF_8);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
/*@Resource
WebsocketSignatureService service;
@Test
void getSignatureValueBeforeSendToWebSocket() {
SignResult sign = service.sign(new SignRequest(CEB621Message));
System.err.println(JSONUtil.toJsonPrettyStr(sign));
}*/
}