开始之前先说一点题外话,几年前曾经看过一个视频,其中一个观点大概就是程序员是一个反传统的群体,其他群体掌握了某个技术,一般都是当做内部商业机密,而程序员则不一样,喜欢开源,尤其 GPL 协议的开源,不仅自己毫无保留的开源,还要求使用他的软件也得开源,也正是这种开源造就了互联网的蓬勃发展。我目前所在公司因为是做金融相关的公司,国家出于某些原因,要求要上报相关的数据到省平台,而省平台的技术采用的是 webservice,和我们目前习惯的 http 接口不太一样,所以前一段时间在写这个的时候走了不少弯路,而网上也没有参考资料,所以决定把相关的核心代码公布出来,供需要的同学参考。需要说明的是:这是我们河南省的系统相关接口,不知道外省是否一致,省平台给的接口文档名是:全国中小企业融资综合信用服务平台省级节点数据接口规范V5.3.pdf,首页写的是:全国中小企业融资综合信用服务平台省级节点数据接口规范,国家公共信用信息中心,河南省营商环境和社会信用建设中心,2024年7月

  1. jar 包引入

<dependency>  
<groupId>cn.hutool</groupId>  
<artifactId>hutool-all</artifactId>  
<version>5.8.10</version>  
</dependency>  
<dependency>  
<groupId>org.bouncycastle</groupId>  
<artifactId>bcprov-jdk15on</artifactId>  
<version>1.70</version>  
</dependency>  
<dependency>  
<groupId>org.bouncycastle</groupId>  
<artifactId>bcpkix-jdk15on</artifactId>  
<version>1.70</version>  
</dependency>  
<dependency>  
<groupId>org.apache.cxf</groupId>  
<artifactId>cxf-rt-frontend-jaxws</artifactId>  
<version>4.0.5</version>  
</dependency>  
<dependency>  
<groupId>org.apache.cxf</groupId>  
<artifactId>cxf-rt-transports-http</artifactId>  
<version>4.0.5</version>  
</dependency>
  1. 上报工具类,其中:queryData 方法是用来查询,sendData 方法用来上报数据

package cn.bridgeli.demo;

import cn.hutool.core.util.RandomUtil;  
import com.alibaba.fastjson.JSONObject;  
import lombok.extern.slf4j.Slf4j;  
import org.apache.commons.codec.CharEncoding;  
import org.apache.commons.lang3.StringUtils;  
import org.apache.cxf.endpoint.Client;  
import org.apache.cxf.jaxws.endpoint.dynamic.JaxWsDynamicClientFactory;  
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;  
import org.bouncycastle.util.encoders.Base64;  
import org.bouncycastle.util.encoders.Hex;

import javax.xml.namespace.QName;  
import java.io.File;  
import java.io.FileInputStream;  
import java.io.FileOutputStream;  
import java.io.IOException;  
import java.io.InputStream;  
import java.math.BigInteger;  
import java.security.PublicKey;

@Slf4j  
public class XyhnUtil {

private static String pubKey;  
private static String queryPubKey;

private static PublicKey reportPublicKey;

// private static XyhnConfig xyhnConfig = SpringUtils.getBean(XyhnConfig.class);  
private static XyhnConfig xyhnConfig = null;

/**  
* 授权查询省平台接口  
*  
* @param method 要调取的方法名  
* @param object 查询方法的参数(不包含:publicKey、appKey)  
* @return 省平台解密后的数据  
*/  
public static String queryData(String method, JSONObject object) {  
//1、组装报文  
if (StringUtils.isEmpty(queryPubKey)) {  
queryPubKey = convertFileToBase64(xyhnConfig.getQueryPubKeyPath());  
}

object.put("publicKey", queryPubKey);  
object.put("appKey", xyhnConfig.getQueryAppKey());  
//2、发送报文  
String jsonRes = callInterface("query", method, object.toJSONString());

//3、解密返回数据  
JSONObject json = JSONObject.parseObject(jsonRes);  
Boolean success = json.getBoolean("success");  
if ("uploadLicense".equals(method)) {  
if (null == success || !success) {  
return jsonRes;  
}  
} else if ("cancelLicense".equals(method)) {  
if (null != success && success) {  
return jsonRes;  
}  
}

BigInteger d = new BigInteger(xyhnConfig.getQueryPriKey(), 16);  
BCECPrivateKey bcecPrivateKey = GMUtil.getPrivatekeyFromD(d);  
String key0 = json.getString("key");  
String data = json.getString("data");  
String signatureData = json.getString("signatureData");  
byte[] decode = Hex.decode(key0);  
// sm2解密  
byte[] bytes1 = GMUtil.sm2Decrypt(decode, bcecPrivateKey);  
// sm4解密  
String content = GMUtil.sm4Decrypt(new String(bytes1), data);  
log.info("content: " + content);  
// 4、验签  
File file = base64ToFileEx(json.getString("pubKey"));  
PublicKey publicKey = GMUtil.getPublickeyFromX509File(file);  
byte[] signatureData1 = Hex.decode(signatureData);  
boolean verifyRes = GMUtil.verifySm3WithSm2(content.getBytes(), xyhnConfig.getUserId().getBytes(), signatureData1, publicKey);  
log.info("method 验签结果:" + verifyRes);  
if (!verifyRes) {  
return null;  
}  
return content;  
}

private static PublicKey getPublicKey() {  
//1、组装报文  
if (StringUtils.isEmpty(pubKey)) {  
pubKey = convertFileToBase64(xyhnConfig.getPubKeyPath());  
}  
JSONObject jsonObject = new JSONObject();  
jsonObject.put("key", xyhnConfig.getAppKey());  
JSONObject object = new JSONObject();  
object.put("requestData", jsonObject);  
object.put("publicKey", pubKey);  
//2、发送报文  
String jsonRes = callInterface("report", "getPublicKey", object.toJSONString());

//3、解密返回数据  
JSONObject json = JSONObject.parseObject(jsonRes);  
BigInteger d = new BigInteger(xyhnConfig.getPriKey(), 16);  
BCECPrivateKey bcecPrivateKey = GMUtil.getPrivatekeyFromD(d);  
String key0 = json.getString("key");  
String data = json.getString("data");  
String signatureData = json.getString("signatureData");  
byte[] decode = Hex.decode(key0);  
// sm2解密  
byte[] bytes1 = GMUtil.sm2Decrypt(decode, bcecPrivateKey);  
// sm4解密  
String returnData = GMUtil.sm4Decrypt(new String(bytes1), data);  
log.info("returnData:" + returnData);  
// 4、验签  
File file = base64ToFileEx(returnData);  
PublicKey publicKey = GMUtil.getPublickeyFromX509File(file);  
byte[] signatureData1 = Hex.decode(signatureData);  
boolean verifyRes = GMUtil.verifySm3WithSm2(returnData.getBytes(), xyhnConfig.getUserId().getBytes(), signatureData1, publicKey);  
log.info("getPublicKey 接口验签结果:" + verifyRes);  
if (!verifyRes) {  
return null;  
}  
return publicKey;  
}

public static String sendData(String jsonStr, String method) {

log.info("调用省平台方法名:" + method + ",参数:" + jsonStr);

try {  
if (StringUtils.isEmpty(pubKey)) { // 解析公钥  
pubKey = convertFileToBase64(xyhnConfig.getPubKeyPath());  
}  
if (reportPublicKey == null) { //请求接口,获取公钥  
reportPublicKey = getPublicKey();  
}

// 1、摘要签名 sm2withsm3  
byte[] msg = jsonStr.getBytes(CharEncoding.ISO_8859_1);  
byte[] userIdBytes = xyhnConfig.getUserId().getBytes();  
BigInteger d = new BigInteger(xyhnConfig.getPriKey(), 16);  
BCECPrivateKey bcecPrivateKey = GMUtil.getPrivatekeyFromD(d);  
byte[] sig = GMUtil.signSm3WithSm2(msg, userIdBytes, bcecPrivateKey);  
String signature = Hex.toHexString(sig);

// 2、sm4加密数据报文  
String key1 = RandomUtil.randomString(16);  
String jsonobj = GMUtil.sm4Encrypt(key1, jsonStr);

//3、sm2加密16位随机码key  
byte[] datamsg = GMUtil.sm2Encrypt(key1.getBytes(), reportPublicKey);  
String s = Hex.toHexString(datamsg);

//4、组装并发送报文  
JSONObject jsonObject2 = new JSONObject();  
jsonObject2.put("requestData", jsonobj); // sm4加密的数据集  
jsonObject2.put("key", s); // sm2加密的16位随机码  
jsonObject2.put("signatureData", signature); // 签名  
jsonObject2.put("publicKey", pubKey); // 我的公钥  
jsonObject2.put("appKey", xyhnConfig.getAppKey()); // 数据授权的key

String setFPRes = callInterface("report", method, jsonObject2.toJSONString());  
return setFPRes;  
} catch (Exception ex) {  
log.error("请求省平台接口异常", ex);  
return null;  
}

}

/**  
* 请求接口  
*  
* @param type,report 回传数据,query 查询  
* @param method  
* @param json  
* @return  
*/  
private static String callInterface(String type, String method, String json) {  
log.info("调用省平台类型:" + type + ",接口:" + method + ",参数:" + json);  
Client client = null;  
QName name = null;  
JaxWsDynamicClientFactory dcf = JaxWsDynamicClientFactory.newInstance();  
String result = null;  
try {  
if ("report".equals(type)) {  
client = dcf.createClient(xyhnConfig.getWsAddr());  
name = new QName(xyhnConfig.getNamespaceURI(), method);  
} else if ("query".equals(type)) {  
client = dcf.createClient(xyhnConfig.getQueryWsAddr());  
name = new QName(xyhnConfig.getQueryNamespaceURI(), method);  
}

Object[] objects = client.invoke(name, json);  
result = objects[0].toString();

} catch (Exception e) {  
log.error("调用省平台接口报错", e);  
} finally {  
if (client != null) {  
try {  
client.close();  
} catch (Exception e) {  
log.error("关闭Client资源时发生异常", e);  
}  
}  
}

log.info("调用省平台类型:" + type + ",接口:" + method + ",返回值:" + result);

return result;  
}

private static String convertFileToBase64(String imgPath) {  
byte[] data = null;  
// 读取图片字节数组  
try (InputStream in = new FileInputStream(imgPath);) {  
data = new byte[in.available()];  
in.read(data);  
} catch (IOException e) {  
log.error("IOException", e);  
}  
// 对字节数组进行Base64编码,得到Base64编码的字符串  
String base64Str = java.util.Base64.getEncoder().encodeToString(data);  
return base64Str;  
}

private static File base64ToFileEx(String base64) {  
if (StringUtils.isBlank(base64)) {  
return null;  
}  
byte[] buff = Base64.decode(base64);  
File file = null;  
FileOutputStream fout = null;  
try {  
file = File.createTempFile("tmp", null);  
fout = new FileOutputStream(file);  
fout.write(buff);  
file.deleteOnExit();  
} catch (IOException e) {  
e.printStackTrace();  
} finally {  
if (fout != null) {  
try {  
fout.close();  
} catch (IOException e) {  
e.printStackTrace();  
}  
}  
}  
return file;  
}

}
  1. 用到的工具类

package cn.bridgeli.demo;

import lombok.extern.slf4j.Slf4j;  
import org.bouncycastle.asn1.ASN1EncodableVector;  
import org.bouncycastle.asn1.ASN1Integer;  
import org.bouncycastle.asn1.ASN1Sequence;  
import org.bouncycastle.asn1.DERSequence;  
import org.bouncycastle.asn1.gm.GMNamedCurves;  
import org.bouncycastle.asn1.x9.X9ECParameters;  
import org.bouncycastle.crypto.InvalidCipherTextException;  
import org.bouncycastle.crypto.engines.SM2Engine;  
import org.bouncycastle.crypto.params.ECDomainParameters;  
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;  
import org.bouncycastle.crypto.params.ECPublicKeyParameters;  
import org.bouncycastle.crypto.params.ParametersWithRandom;  
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;  
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;  
import org.bouncycastle.jcajce.spec.SM2ParameterSpec;  
import org.bouncycastle.jce.provider.BouncyCastleProvider;  
import org.bouncycastle.jce.spec.ECParameterSpec;  
import org.bouncycastle.jce.spec.ECPrivateKeySpec;  
import org.bouncycastle.util.encoders.Hex;

import javax.crypto.Cipher;  
import javax.crypto.spec.SecretKeySpec;  
import java.io.File;  
import java.io.FileInputStream;  
import java.io.IOException;  
import java.math.BigInteger;  
import java.nio.charset.StandardCharsets;  
import java.security.Key;  
import java.security.PrivateKey;  
import java.security.PublicKey;  
import java.security.SecureRandom;  
import java.security.Security;  
import java.security.Signature;  
import java.security.cert.CertificateFactory;  
import java.security.cert.X509Certificate;  
import java.util.Arrays;

@Slf4j  
public class GMUtil {  
private static X9ECParameters x9ECParameters = GMNamedCurves.getByName("sm2p256v1");  
private static ECDomainParameters ecDomainParameters;  
private static ECParameterSpec ecParameterSpec;

public static byte[] signSm3WithSm2(byte[] msg, byte[] userId, PrivateKey privateKey) {  
return rsAsn1ToPlainByteArray(signSm3WithSm2Asn1Rs(msg, userId, privateKey));  
}

public static byte[] signSm3WithSm2Asn1Rs(byte[] msg, byte[] userId, PrivateKey privateKey) {  
try {  
SM2ParameterSpec parameterSpec = new SM2ParameterSpec(userId);  
Signature signer = Signature.getInstance("SM3withSM2", "BC");  
signer.setParameter(parameterSpec);  
signer.initSign(privateKey, new SecureRandom());  
signer.update(msg, 0, msg.length);  
byte[] sig = signer.sign();  
return sig;  
} catch (Exception e) {  
throw new RuntimeException(e);  
}  
}

public static boolean verifySm3WithSm2(byte[] msg, byte[] userId, byte[] rs, PublicKey publicKey) {  
return verifySm3WithSm2Asn1Rs(msg, userId, rsPlainByteArrayToAsn1(rs), publicKey);  
}

public static boolean verifySm3WithSm2Asn1Rs(byte[] msg, byte[] userId, byte[] rs, PublicKey publicKey) {  
try {  
SM2ParameterSpec parameterSpec = new SM2ParameterSpec(userId);  
Signature verifier = Signature.getInstance("SM3withSM2", "BC");  
verifier.setParameter(parameterSpec);  
verifier.initVerify(publicKey);  
verifier.update(msg, 0, msg.length);  
return verifier.verify(rs);  
} catch (Exception e) {  
throw new RuntimeException(e);  
}  
}

public static byte[] changeC1C2C3ToC1C3C2(byte[] c1c2c3) {  
int c1Len = (x9ECParameters.getCurve().getFieldSize() + 7) / 8 * 2 + 1;

byte[] result = new byte[c1c2c3.length];  
System.arraycopy(c1c2c3, 0, result, 0, c1Len);  
System.arraycopy(c1c2c3, c1c2c3.length &#8211; 32, result, c1Len, 32);  
System.arraycopy(c1c2c3, c1Len, result, c1Len + 32, c1c2c3.length &#8211; c1Len &#8211; 32);  
return result;  
}

public static byte[] changeC1C3C2ToC1C2C3(byte[] c1c3c2) {  
int c1Len = (x9ECParameters.getCurve().getFieldSize() + 7) / 8 * 2 + 1;

byte[] result = new byte[c1c3c2.length];  
System.arraycopy(c1c3c2, 0, result, 0, c1Len);  
System.arraycopy(c1c3c2, c1Len + 32, result, c1Len, c1c3c2.length &#8211; c1Len &#8211; 32);  
System.arraycopy(c1c3c2, c1Len, result, c1c3c2.length &#8211; 32, 32);  
return result;  
}

public static byte[] sm2Decrypt(byte[] data, PrivateKey key) {  
return sm2DecryptOld(changeC1C3C2ToC1C2C3(data), key);  
}

public static byte[] sm2Encrypt(byte[] data, PublicKey key) {  
return changeC1C2C3ToC1C3C2(sm2EncryptOld(data, key));  
}

public static byte[] sm2EncryptOld(byte[] data, PublicKey key) {  
BCECPublicKey localECPublicKey = (BCECPublicKey) key;  
ECPublicKeyParameters ecPublicKeyParameters = new ECPublicKeyParameters(localECPublicKey.getQ(), ecDomainParameters);  
SM2Engine sm2Engine = new SM2Engine();  
sm2Engine.init(true, new ParametersWithRandom(ecPublicKeyParameters, new SecureRandom()));

try {  
return sm2Engine.processBlock(data, 0, data.length);  
} catch (InvalidCipherTextException e) {  
throw new RuntimeException(e);  
}  
}

public static byte[] sm2DecryptOld(byte[] data, PrivateKey key) {  
BCECPrivateKey localECPrivateKey = (BCECPrivateKey) key;  
ECPrivateKeyParameters ecPrivateKeyParameters = new ECPrivateKeyParameters(localECPrivateKey.getD(), ecDomainParameters);  
SM2Engine sm2Engine = new SM2Engine();  
sm2Engine.init(false, ecPrivateKeyParameters);

try {  
return sm2Engine.processBlock(data, 0, data.length);  
} catch (InvalidCipherTextException e) {  
throw new RuntimeException(e);  
}  
}

public static byte[] sm4Encrypt(byte[] keyBytes, byte[] plain) {  
byte[] keyBytes0;  
if (keyBytes.length != 16) {  
keyBytes0 = new byte[16];

for (int i = 0; i < keyBytes0.length; ++i) {  
if (keyBytes.length > i) {  
keyBytes0[i] = keyBytes[i];  
}  
}

keyBytes = keyBytes0;  
}

if (plain.length % 16 != 0) {  
keyBytes0 = new byte[16 * (plain.length / 16 + 1)];  
System.arraycopy(plain, 0, keyBytes0, 0, plain.length);  
plain = keyBytes0;  
}

try {  
Key key = new SecretKeySpec(keyBytes, "SM4");  
Cipher out = Cipher.getInstance("SM4/ECB/NoPadding", "BC");  
out.init(1, key);  
return out.doFinal(plain);  
} catch (Exception e) {  
throw new RuntimeException(e);  
}  
}

public static byte[] sm4Decrypt(byte[] keyBytes, byte[] cipher) {  
byte[] keyBytes0;  
if (keyBytes.length != 16) {  
keyBytes0 = new byte[16];

for (int i = 0; i < keyBytes0.length; ++i) {  
if (keyBytes.length > i) {  
keyBytes0[i] = keyBytes[i];  
}  
}

keyBytes = keyBytes0;  
}

if (cipher.length % 16 != 0) {  
keyBytes0 = new byte[16 * (cipher.length / 16 + 1)];  
System.arraycopy(cipher, 0, keyBytes0, 0, cipher.length);  
cipher = keyBytes0;  
}

try {  
Key key = new SecretKeySpec(keyBytes, "SM4");  
Cipher in = Cipher.getInstance("SM4/ECB/NoPadding", "BC");  
in.init(2, key);  
byte[] bytes = in.doFinal(cipher);

for (int i = bytes.length &#8211; 1; i >= 0; &#8211;i) {  
if (bytes[i] != 0) {  
byte[] bytes1 = new byte[i + 1];  
System.arraycopy(bytes, 0, bytes1, 0, i + 1);  
bytes = bytes1;  
i = -1;  
}  
}

return bytes;  
} catch (Exception e) {  
throw new RuntimeException(e);  
}  
}

public static String sm4Encrypt(String key, String plan) {  
byte[] keyBytes = new byte[16];  
byte[] keyBytes0 = key.getBytes(StandardCharsets.UTF_8);

for (int i = 0; i < keyBytes.length; ++i) {  
if (keyBytes0.length > i) {  
keyBytes[i] = keyBytes0[i];  
}  
}

byte[] cipher = plan.getBytes(StandardCharsets.UTF_8);

byte[] bytes = sm4Encrypt(keyBytes, cipher);  
return Hex.toHexString(bytes).toUpperCase();  
}

public static String sm4Decrypt(String key, String cipher) {  
byte[] keyBytes = new byte[16];  
byte[] keyBytes0 = key.getBytes(StandardCharsets.UTF_8);

for (int i = 0; i < keyBytes.length; ++i) {  
if (keyBytes0.length > i) {  
keyBytes[i] = keyBytes0[i];  
}  
}

byte[] cipherbytes = Hex.decode(cipher);  
byte[] bytes = sm4Decrypt(keyBytes, cipherbytes);

return new String(bytes, StandardCharsets.UTF_8);

}

private static byte[] bigIntToFixexLengthBytes(BigInteger rOrS) {  
byte[] rs = rOrS.toByteArray();  
if (rs.length == 32) {  
return rs;  
} else if (rs.length == 33 && rs[0] == 0) {  
return Arrays.copyOfRange(rs, 1, 33);  
} else if (rs.length < 32) {  
byte[] result = new byte[32];  
Arrays.fill(result, (byte) 0);  
System.arraycopy(rs, 0, result, 32 &#8211; rs.length, rs.length);  
return result;  
} else {  
throw new RuntimeException("err rs: " + Hex.toHexString(rs));  
}  
}

private static byte[] rsAsn1ToPlainByteArray(byte[] rsDer) {  
ASN1Sequence seq = ASN1Sequence.getInstance(rsDer);  
byte[] r = bigIntToFixexLengthBytes(ASN1Integer.getInstance(seq.getObjectAt(0)).getValue());  
byte[] s = bigIntToFixexLengthBytes(ASN1Integer.getInstance(seq.getObjectAt(1)).getValue());  
byte[] result = new byte[64];  
System.arraycopy(r, 0, result, 0, r.length);  
System.arraycopy(s, 0, result, 32, s.length);  
return result;  
}

private static byte[] rsPlainByteArrayToAsn1(byte[] sign) {  
if (sign.length != 64) {  
throw new RuntimeException("err rs. ");  
} else {  
BigInteger r = new BigInteger(1, Arrays.copyOfRange(sign, 0, 32));  
BigInteger s = new BigInteger(1, Arrays.copyOfRange(sign, 32, 64));  
ASN1EncodableVector v = new ASN1EncodableVector();  
v.add(new ASN1Integer(r));  
v.add(new ASN1Integer(s));

try {  
return (new DERSequence(v)).getEncoded("DER");  
} catch (IOException var5) {  
throw new RuntimeException(var5);  
}  
}  
}

public static BCECPrivateKey getPrivatekeyFromD(BigInteger d) {  
ECPrivateKeySpec ecPrivateKeySpec = new ECPrivateKeySpec(d, ecParameterSpec);  
return new BCECPrivateKey("EC", ecPrivateKeySpec, BouncyCastleProvider.CONFIGURATION);  
}

public static PublicKey getPublickeyFromX509File(File file) {  
try {  
CertificateFactory cf = CertificateFactory.getInstance("X.509", "BC");  
FileInputStream in = new FileInputStream(file);  
X509Certificate x509 = (X509Certificate) cf.generateCertificate(in);  
return x509.getPublicKey();  
} catch (Exception var4) {  
throw new RuntimeException(var4);  
}  
}

static {  
ecDomainParameters = new ECDomainParameters(x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN());  
ecParameterSpec = new ECParameterSpec(x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN());  
if (Security.getProvider("BC") == null) {  
Security.addProvider(new BouncyCastleProvider());  
}  
}  
}
  1. 相关配置

package cn.bridgeli.demo;

import lombok.Data;  
import org.springframework.boot.context.properties.ConfigurationProperties;  
import org.springframework.stereotype.Component;

@Data  
@Component  
@ConfigurationProperties(prefix = "xyhn")  
//@RefreshScope  
public class XyhnConfig {

private String userId;  
private String platformId;

private String appKey;  
private String priKey;  
private String wsAddr;  
private String namespaceURI;  
private String pubKeyPath;

private String queryAppKey;  
private String queryPriKey;  
private String queryWsAddr;  
private String queryNamespaceURI;  
private String queryPubKeyPath;  
}

以上是上报和查询数据的核心方法,下面是查询具体数据的封装

  1. 查询的接口:

package cn.bridgeli.demo;

public interface XyhnService {  
Response queryDatas(String param, String page, String size);  
}

package cn.bridgeli.demo;

import com.alibaba.fastjson.JSONObject;  
import lombok.extern.slf4j.Slf4j;  
import org.apache.commons.lang3.StringUtils;  
import org.w3c.dom.Document;  
import org.w3c.dom.Element;  
import org.w3c.dom.NamedNodeMap;  
import org.w3c.dom.NodeList;  
import org.xml.sax.InputSource;  
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;  
import javax.xml.parsers.DocumentBuilderFactory;  
import javax.xml.parsers.ParserConfigurationException;  
import java.io.IOException;  
import java.io.StringReader;  
import java.util.ArrayList;  
import java.util.HashMap;  
import java.util.List;  
import java.util.Map;

@Slf4j  
public abstract class AbstractXyhnService implements XyhnService {

// private static ICompInfoService compInfoService = SpringUtils.getBean(ICompInfoService.class);

@Override  
public Response queryDatas(String enterpriseName, String page, String size) {

CompInfo compInfoParam = new CompInfo();  
// compInfoParam.setEnterpriseName(enterpriseName);  
// List<CompInfo> compInfos = compInfoService.selectCompInfoList(compInfoParam);  
// CompInfo compInfo = null;  
// if (CollectionUtils.isEmpty(compInfos)) {  
// compInfo = new CompInfo();  
// compInfo.setEnterpriseName(enterpriseName);  
// } else {  
// compInfo = compInfos.get(0);  
// }

List<QueryParam> list = buildQueryParam(compInfoParam);

QueryParam isDeleteParam = new QueryParam();  
isDeleteParam.setField_value("0");  
isDeleteParam.setField_name("$$_ISDELETE");  
isDeleteParam.setField_type("String");  
isDeleteParam.setField_symbol("=");  
list.add(isDeleteParam);

QueryParam stateParam = new QueryParam();  
stateParam.setField_value("0");  
stateParam.setField_name("$$_STATE");  
stateParam.setField_type("String");  
stateParam.setField_symbol("=");  
list.add(stateParam);

if (StringUtils.isBlank(size) || StringUtils.isBlank(page)) {  
size = "20";  
page = "1";  
}

JSONObject jsonObject = new JSONObject();  
buildTableIdParam(jsonObject);

jsonObject.put("query", list);  
jsonObject.put("page", page);  
jsonObject.put("size", size);

buildSortParam(jsonObject);

String data = XyhnUtil.queryData("queryPageDataByTableId", jsonObject);

return parseXml(data);  
}

protected abstract List<QueryParam> buildQueryParam(CompInfo compInfo);

protected abstract void buildTableIdParam(JSONObject jsonObject);

protected abstract void buildSortParam(JSONObject jsonObject);

public static Response parseXml(String xmlContent) {  
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();  
Document doc = null;  
try {  
DocumentBuilder builder = factory.newDocumentBuilder();  
doc = builder.parse(new InputSource(new StringReader(xmlContent)));  
} catch (ParserConfigurationException e) {  
log.error("ParserConfigurationException", e);  
} catch (IOException e) {  
log.error("IOException", e);  
} catch (SAXException e) {  
log.error("SAXException", e);  
}

if (null == doc) {  
return null;  
}

Response response = new Response();

Element root = doc.getDocumentElement();  
Element datas = (Element) root.getElementsByTagName("datas").item(0);

// Extract items  
NodeList rowList = datas.getElementsByTagName("row");  
if (rowList.getLength() <= 0) {  
return response;  
}

List<Map<String, String>> items = new ArrayList<>();  
for (int i = 0; i < rowList.getLength(); i++) {  
Element row = (Element) rowList.item(i);  
NodeList itemList = row.getElementsByTagName("item");  
if (itemList.getLength() == 0) {  
continue;  
}  
Map<String, String> itemMap = new HashMap<>();  
for (int j = 0; j < itemList.getLength(); j++) {  
Element item = (Element) itemList.item(j);  
String key = item.getAttribute("field");  
String value = item.getTextContent();  
itemMap.put(key, value);  
}  
items.add(itemMap);

}  
response.setData(items);

// Extract attributes from the &#8216;datas&#8217; tag  
NamedNodeMap attrs = datas.getAttributes();  
response.setStartIndex(Integer.parseInt(attrs.getNamedItem("startIndex").getNodeValue()));  
response.setTotalPage(Integer.parseInt(attrs.getNamedItem("totalPage").getNodeValue()));  
response.setTotalSize(Integer.parseInt(attrs.getNamedItem("totalSize").getNodeValue()));  
response.setTime(Integer.parseInt(attrs.getNamedItem("time").getNodeValue()));  
response.setSize(Integer.parseInt(attrs.getNamedItem("size").getNodeValue()));

log.info("解析成对象之后: " + JSONObject.toJSONString(response));

return response;  
}

}

查询的具体实现主要分三种:

① 普通查询,即公开公示的接口:


package cn.bridgeli.demo;

import com.alibaba.fastjson.JSONObject;  
import org.springframework.stereotype.Service;

import java.util.List;  
import java.util.stream.Collectors;  
import java.util.stream.Stream;

@Service  
public class GsAoOpaDetailServiceImpl extends AbstractXyhnService {  
@Override  
protected List<QueryParam> buildQueryParam(CompInfo compInfo) {  
QueryParam queryParam = new QueryParam();  
queryParam.setField_value(compInfo.getEnterpriseName());  
queryParam.setField_type("String");  
queryParam.setField_symbol("=");  
queryParam.setField_name("ENTNAME");  
return Stream.of(queryParam).collect(Collectors.toList());  
}

@Override  
protected void buildTableIdParam(JSONObject jsonObject) {  
jsonObject.put("tableId", "");  
}

@Override  
protected void buildSortParam(JSONObject jsonObject) {  
// jsonObject.put("sort", Stream.of("$$_MDATE").collect(Collectors.toList()));  
}  
}

② 普通授权接口


package cn.bridgeli.demo;

import com.alibaba.fastjson.JSONObject;  
import org.springframework.stereotype.Service;

import javax.annotation.Resource;  
import java.util.List;  
import java.util.stream.Collectors;  
import java.util.stream.Stream;

@Service  
public class ZxqySbbxfjnServiceImpl extends AbstractXyhnService {  
@Resource  
private XYHNConfig xyhnConfig;

@Override  
protected List<QueryParam> buildQueryParam(CompInfo compInfo) {

if (!"0".equals(compInfo.getCancelAuthState())) {  
throw new ServiceException("接口未授权,请先授权");  
}  
String licenseId = compInfo.getLicenseId();  
if (StringUtils.isBlank(licenseId)) {  
throw new ServiceException("接口未授权,无法调用");  
}

QueryParam queryParam = new QueryParam();  
queryParam.setField_value(compInfo.getEnterpriseName());  
queryParam.setField_type("String");  
queryParam.setField_symbol("=");  
queryParam.setField_name("enterpriseName");

QueryParam licenseIdParam = new QueryParam();  
licenseIdParam.setField_value(licenseId);  
licenseIdParam.setField_name("licenseId");  
licenseIdParam.setField_type("String");  
licenseIdParam.setField_symbol("=");

QueryParam uniscIdParam = new QueryParam();  
uniscIdParam.setField_value(compInfo.getUniscId());  
uniscIdParam.setField_name("uniscId");  
uniscIdParam.setField_type("String");  
uniscIdParam.setField_symbol("=");

QueryParam platformIdParam = new QueryParam();  
platformIdParam.setField_value(xyhnConfig.getPlatformId());  
platformIdParam.setField_name("platformId");  
platformIdParam.setField_type("String");  
platformIdParam.setField_symbol("=");

QueryParam searchOrgCodeParam = new QueryParam();  
// searchOrgCodeParam.setField_value(compInfo.getAuthorizedOrgsCode());  
searchOrgCodeParam.setField_name("searchOrgCode");  
searchOrgCodeParam.setField_type("String");  
searchOrgCodeParam.setField_symbol("=");

return Stream.of(queryParam, licenseIdParam, uniscIdParam, platformIdParam, searchOrgCodeParam).collect(Collectors.toList());  
}

@Override  
protected void buildTableIdParam(JSONObject jsonObject) {  
jsonObject.put("tableId", "");  
}

@Override  
protected void buildSortParam(JSONObject jsonObject) {

}  
}

③ 省授权接口


package cn.bridgeli.demo;

import com.alibaba.fastjson.JSONObject;  
import org.springframework.stereotype.Service;

import javax.annotation.Resource;  
import java.util.List;  
import java.util.stream.Collectors;  
import java.util.stream.Stream;

@Service  
public class SrstCbdwsbjfxxServiceImpl extends AbstractXyhnService {

@Resource  
private XYHNConfig xyhnConfig;

// searchSubject 和 searchSubjectCode,对应 getAuth 接口的 searchSubject 和 searchSubjectCode  
@Override  
protected List<QueryParam> buildQueryParam(CompInfo compInfo) {

String authId = compInfo.getAuthId();  
if (StringUtils.isBlank(authId)) {  
throw new ServiceException("请先获取省授权");  
}  
Date validDate = compInfo.getValidDate();

QueryParam searchSubjectParam = new QueryParam();  
searchSubjectParam.setField_value(compInfo.getEnterpriseName());  
searchSubjectParam.setField_type("String");  
searchSubjectParam.setField_symbol("=");  
searchSubjectParam.setField_name("aab004");

QueryParam searchSubjectCodeParam = new QueryParam();  
searchSubjectCodeParam.setField_value(compInfo.getUniscId());  
searchSubjectCodeParam.setField_type("String");  
searchSubjectCodeParam.setField_symbol("=");  
searchSubjectCodeParam.setField_name("aab039");

QueryParam authKeyParam = new QueryParam();  
authKeyParam.setField_value(xyhnConfig.getQueryAppKey());  
authKeyParam.setField_name("authKey");  
authKeyParam.setField_type("String");  
authKeyParam.setField_symbol("=");

QueryParam authIDParam = new QueryParam();  
authIDParam.setField_value(authId);  
authIDParam.setField_name("authID");  
authIDParam.setField_type("String");  
authIDParam.setField_symbol("=");

return Stream.of(searchSubjectParam, searchSubjectCodeParam, authKeyParam, authIDParam).collect(Collectors.toList());  
}

@Override  
protected void buildTableIdParam(JSONObject jsonObject) {  
jsonObject.put("tableId", "");  
}

@Override  
protected void buildSortParam(JSONObject jsonObject) {

}  
}
  1. 查询参数的封装:

package cn.bridgeli.demo;

import lombok.Data;

@Data  
public class QueryParam {  
private String field_name;  
private String field_type;  
private String field_value;  
private String field_symbol;  
private String field_grantmapping;  
}
  1. 返回值的封装

package cn.bridgeli.demo;

import lombok.Data;

import java.util.List;  
import java.util.Map;

@Data  
public class Response {

private Integer startIndex;  
private Integer totalPage;  
private Integer totalSize;  
private Integer time;  
private Integer size;  
private List<Map<String, String>> data;  
}
  1. 接口类型

package cn.bridgeli.demo;

import lombok.Getter;

@Getter  
public enum FaGaiWeiInterfaceEnum {

gsQyjbxxServiceImpl("社会公开", "工商企业基本信息(带行业代码)"),  
zxqyQysfqsServiceImpl("授权查询", "中小企业融资-企业是否欠税"),  
zrtBdcqdjxxFdcqServiceImpl("授权查询", "不动产_不动产权登记信息_房地产权信息-省授权"),  
mztHydjxxServiceImpl("社会公开", "婚姻登记信息-省授权"),  
;

FaGaiWeiInterfaceEnum(String openType, String description) {  
this.openType = openType;  
this.description = description;  
}

private final String openType;  
private final String description;

}

最后也必须要说明的是,这段代码是我随手从项目中摘出来的,并不完全,也会存在很多问题,但思路是没错的,代码也存在很大的优化空间.