階段一:根據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