全國人口 信息(NCIIC)接口開發紀要

階段一:根據wsdl2java命令解析https://ws.nciic.org.cn/nciic_ws/services/NciicServices?wsdl以生成接口調用的對象類:html

wsdl2java用法:
wsdl2java -p com -d src -all wsdljava

實例1.:wsdl2java -p com -client -d d:/src https://ws.nciic.org.cn/nciic_ws/services/NciicServices?wsdl
實例2.:wsdl2java -p com -all -d E:\jiekou E:\jiekou\NciicServices.wsdl
生成java代碼後能夠直接複製到客戶端中再客戶端中使用(具體cxf及wsdl2java使用方式,可參看我另一篇博客:http://www.cnblogs.com/hedongfei/p/7487149.html)web

階段二:將生成的代碼複製到項目中後,在客戶端調用測試的過程當中,因公安部拒絕提供證書,僅提供了inlisene文件,沒法進行正常調試。故開始尋找cxf客戶端調用規避證書或者無條件信任證書的方式(下附的信任證書方式是我尋找到的其中一種方式,經我屢次驗證,我所尋找的方法均無用,下附的也無用。彷佛CXF在生成的客戶端中不存在規避的途徑):apache

1、當不須要使用任何證書訪問https網頁時,只需配置信任任何證書
HttpClient http = new HttpClient();  
String url = "https://payment.cib.com.cn/payment/api/rest";  
Protocol myhttps = new Protocol("https", new MySSLProtocolSocketFactory(), 443);   
Protocol.registerProtocol("https", myhttps);   
PostMethod post = new PostMethod(url);  
 
 
2、其中信任任何證書的類
import java.io.IOException;  
import java.net.InetAddress;  
import java.net.InetSocketAddress;  
import java.net.Socket;  
import java.net.SocketAddress;  
import java.net.UnknownHostException;  
import java.security.KeyManagementException;  
import java.security.NoSuchAlgorithmException;  
import java.security.cert.CertificateException;  
import java.security.cert.X509Certificate;  
  
import javax.net.SocketFactory;  
import javax.net.ssl.SSLContext;  
import javax.net.ssl.TrustManager;  
import javax.net.ssl.X509TrustManager;  
  
import org.apache.commons.httpclient.ConnectTimeoutException;  
import org.apache.commons.httpclient.params.HttpConnectionParams;  
import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;  
  
  
  
/** 
 * author by lpp 
 * 
 * created at 2010-7-26 上午09:29:33 
 */  
public class MySSLProtocolSocketFactory implements ProtocolSocketFactory {  
  
  private SSLContext sslcontext = null;   
   
  private SSLContext createSSLContext() {   
      SSLContext sslcontext=null;   
      try {   
          sslcontext = SSLContext.getInstance("SSL");   
          sslcontext.init(null, new TrustManager[]{new TrustAnyTrustManager()}, new java.security.SecureRandom());   
      } catch (NoSuchAlgorithmException e) {   
          e.printStackTrace();   
      } catch (KeyManagementException e) {   
          e.printStackTrace();   
      }   
      return sslcontext;   
  }   
   
  private SSLContext getSSLContext() {   
      if (this.sslcontext == null) {   
          this.sslcontext = createSSLContext();   
      }   
      return this.sslcontext;   
  }   
   
  public Socket createSocket(Socket socket, String host, int port, boolean autoClose)   
          throws IOException, UnknownHostException {   
      return getSSLContext().getSocketFactory().createSocket(   
              socket,   
              host,   
              port,   
              autoClose   
          );   
  }   
  
  public Socket createSocket(String host, int port) throws IOException,   
          UnknownHostException {   
      return getSSLContext().getSocketFactory().createSocket(   
              host,   
              port   
          );   
  }   
   
   
  public Socket createSocket(String host, int port, InetAddress clientHost, int clientPort)   
          throws IOException, UnknownHostException {   
      return getSSLContext().getSocketFactory().createSocket(host, port, clientHost, clientPort);   
  }   
  
  public Socket createSocket(String host, int port, InetAddress localAddress,   
          int localPort, HttpConnectionParams params) throws IOException,   
          UnknownHostException, ConnectTimeoutException {   
      if (params == null) {   
          throw new IllegalArgumentException("Parameters may not be null");   
      }   
      int timeout = params.getConnectionTimeout();   
      SocketFactory socketfactory = getSSLContext().getSocketFactory();   
      if (timeout == 0) {   
          return socketfactory.createSocket(host, port, localAddress, localPort);   
      } else {   
          Socket socket = socketfactory.createSocket();   
          SocketAddress localaddr = new InetSocketAddress(localAddress, localPort);   
          SocketAddress remoteaddr = new InetSocketAddress(host, port);   
          socket.bind(localaddr);   
          socket.connect(remoteaddr, timeout);   
          return socket;   
      }   
  }   
   
  //自定義私有類   
  private static class TrustAnyTrustManager implements X509TrustManager {   
      
      public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {   
      }   
  
      public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {   
      }   
  
      public X509Certificate[] getAcceptedIssuers() {   
          return new X509Certificate[]{};   
      }   
  }     
  
  
}  

階段三:在尋求cxf規避證書無果後,我決定換成Xfire的方式實現接口。我在網上搜索是否存在xfire相關解析wsdl命令,以生成對象類,在尋找的過程當中確有發現,但在eclipse安裝xfire插件時,發現根本下載不了,方案亦pass··尋找到的xfire解析方案以下:api

這篇筆記,只是針對於基於wsdl文件生成客戶端代碼後,來編寫訪問webservice的客戶端。做爲開發過程當中遇到的問題的記錄。
開發環境:jdk1.五、xfire-1.2.6
因爲服務端並不是本公司開發,因此只能拿到服務端的wsdl文件,首先想到的是,基於wsdl文件生成客戶端代碼,而後在此基礎上按照業務來編寫請求類。
XFire 官方網站 http://xfire.codehaus.org
能夠從該網站下載xfire-distribution-1.2.6.zip文件,裏面包含了xFire的文檔、類包及相關文檔說明。
jar包都在lib下,另外不要忘記了xfire-all-1.2.6.jar包。
1、new一個project:MyWebservice
而後右鍵再new一下,選擇other: 

 

 


 next,出現下圖:
 

 



上圖中的WSDL URL or path欄能夠選擇url地址:http://<ip>:<port>/path/XXX?wsdl,也能夠將wsdl文件下載到本地,選擇本地wsdl文件。因爲本項目用到的url本地沒法訪問,所以必須在服務器上下載服務端的wsdl文件到本地,利用本地的wsdl文件生成客戶端。
Output directory一欄,是選擇你生成的代碼放入的目錄。因爲以前新建了一個MyWebservice的工程,因此這裏選擇該工程的src目錄,固然也能夠新建你指定的目錄列表(下面的package一欄能夠選擇指定的目錄,這裏暫時選擇default)。以下圖:

 


 
點擊finish完成,生成以下圖目錄結構的客戶端代碼(MyClient類是後面本身編寫的訪問請求類)。

 



 
2、編寫客戶端調用類:MyClient
Java代碼  收藏代碼
package com.telement.intf.huaxia.service.impl;  
  
import java.net.MalformedURLException;  
import java.net.URL;  
  
import javax.xml.bind.JAXBElement;  
import javax.xml.namespace.QName;  
  
import org.codehaus.xfire.XFireFactory;  
import org.codehaus.xfire.client.Client;  
import org.codehaus.xfire.client.XFireProxyFactory;  
import org.codehaus.xfire.service.Service;  
import org.codehaus.xfire.service.binding.ObjectServiceFactory;  
  
public class MyClient {  
  
    public static void main(String[] args) {  
        MyClient mc = new MyClient();  
        mc.queryWebSiteByXfireThree();  
    }  
  
    public void queryWebSiteByXfireOne() {  
        Service srvcModel = new ObjectServiceFactory()  
                .create(HuaXiaServiceImplPortType.class);  
        XFireProxyFactory factory = new XFireProxyFactory(XFireFactory  
                .newInstance().getXFire());  
  
        String helloWorldURL = "http://<ip>:<port>/path/serviceName";  
        String requestXmlStr = "yours_request_str";  
        try {  
            HuaXiaServiceImplPortType srvc = (HuaXiaServiceImplPortType) factory  
                    .create(srvcModel, helloWorldURL);  
            JAXBElement<String> requestParm = new JAXBElement<String>(  
                    new QName("http://impl.service.huaxia.intf.telement.com/",  
                            "HuaXiaServiceImplPort"), String.class,  
                    requestXmlStr);  
            JAXBElement<String> resultStr = srvc.queryWebSite(requestParm);  
            System.out.println(resultStr.getValue());  
        } catch (MalformedURLException e) {  
            e.printStackTrace();  
        }  
  
    }  
  
    public void queryWebSiteByXfireTwo() {  
        String requestXmlStr = "yours_request_str";  
        URL url = null;  
        Client client = null;  
        try {  
            url = new URL("http://<ip>:<port>/path/serviceName?wsdl");  
            client = new Client(url);  
            System.out.println(requestXmlStr);  
            String result = (String) client.invoke("queryWebSite",  
                    new Object[] { requestXmlStr })[0];  
            System.out.println(result);  
        } catch (MalformedURLException e) {  
            e.printStackTrace();  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
    }  
  
    public void queryWebSiteByXfireThree() {  
        String requestXmlStr = "yours_request_str";  
        HuaXiaServiceImplClient hxc = new HuaXiaServiceImplClient();  
        HuaXiaServiceImplPortType service = hxc.getHuaXiaServiceImplPort();  
        JAXBElement<String> requestParm = new JAXBElement<String>(new QName(  
                "http://impl.service.huaxia.intf.telement.com/",  
                "HuaXiaServiceImplPort"), String.class, requestXmlStr);  
        JAXBElement<String> resultStr = service.queryWebSite(requestParm);  
        System.out.println(resultStr.getValue());  
    }  
}  
 
 調用上述類中方法一和方法三測試後發現以下異常:(方法二正常,由於方法二與生成的客戶端存根代碼無關)
Java代碼  收藏代碼
Exception in thread "main" org.codehaus.xfire.XFireRuntimeException: Could not invoke service.. Nested exception is org.codehaus.xfire.fault.XFireFault: Couldn't get property {http://lang.java}classes from bean class java.lang.String. Nested exception is java.lang.reflect.InvocationTargetException: null  
org.codehaus.xfire.fault.XFireFault: Couldn't get property {http://lang.java}classes from bean class java.lang.String. Nested exception is java.lang.reflect.InvocationTargetException: null  
    at org.codehaus.xfire.fault.XFireFault.createFault(XFireFault.java:89)  
    at org.codehaus.xfire.client.Invocation.invoke(Invocation.java:83)  
    at org.codehaus.xfire.client.Invocation.invoke(Invocation.java:114)  
    at org.codehaus.xfire.client.Client.invoke(Client.java:336)  
    at org.codehaus.xfire.client.XFireProxy.handleRequest(XFireProxy.java:77)  
    at org.codehaus.xfire.client.XFireProxy.invoke(XFireProxy.java:57)  
    at $Proxy6.queryWebSite(Unknown Source)  
    at com.telement.intf.huaxia.service.impl.MyClient.queryWebSiteByXfireOne(MyClient.java:77)  
    at com.telement.intf.huaxia.service.impl.MyClient.main(MyClient.java:27)  
Caused by: org.codehaus.xfire.XFireRuntimeException: Couldn't get property {http://lang.java}classes from bean class java.lang.String. Nested exception is java.lang.reflect.InvocationTargetException: null  
    at org.codehaus.xfire.aegis.type.basic.BeanType.readProperty(BeanType.java:446)  
    at org.codehaus.xfire.aegis.type.basic.BeanType.writeObject(BeanType.java:377)  
    at org.codehaus.xfire.aegis.type.basic.BeanType.writeObject(BeanType.java:392)  
    at org.codehaus.xfire.aegis.type.basic.ArrayType.writeValue(ArrayType.java:298)  
    at org.codehaus.xfire.aegis.type.basic.ArrayType.writeObject(ArrayType.java:210)  
    at org.codehaus.xfire.aegis.type.basic.BeanType.writeObject(BeanType.java:392)  
    at org.codehaus.xfire.aegis.type.basic.BeanType.writeObject(BeanType.java:392)  
    at org.codehaus.xfire.aegis.type.basic.ArrayType.writeValue(ArrayType.java:298)  
    at org.codehaus.xfire.aegis.type.basic.ArrayType.writeObject(ArrayType.java:210)  
    at org.codehaus.xfire.aegis.type.basic.BeanType.writeObject(BeanType.java:392)  
    at org.codehaus.xfire.aegis.type.basic.BeanType.writeObject(BeanType.java:392)  
    at org.codehaus.xfire.aegis.type.basic.ArrayType.writeValue(ArrayType.java:298)  
    at org.codehaus.xfire.aegis.type.basic.ArrayType.writeObject(ArrayType.java:210)  
    at org.codehaus.xfire.aegis.type.basic.BeanType.writeObject(BeanType.java:392)  
    at org.codehaus.xfire.aegis.type.basic.BeanType.writeObject(BeanType.java:392)  
    at org.codehaus.xfire.aegis.type.basic.ArrayType.writeValue(ArrayType.java:298)  
 
思路一直停留在org.codehaus.xfire.XFireRuntimeException: Could not invoke service上面,但後面發現修改
wsdl文件中的一個屬性nillable,能夠解決下面的這個Exception:
Java代碼  收藏代碼
Couldn't get property {http://lang.java}classes from bean class java.lang.String. Nested exception is java.lang.reflect.InvocationTargetException: null  
  
從服務端下載到的wsdl文件中,<xsd:element>元素存在這麼幾個元素:
<xsd:element minOccurs="0" name="requestXml" nillable="true" type="xsd:string"></xsd:element>
形成在生成客戶端代碼的時候將原本是String類型的請求參數和返回值都封裝成了JAXBElement<String>這個類,所以在編寫MyClient這個類的時候,天然就想到要將請求參數封裝爲JAXBElement<String>。
 
通過調試發現改成:
<xsd:element minOccurs="0" name="requestXml" nillable="false" type="xsd:string"></xsd:element>
或者
<xsd:element maxOccurs="1" minOccurs="1" name="requestXml" nillable="true" type="xsd:string"></xsd:element>
 
再根據wsdl文件來生成客戶端,即是本真的String類型做爲參數和返回的數據類型。  
 
3、修改編寫好的客戶端調用類:MyClient
Java代碼  收藏代碼
package com.telement.intf.huaxia.service.impl;  
  
import java.net.MalformedURLException;  
import java.net.URL;  
  
import org.codehaus.xfire.XFireFactory;  
import org.codehaus.xfire.client.Client;  
import org.codehaus.xfire.client.XFireProxyFactory;  
import org.codehaus.xfire.service.Service;  
import org.codehaus.xfire.service.binding.ObjectServiceFactory;  
  
public class MyClient {  
  
    public static void main(String[] args) {  
        MyClient mc = new MyClient();  
        mc.queryWebSiteByXfireOne();  
    }  
  
    public void queryWebSiteByXfireOne() {  
        Service srvcModel = new ObjectServiceFactory()  
                .create(HuaXiaServiceImplPortType.class);  
        XFireProxyFactory factory = new XFireProxyFactory(XFireFactory  
                .newInstance().getXFire());  
  
        String helloWorldURL = "http://<ip>:<port>/path/serviceName";  
        String requestXmlStr = "yours_request_str";  
        try {  
            HuaXiaServiceImplPortType srvc = (HuaXiaServiceImplPortType) factory  
                    .create(srvcModel, helloWorldURL);  
            String resultStr = srvc.queryWebSite(requestXmlStr);  
            System.out.println(resultStr);  
        } catch (MalformedURLException e) {  
            e.printStackTrace();  
        }  
  
    }  
  
    public void queryWebSiteByXfireTwo() {  
        String requestXmlStr = "yours_request_str";  
        URL url = null;  
        Client client = null;  
        try {  
            url = new URL("http://<ip>:<port>/path/serviceName?wsdl");  
            client = new Client(url);  
            System.out.println(requestXmlStr);  
            String result = (String) client.invoke("queryWebSite",  
                    new Object[] { requestXmlStr })[0];  
            System.out.println(result);  
        } catch (MalformedURLException e) {  
            e.printStackTrace();  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
    }  
  
    public void queryWebSiteByXfireThree() {  
        String requestXmlStr = "yours_request_str";  
        HuaXiaServiceImplClient hxc = new HuaXiaServiceImplClient();  
        HuaXiaServiceImplPortType service = hxc.getHuaXiaServiceImplPort();  
        String resultStr = service.queryWebSite(requestXmlStr);  
        System.out.println(resultStr);  
    }  
}  
 
方法一的方式調用依然存在Exception:
Java代碼  收藏代碼
Exception in thread "main" org.codehaus.xfire.XFireRuntimeException: Could not invoke service.. Nested exception is org.codehaus.xfire.fault.XFireFault: Fault occurred while proce  
ssing.  
org.codehaus.xfire.fault.XFireFault: Fault occurred while processing.  
        at org.codehaus.xfire.fault.Soap11FaultSerializer.readMessage(Soap11FaultSerializer.java:31)  
        at org.codehaus.xfire.fault.SoapFaultSerializer.readMessage(SoapFaultSerializer.java:28)  
        at org.codehaus.xfire.soap.handler.ReadHeadersHandler.checkForFault(ReadHeadersHandler.java:111)  
        at org.codehaus.xfire.soap.handler.ReadHeadersHandler.invoke(ReadHeadersHandler.java:67)  
        at org.codehaus.xfire.handler.HandlerPipeline.invoke(HandlerPipeline.java:131)  
        at org.codehaus.xfire.client.Client.onReceive(Client.java:406)  
        at org.codehaus.xfire.transport.http.HttpChannel.sendViaClient(HttpChannel.java:139)  
        at org.codehaus.xfire.transport.http.HttpChannel.send(HttpChannel.java:48)  
        at org.codehaus.xfire.handler.OutMessageSender.invoke(OutMessageSender.java:26)  
        at org.codehaus.xfire.handler.HandlerPipeline.invoke(HandlerPipeline.java:131)  
        at org.codehaus.xfire.client.Invocation.invoke(Invocation.java:79)  
        at org.codehaus.xfire.client.Invocation.invoke(Invocation.java:114)  
        at org.codehaus.xfire.client.Client.invoke(Client.java:336)  
        at org.codehaus.xfire.client.XFireProxy.handleRequest(XFireProxy.java:77)  
        at org.codehaus.xfire.client.XFireProxy.invoke(XFireProxy.java:57)  
        at $Proxy6.queryWebSite(Unknown Source)  
        at com.telement.intf.huaxia.service.impl.MyClient.queryWebSiteByXfireOne(MyClient.java:31)  
        at com.telement.intf.huaxia.service.impl.MyClient.main(MyClient.java:16)  
 
但方法二和方法三調用可正常返回結果。
 
 因爲在網上搜索到不少文章都是提到xfire客戶端調用方式是上述MyClient類中的方法一,因此筆者寫了一個簡單的服務端,而後另外起一個project來生成客戶端並測試,發現確實是行得通的。因此暫時將該問題記錄下來,若是有知道緣由的童鞋,也請指點下。

來自博客:http://xm-koma.iteye.com/blog/1585413

階段四:在沒法用xfire解析對象類後,我決定直接用XFIRE的實現方式調用以cxf的wsdl2java命令生成的對象類,最後木想到,成功了,哈哈哈:附個人client以下(不須要動cxf以生成的各對象類,在個人client裏須要從新設置SERVICE_URL,這個是重點!):服務器

package com.aebiz.app.web.modules.controllers.open.api.nciccClient.client;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Proxy;
import java.net.MalformedURLException;

import com.aebiz.app.web.modules.controllers.open.api.nciic.NciicService;
import com.aebiz.app.web.modules.controllers.open.api.nciic.impl.NciicServiceImpl;
import com.aebiz.app.web.modules.controllers.open.api.nciic.ncicc_client_server.NciicServicesPortType;
import org.apache.commons.httpclient.protocol.Protocol;
import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;
import org.codehaus.xfire.client.Client;
import org.codehaus.xfire.client.XFireProxy;
import org.codehaus.xfire.client.XFireProxyFactory;
import org.codehaus.xfire.service.Service;
import org.codehaus.xfire.service.binding.ObjectServiceFactory;
import org.codehaus.xfire.transport.http.CommonsHttpMessageSender;
import org.codehaus.xfire.transport.http.EasySSLProtocolSocketFactory;
import org.codehaus.xfire.util.dom.DOMOutHandler;

import javax.servlet.http.HttpServletRequest;

/**
 * Created by aebiz on 2017/9/24.
 */
public class NciicClient {
    public static final String SERVICE_URL = "https://ws.nciic.org.cn/nciic_ws/services/";
    public static final String NCIICCHENK_INLICENSE = "***";//此處填密文

    public NciicClient() {
    }

    /**
     * XFire調用方法
     * @param serviceName
     * @param condition
     * @return
     * @throws MalformedURLException
     */
    public String executeClient(String serviceName,
                                String condition)
            throws MalformedURLException {
        ProtocolSocketFactory easy = new
                EasySSLProtocolSocketFactory();
        Protocol protocol = new Protocol("https", easy, 443);
        Protocol.registerProtocol("https", protocol);
        Service serviceModel = new ObjectServiceFactory().create(
                NciicServicesPortType.class, "NciicServices", null, null);
        NciicServicesPortType service = (NciicServicesPortType) new
                XFireProxyFactory().create(
                serviceModel, SERVICE_URL + serviceName);
        Client client = ((XFireProxy)
                Proxy.getInvocationHandler(service))
                .getClient();
        client.addOutHandler(new DOMOutHandler());

        //壓縮傳輸
        client.setProperty(CommonsHttpMessageSender.GZIP_ENABLED,
                Boolean.TRUE);
        //忽略超時
        client.setProperty(CommonsHttpMessageSender.DISABLE_EXPECT_CONTINUE, "1");
        client.setProperty(CommonsHttpMessageSender.HTTP_TIMEOUT,
                "0");


        BufferedReader in;

        //調用覈查方法
        String result = service.nciicCheck(NCIICCHENK_INLICENSE, condition);

        System.out.println("結果:" + result);
        return result;
    }


    /**
     * @param args
     * @throws MalformedURLException
     */
    public static void main(String[] args) throws
            MalformedURLException {
        /**
         *  受權文件名稱,若是該文件不在客戶端工程根目錄下,請
         *  將文件路徑添加。如 C:\\1.txt
         */
        String license = "受權文件";
        String con = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n" +
                "<ROWS><INFO><SBM>$$SBM$$</SBM></INFO><ROW><GMSFHM> 公民身份號碼\n" +
                "</GMSFHM><XM> 姓名</XM></ROW><ROW FSD=\" $$FSD$$ \" YWLX=\" $$YWLX$$  \" >\n" +
                "<GMSFHM>$$idno$$</GMSFHM><XM>$$username$$</XM></ROW></ROWS>";

        con= con.replace("$$idno$$","***").replace("$$username$$","**");
new NciicClient().executeClient("NciicServices",
                con);
    }
}

總結:此接口的調節過程可謂蛋疼至極,因接口文檔他大爺的簡陋到爆,實現方式能夠說所有靠我幾年來的經驗和技術推敲,沒有任何demo,只有簡單的接口介紹,中間走了不少彎路,請教了很多次公司的架構師及分公司cto等前輩,他們亦對cxf進行https訪問時如何規避證書或者信任證書的辦法不甚瞭解,在屢次尋找其餘解決方案的時候,最終成功用wsdl2java解析對象類,而後用XFire的方式調用的方案解決,故分享之。架構

p.s.此前未曾想起試用的最終解決方案,實是由於我一直認爲wsdl2java是cxf帶的解析命令,我覺得生成的對象類不會和axis或者XFire兼容,故沒想過嘗試,在此尤其感謝公司架構師的提醒,也提醒了我做爲開發工程師要多學多想!另外,提醒一點!若是cxf的webservicer接口調用https的連接,其實只須要將證書放入jre,而cxf的調用方式會徹底可行的!此條博客的方案,僅僅是由於公安部不提供證書,而我又須要連接·····哇哇哇········app

相關文章
相關標籤/搜索