关于java调用.Net WCF服务的讲解.

 

最近做毕业设计,一个团体项目。

我做的是关于系统构架方面的。简单点说就是.Net写的一个WCF服务,而访问服务的是java客户端或者说是android客户端。

而我的WCF服务配置成wsHttpBinding+certificate证书加密+自定义的用户名密码检验功能。在服务的访问上用了https协议,个人也没做过java平台和.Net Web Service的整合。而且https无疑增加了这个难度。

所以刚开始的想法是通过asp.net网页来访问服务,再利用网页来给java,android返回xml。这样利用http避免了平台的不兼容性。

缺点是各种业务的增加删除不紧要在Web Service中增加,还要在网页中增加。

而后来由于另外几个同学迟迟没有开始做应用端,导致业务无法预料,时间越脱越长。

个人觉得时间可能有点紧,就想把框架简单化,直接让java端和andriod客户端调用WCF服务。

于是清明放假这几天改造了一下程序框架,网络上参考了各种文章,终于调试成功了。

废话不说了,看代码。

首先,最简单的服务,没有certificate,没有自定义的用户名密码检验,只有wsHttpBinding。

WCF服务配置程序如下:

 

 

  1. <?xml version="1.0" encoding="utf-8" ?>  
  2. <configuration>  
  3.   <system.serviceModel>  
  4.     <behaviors>  
  5.         
  6.       <serviceBehaviors>  
  7.         <behavior name="httpsBasicBindings">  
  8.           <serviceMetadata httpGetEnabled="false" httpGetUrl="http://127.0.0.1:8889/test" />  
  9.           <serviceDebug includeExceptionDetailInFaults="true" />  
  10.         </behavior>  
  11.       </serviceBehaviors>  
  12.     </behaviors>  
  13.     <services>  
  14.       <service behaviorConfiguration="httpsBasicBindings" name="TestService.TestService">  
  15.         <endpoint address="" binding="basicHttpBinding" bindingConfiguration="test1" contract="TestService.ITestService">  
  16.         </endpoint>  
  17.         <endpoint address="mex" binding="mexHttpsBinding" contract="IMetadataExchange" />  
  18.       </service>  
  19.     </services>  
  20.     <bindings>  
  21.       <basicHttpBinding>  
  22.         <binding name="test1">  
  23.           <security mode="Transport">  
  24.             <transport clientCredentialType="None"/>  
  25.           </security>  
  26.         </binding>  
  27.       </basicHttpBinding>  
  28.     </bindings>  
  29.   </system.serviceModel>  

 

 

java客户端调用程序:(用axis2-1.4.1生成,生成方法cmd下 axis2的bin目录下 wsdl2java -uri http://localhost:8889/test?wsdl)

 

 

  1.       
  2.       
  3.     TestServiceStub.GetSysTime paraGetSysTime=new TestServiceStub.GetSysTime();       
  4.     try {  
  5.           
  6.         TestServiceStub.GetSysTimeResponse response = client.GetSysTime(paraGetSysTime);  
  7.         resultString=response.getGetSysTimeResult();  
  8.     } catch (RemoteException e) {  
  9.         // TODO Auto-generated catch block   
  10.         e.printStackTrace();  
  11.     }  
  12. catch (AxisFault e) {  
  13.     // TODO Auto-generated catch block   
  14.     e.printStackTrace();  
  15. }  
  16. System.out.print(resultString);  

 

 

结果调用失败,一直是什么read time out,超时。网查查阅资料,说这么设置就可以了。

 

 

  1. client._getServiceClient().getOptions().setTimeOutInMilliSeconds(5*60*1000);  

 

 

于是设置,结果还是超时。继续找资料。发现都是说超时。当时那个崩溃啊~~好在翻了10页google,终于找到一篇说是java不支持wshttpbinding协议。因为wshttpbinding是微软在SOAP1.0基础上改进过的,不是通用的绑定协议。这里又补充了下理论知识,呵呵~~于是改为basicHttpBinding。

WCF服务配置(basicHttpBinding)

 

 

  1. <?xml version="1.0" encoding="utf-8" ?>  
  2. <configuration>  
  3.   <system.serviceModel>  
  4.     <behaviors>  
  5.         
  6.       <serviceBehaviors>  
  7.         <behavior name="httpsBasicBindings">  
  8.           <serviceMetadata httpGetEnabled="true" httpGetUrl="http://127.0.0.1:8889/test" />  
  9.           <serviceDebug includeExceptionDetailInFaults="true" />  
  10.             
  11.         </behavior>  
  12.       </serviceBehaviors>  
  13.     </behaviors>  
  14.     <services>  
  15.       <service behaviorConfiguration="httpsBasicBindings" name="TestService.TestService">  
  16.         <endpoint address="" binding="basicHttpBinding" bindingConfiguration="test1" contract="TestService.ITestService">  
  17.         </endpoint>  
  18.         <endpoint address="mex" binding="mexHttpsBinding" contract="IMetadataExchange" />  
  19.       </service>  
  20.     </services>  
  21.     <bindings>  
  22.       <basicHttpBinding>  
  23.         <binding name="test1">  
  24.           <security mode="Transport">  
  25.             <transport clientCredentialType="None"/>  
  26.           </security>  
  27.         </binding>  
  28.       </basicHttpBinding>  
  29.     </bindings>  
  30.   </system.serviceModel>  
  31. </configuration>  

 

 

然后重新用axis2 生成客户端代理。调用代码,果然成功。调用代码不变。

调用结果图:

调用结果

 

下面是axis2生成的客户端代理结构图。

 

 

证书制作,绑定端口什么的这边就不讲了,网上有关于这方面的讲解。用到了XP系统的话是httpcfg.exe,Win7目前我还没用过,反正是有一个差不多的工具。之前我也转载过一个制作证书的文章。

这边的话主要讲配置文件,以及调用中我遇到的一些问题。

WCF服务配置文件:

 

 

  1. <?xml version="1.0" encoding="utf-8" ?>  
  2. <configuration>  
  3.   <system.serviceModel>  
  4.     <behaviors>  
  5.         
  6.       <serviceBehaviors>  
  7.         <behavior name="httpsBasicBindings">  
  8.           <serviceMetadata httpsGetEnabled="true" httpGetEnabled="true" httpGetUrl="http://127.0.0.1:8889/test" />  
  9.           <serviceDebug includeExceptionDetailInFaults="true" />  
  10.           <serviceCredentials>  
  11.             <serviceCertificate storeName="My" x509FindType="FindBySubjectName" findValue="HL7Server" storeLocation="LocalMachine"/>  
  12.           </serviceCredentials>  
  13.         </behavior>  
  14.       </serviceBehaviors>  
  15.     </behaviors>  
  16.     <services>  
  17.       <service behaviorConfiguration="httpsBasicBindings" name="TestService.TestService">  
  18.         <endpoint address="" binding="basicHttpBinding" bindingConfiguration="test1" contract="TestService.ITestService">  
  19.         </endpoint>  
  20.         <endpoint address="mex" binding="mexHttpsBinding" contract="IMetadataExchange" />  
  21.       </service>  
  22.     </services>  
  23.     <bindings>  
  24.       <basicHttpBinding>  
  25.         <binding name="test1">  
  26.           <security mode="Transport">  
  27.             <transport clientCredentialType="None"/>  
  28.           </security>  
  29.         </binding>  
  30.       </basicHttpBinding>  
  31.     </bindings>  
  32.   </system.serviceModel>  
  33. </configuration>  

 

 

在serviceBehaviors里面的serviceMetadata配置中,一定要设置httpGetEnabled=“true”,之前我没有设置,想通过wsdl2java -uri https://127.0.0.1:9001/test?wsdl 来生成axis2的服务代理,结果一直生成不了。错误代码:

网上查了很多资料,大部分都是说Java调用WCF服务的过程中出现的这个错误,是证书没有导入到java运行环境的security容器中。结果用网上提供的方法试了N次,还是失败。

后来我反思,为什么要用https://127.0.0.1:9001/test?wsdl这个地址来生成代理呢?这个请求会被浏览器阻截,所以我没有获取wsdl文件,当然会失败。然后尝试用http://127.0.0.1:8889/test?wsdl这个地址来生成代理。就是上面设置的

 

  1. httpGetEnabled="true" httpGetUrl="http://127.0.0.1:8889/test"  

 

 

这一段。然后生成成功。很高兴,以为就要成功了。然后进行测试。结果失败-_-!

测试代码如下:

 

 

  1. String resultString="";  
  2.         try {  
  3.             TestServiceStub client=new TestServiceStub();           client._getServiceClient().getOptions().setProperty(org.apache.axis2.transport.http.HTTPConstants.CHUNKED, Boolean.FALSE);  
  4.               
  5.             //client._getServiceClient().getOptions().setTimeOutInMilliSeconds(5*60*1000);   
  6.             TestServiceStub.GetSysTime paraGetSysTime=new TestServiceStub.GetSysTime();       
  7.             try {  
  8.                   
  9.                 TestServiceStub.GetSysTimeResponse response = client.GetSysTime(paraGetSysTime);  
  10.                 resultString=response.getGetSysTimeResult();  
  11.             } catch (RemoteException e) {  
  12.                 // TODO Auto-generated catch block   
  13.                 e.printStackTrace();  
  14.             }  
  15.         } catch (AxisFault e) {  
  16.             // TODO Auto-generated catch block   
  17.             e.printStackTrace();  
  18.         }  
  19.         System.out.print(resultString);  

 

 

java的错误信息:

 

 

  1. org.apache.axis2.AxisFault: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target  
  2.     at org.apache.axis2.AxisFault.makeFault(AxisFault.java:430)  
  3.     at org.apache.axis2.transport.http.AxisRequestEntity.writeRequest(AxisRequestEntity.java:98)  
  4.     at org.apache.commons.httpclient.methods.EntityEnclosingMethod.writeRequestBody(EntityEnclosingMethod.java:499)  
  5.     at org.apache.commons.httpclient.HttpMethodBase.writeRequest(HttpMethodBase.java:2114)  
  6.     at org.apache.commons.httpclient.HttpMethodBase.execute(HttpMethodBase.java:1096)  
  7.     at org.apache.commons.httpclient.HttpMethodDirector.executeWithRetry(HttpMethodDirector.java:398)  
  8.     at org.apache.commons.httpclient.HttpMethodDirector.executeMethod(HttpMethodDirector.java:171)  
  9.     at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:397)  
  10.     at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:346)  
  11.     at org.apache.axis2.transport.http.AbstractHTTPSender.executeMethod(AbstractHTTPSender.java:542)  
  12.     at org.apache.axis2.transport.http.HTTPSender.sendViaPost(HTTPSender.java:189)  
  13.     at org.apache.axis2.transport.http.HTTPSender.send(HTTPSender.java:75)  
  14.     at org.apache.axis2.transport.http.CommonsHTTPTransportSender.writeMessageWithCommons(CommonsHTTPTransportSender.java:371)  
  15.     at org.apache.axis2.transport.http.CommonsHTTPTransportSender.invoke(CommonsHTTPTransportSender.java:209)  
  16.     at org.apache.axis2.engine.AxisEngine.send(AxisEngine.java:448)  
  17.     at org.apache.axis2.description.OutInAxisOperationClient.send(OutInAxisOperation.java:401)  
  18.     at org.apache.axis2.description.OutInAxisOperationClient.executeImpl(OutInAxisOperation.java:228)  
  19.     at org.apache.axis2.client.OperationClient.execute(OperationClient.java:163)  
  20.     at org.tempuri.TestServiceStub.GetSysTime(TestServiceStub.java:205)  
  21.     at test.main(test.java:42)  
  22. Caused by: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target  
  23.     at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:174)  
  24.     at com.sun.net.ssl.internal.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1611)  
  25.     at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:187)  
  26.     at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:181)  
  27.     at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1035)  
  28.     at com.sun.net.ssl.internal.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:124)  
  29.     at com.sun.net.ssl.internal.ssl.Handshaker.processLoop(Handshaker.java:516)  
  30.     at com.sun.net.ssl.internal.ssl.Handshaker.process_record(Handshaker.java:454)  
  31.     at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:884)  
  32.     at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1112)  
  33.     at com.sun.net.ssl.internal.ssl.SSLSocketImpl.writeRecord(SSLSocketImpl.java:623)  
  34.     at com.sun.net.ssl.internal.ssl.AppOutputStream.write(AppOutputStream.java:59)  
  35.     at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:65)  
  36.     at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:123)  
  37.     at java.io.FilterOutputStream.flush(FilterOutputStream.java:123)  
  38.     at org.apache.axis2.transport.http.AxisRequestEntity.writeRequest(AxisRequestEntity.java:94)  
  39.     ... 18 more  
  40. Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target  
  41.     at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:285)  
  42.     at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:191)  
  43.     at sun.security.validator.Validator.validate(Validator.java:218)  
  44.     at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:126)  
  45.     at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:209)  
  46.     at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:249)  
  47.     at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1014)  
  48.     ... 29 more  
  49. Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target  
  50.     at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:174)  
  51.     at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:238)  
  52.     at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:280)  
  53.     ... 35 more  

 

 

然后再到网上查找,结果发现就是之前找到的说是证书没有导入到java运行环境的security容器中的错误,于是再导入证书,

导入证书的步骤:

 

1,在IE中访问WSDL的URL,弹出“安全警报”窗口,查看证书->详细信息标签页->复制到文件->下一步->下一步->指定文件名,将证书下载保存为.cer文件,例如:test_axis.cer

2,用下载到的证书文件生成信任库文件:

$ keytool -import -file test_axis.cer -storepass changeit -keystore client.truststore -alias serverkey -noprompt

3,在调用WebService代码前指定信任库文件的路径:

System.setProperty("javax.net.ssl.trustStore", "/tmp/client.truststore");

System.setProperty("javax.net.ssl.trustStorePassword", "changeit");

然后在java调用代码中加入

 

System.setProperty("javax.net.ssl.trustStore", "/tmp/client.truststore");

System.setProperty("javax.net.ssl.trustStorePassword", "changeit");

修改之后的java代码:

 

 

  1. public static void main(String[] args) {  
  2.         String resultString="";  
  3.         try {  
  4.             TestServiceStub client=new TestServiceStub();  
  5.             //-----------------------------------------------------------------------   
  6.             System.setProperty("javax.net.ssl.trustStore""D://java//jre//lib//security//hl7");  
  7.             System.setProperty("javax.net.ssl.trustStorePassword""*******");  
  8.             //设置信任证书   
  9.             //-----------------------------------------------------------------------   
  10.               
  11.               
  12.             client._getServiceClient().getOptions().setProperty(org.apache.axis2.transport.http.HTTPConstants.CHUNKED, Boolean.FALSE);  
  13.             //client._getServiceClient().getOptions().setProperty(org.   
  14.               
  15.             //client._getServiceClient().getOptions().setTimeOutInMilliSeconds(5*60*1000);   
  16.             TestServiceStub.GetSysTime paraGetSysTime=new TestServiceStub.GetSysTime();       
  17.             try {  
  18.                   
  19.                 TestServiceStub.GetSysTimeResponse response = client.GetSysTime(paraGetSysTime);  
  20.                 resultString=response.getGetSysTimeResult();  
  21.             } catch (RemoteException e) {  
  22.                 // TODO Auto-generated catch block   
  23.                 e.printStackTrace();  
  24.             }  
  25.         } catch (AxisFault e) {  
  26.             // TODO Auto-generated catch block   
  27.             e.printStackTrace();  
  28.         }  
  29.         System.out.print(resultString);  
  30.           
  31.     }  

 

 

然后运行,终于成功了。

调用结果:

 

 

 

上次将证书加入WCF服务中去之后,调用也调试成功。这里最后将自定义客户验证加上去之后就是最终版本了。我也不想改了。估计我的毕业设计中关于WCF服务的构架也就是这样了。

好了,先来看下加入自定义客户端验证的服务配置文件:

 

 

  1. <?xml version="1.0" encoding="utf-8" ?>  
  2. <configuration>  
  3.   <system.serviceModel>  
  4.     <behaviors>  
  5.         
  6.       <serviceBehaviors>  
  7.         <behavior name="httpsBasicBindings">  
  8.           <serviceMetadata httpsGetEnabled="true" httpGetEnabled="false" httpGetUrl="http://127.0.0.1:8889/test" />  
  9.           <serviceDebug includeExceptionDetailInFaults="true" />  
  10.           <serviceCredentials>  
  11.             <serviceCertificate storeName="My" x509FindType="FindBySubjectName" findValue="HL7Server" storeLocation="LocalMachine"/>  
  12.             <userNameAuthentication  userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="HostApp.UserValidate,HostApp" />  
  13.           </serviceCredentials>  
  14.         </behavior>  
  15.       </serviceBehaviors>  
  16.     </behaviors>  
  17.     <services>  
  18.       <service behaviorConfiguration="httpsBasicBindings" name="TestService.TestService">  
  19.         <endpoint address="" binding="basicHttpBinding" bindingConfiguration="test1" contract="TestService.ITestService">  
  20.         </endpoint>  
  21.         <endpoint address="mex" binding="mexHttpsBinding" contract="IMetadataExchange" />  
  22.       </service>  
  23.     </services>  
  24.     <bindings>  
  25.       <basicHttpBinding>  
  26.         <binding name="test1">  
  27.           <security mode="Transport">  
  28.             <transport clientCredentialType="Basic"/>  
  29.           </security>  
  30.         </binding>  
  31.       </basicHttpBinding>  
  32.     </bindings>  
  33.   </system.serviceModel>  
  34. </configuration>  

 

 

配置中间中

 

 

  1. <userNameAuthentication  userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="HostApp.UserValidate,HostApp" />  
  2. 和  
  3. <bindings>  
  4.       <basicHttpBinding>  
  5.         <binding name="test1">  
  6.           <security mode="Transport">  
  7.             <transport clientCredentialType="Basic"/>  
  8.           </security>  
  9.         </binding>  
  10.       </basicHttpBinding>  
  11.     </bindings>  

 

 

说明了userNamePasswordValidationMode用户名密码验证是自己来定义的,clientCredentialType=Basic说明是基础的验证。

这2个配置一起控制了要进行验证,验证的方式由我们自己定义。

下面是自定义验证的代码:

 

 

  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Text;  
  5. using System.IdentityModel;  
  6. using System.ServiceModel;  
  7.   
  8. namespace HostApp  
  9. {  
  10.     class UserValidate : System.IdentityModel.Selectors.UserNamePasswordValidator  
  11.     {  
  12.         public override void Validate(string userName, string password)  
  13.         {  
  14.             if (userName != "cch" || password != "cch")  
  15.             {  
  16.                 throw new System.IdentityModel.Tokens.SecurityTokenException("用户名密码错误!");  
  17.             }  
  18.             else  
  19.             {  
  20.             }  
  21.         }  
  22.     }  
  23. }  

 

 

这边账号密码都是cch,如果调用的时候账号密码不正确,就会返回

org.apache.axis2.AxisFault: Transport error: 401 Error: Unauthorized

这个错误。

java调用代码如下:

 

 

  1. import java.rmi.RemoteException;  
  2. import org.apache.axis2.AxisFault;  
  3. import org.apache.axis2.transport.http.HttpTransportProperties;  
  4. import org.tempuri.TestServiceStub;  
  5.   
  6.   
  7.   
  8. //import com.sun.mail.handlers.image_gif;   
  9.   
  10. public class test {  
  11.     /** 
  12.      * @param args 
  13.      */  
  14.     public static void main(String[] args) {  
  15.         String resultString="";  
  16.         try {  
  17.             TestServiceStub client=new TestServiceStub();  
  18.             //-----------------------------------------------------------------------   
  19.             System.setProperty("javax.net.ssl.trustStore""D://java//jre//lib//security//hl7");  
  20.             System.setProperty("javax.net.ssl.trustStorePassword""119215277");  
  21.             //设置信任证书   
  22.             //-----------------------------------------------------------------------   
  23.             //-----------------------------------------------------------------------   
  24.             HttpTransportProperties.Authenticator authenticator=new HttpTransportProperties.Authenticator();  
  25.             authenticator.setUsername("cch");  
  26.             authenticator.setPassword("cch");  
  27.             //设置Basic认证   
  28.             //-----------------------------------------------------------------------   
  29.             client._getServiceClient().getOptions().setProperty(org.apache.axis2.transport.http.HTTPConstants.CHUNKED, Boolean.FALSE);  
  30.             client._getServiceClient().getOptions().setProperty(org.apache.axis2.transport.http.HTTPConstants.AUTHENTICATE, authenticator);  
  31.               
  32.             //client._getServiceClient().getOptions().setTimeOutInMilliSeconds(5*60*1000);   
  33.             TestServiceStub.GetSysTime paraGetSysTime=new TestServiceStub.GetSysTime();       
  34.             try {  
  35.                   
  36.                 TestServiceStub.GetSysTimeResponse response = client.GetSysTime(paraGetSysTime);  
  37.                 resultString=response.getGetSysTimeResult();  
  38.             } catch (RemoteException e) {  
  39.                 // TODO Auto-generated catch block   
  40.                 e.printStackTrace();  
  41.             }  
  42.         } catch (AxisFault e) {  
  43.             // TODO Auto-generated catch block   
  44.             e.printStackTrace();  
  45.         }  
  46.         System.out.print(resultString);  
  47.           
  48.     }  
  49. }  

 

 

调用结果如图:

 

 

posted @ 2011-12-03 15:47  therockthe  阅读(16379)  评论(1编辑  收藏  举报