开始之前先说一点题外话,几年前曾经看过一个视频,其中一个观点大概就是程序员是一个反传统的群体,其他群体掌握了某个技术,一般都是当做内部商业机密,而程序员则不一样,喜欢开源,尤其 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) {  
      log.error("IOException", e);  
    } 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 - 32, result, c1Len, 32);  
    System.arraycopy(c1c2c3, c1Len, result, c1Len + 32, c1c2c3.length - c1Len - 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 - c1Len - 32);  
    System.arraycopy(c1c3c2, c1Len, result, c1c3c2.length - 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 - 1; i >= 0; -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 - 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 class Holder {  
    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<String> queryDatas(String enterpriseName, 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<String> 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 'datas' 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;

}

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