.Net调用Java端带有WS-Security支持的Web Service各方案实战【转】

原文:http://www.xuebuyuan.com/641669.html

到现在为止,我们AEP平台已经发布很长一段时间了,也有很多ISV接入并上线了,就语言而言,目前主要有三类:Java、.Net、Php;Java和Php的调用不存在很复杂的问题,但是.Net就要相对复杂不少, 现在已上线的ISV采用的是.Net SDK 2.0 + WSE 2.0【Web Services Enhancements】,但是随着.Net 3.0和3.5的普及,我们需要支持更多的.Net接入方案;

总的来说,针对不同的.Net SDK版本有三种实现来满足对带有WS-Security支持的Web Service调用,即WSE 2.0、WSE 3.0、WCF【Windows Communication Foundation】;其中WSE 2.0针对的是VS 2003版本,WSE 3.0针对的是VS 2005版本,WCF针对的是VS 2008,从SDK要求来看,WSE 2.0和3.0是2.0,而WCF要求是3.0或3.5;前面我的同事已经将WSE 2.0调通,但是对WSE 3.0始终无法调通,具体可以参见《Web Service 、WS-Security、Java和.net的互通(在路上-基于SCA规范的应用服务框架成长记之四》;而WCF经过微软研究院几个月的调试,也已经调通,具体可以参见《.Net在Alibaba的AEP平台上的应用》;不幸的是,针对这两种配置方案经测试是不兼容的,因为不止是.Net客户端调整配置和代码,Java服务端仍然要做出调整,这对于我们的系统侵入是非常大的,因为现在已经有ISV采用WSE 2.0实现在生产环境运行了,要想在服务端做出调整的话必须要做到无缝升级,也即线上所有的ISV客户端无需做任何修改,否则这个升级代价会非常高;

功夫不负有心人啊,经过几天的研究,终于让我找到一个更加有效的解决方案,在新的配置方案下,WSE 2.0、WSE 3.0、WCF全部都可以调试通过,并且对Java端没有任何影响,Php的调用经确认也不存在任何问题;服务端OutflowSecurity配置文件修改如下:其中,1)items中添加了Timestamp的配置,并且在Signature之前,这是为了满足WCF的测试而特意设置的,幸好这不影响其他方案的调试;
2)signatureKeyIdentifier设置为DirectReference,该配置项在前面已经提到过,他和X509KeyIdentifier一样,是将签名者的证书公共部分直接通过soap xml传递给客户端,然后客户端再和他本地的公钥证书做对比,通过之后进行签名验证;关于该配置项,在Java服务端WSS4J的javadoc中描述有误,javadoc中说该项只允许两个值:IssuerSerial和SKIKeyIdentifier,但实际上他可以取5种值,这可能是导致很多人无法在.Net环境调试通过的原因;
3)signatureParts种指定了参与签名的是Timestamp和body部分,这也是WCF所特别需要的,因为WCF要求soap xml种的header部分都要参与签名;

<items>Timestamp Signature</items>
<signatureKeyIdentifier>DirectReference</signatureKeyIdentifier>
<signatureParts>{Element}{http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd}Timestamp;{Element}{}Body</signatureParts>

注意,签名部分中Body的namespace是空的,而没有设置为http://schemas.xmlsoap.org/soap/envelope/(SOAP 1.1对应的ns)或者http://www.w3.org/2003/05/soap-envelope(对应SOAP 1.2),这是为了兼容SOAP 1.1和1.2两个版本,若指定了ns,则只能兼容其一,.Net、Java客户端调用默认都使用SOAP 1.1版,而WSF/PHP则默认使用1.2版本,这样就无法做到各语言的完全
服务端搞定之后,我们就可以来进行.Net客户端的调试工作了,这里就不描述证书的准备工作了,直接开始;注意:以下的配置方案是在带有WSS支持的Web Service服务端特定配置基础上调试通过的,随着服务端配置的变化,客户端的配置和编码也会有一定的调整,所以以下配置并不保证对所有服务端配置都联调成功;

WSE 2.0配置(VS 2003)

1)右键单击工程,选择WSE Settings 2.0 ...开始设置,若在VS 2005环境种使用WSE 2.0,则菜单中没有该项,需要单独打开WSE的Configuration Tool进行设置;
2)在Settings Tool中General页中选中Enable this projects for Web Services Enhancements;
3)Policy页中选中Enable Policy,点击Add ...按钮新建Policy配置文件;
4)Add or Rename Endpoint URI窗口中保留默认值<DefaultEndpoint>,点击OK;
5)保持Next,到Message Settings窗口中对Request Message、Response Message都选择Require Signatures,因为我们只需要进行签名验证,而不需要加密;
6)Next到Client Certificate窗口中,选择客户端签名需要的私钥证书;
7)Trusted Server Certificate窗口中,选择验证服务端签名需要的公钥证书;
8)保存退出,会在工程中出现policyCache.config配置文件,打开;
9)找到response对应的Policy配置项中的wsp:MessagePredicate和wssp:MessageParts,删除其中多余的配置项,只保留wsp:Body();
10)添加Web Reference,然后修改Reference.cs文件中继承的父类,由System.Web.Services.Protocols.SoapHttpClientProtocol修改为Microsoft.Web.Services2.WebServicesClientProtocol;对于此点可能在vs2003环境下面是不需要的,我只在VS 2005环境下面测试过,VS 2005下面是必须的;
11)写ws调用类并运行;
和我们以前WSE 2.0的配置相比,主要变化在于现在不需要选中Use RFC2380选项了;

WSE 3.0配置(VS 2005)

和WSE 2.0配置一样,完全借助于Configuration Tool,注意以下点:
1)可以先配置Policy再添加Web Reference引用,这样产生的代理类会自动继承Microsoft.Web.Services3.WebServicesClientProtocol;
2)调用代码中需要自己设置要使用的policy名称;
3)Security页中X.509 Certificate Settings中的选项都保持默认值,除了Store Location;
4)新建Policy时Message Protection步骤中选择要注意顺序,顺序的不同会产生很不一样的效果,结果会导致接口调用步成功,最傻瓜的操作如下图所示:
这应该是.Net下面调用带有WSS支持的Web Service最傻瓜的操作了,全向导性配置,只需要三行代码即可调用WS;

WCF配置(VS 2008)

WCF的配置又复杂一些了,因为WCF整合了许多其他的东西,而不仅仅是一个支持WS-Security的工具,配置文件中各项的选择性也非常的大,取值范围非常的广,这样反而让开发人员在配置的时候感觉到盲目;WCF也有自己的配置工具WCF Service Configuration Editor,可以从“工具”菜单栏或者开始菜单中加载;这里就不帖具体每项的配置了,因为配置工具中没有向导;直接帖一个可用的配置文件: 

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <!-- If you wan to turn on logging, uncomment the "sources" section below, and set the correct log files in sharedListeners section. -->
    <system.diagnostics>
        <sharedListeners>
          <!-- TODO: Please fix the log file name here! -->
            <add initializeData="F:TempTracesapp_messages_aliclient.svclog"
                type="System.Diagnostics.XmlWriterTraceListener, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
                name="ServiceModelMessageLoggingListener" traceOutputOptions="Timestamp">
                <filter type="" />
            </add>
          <!-- TODO: Please fix the log file name here! -->
            <add initializeData="F:TempTracesapp_tracelog_aliclient.svclog"
                type="System.Diagnostics.XmlWriterTraceListener, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
                name="ServiceModelTraceListener" traceOutputOptions="Timestamp">
                <filter type="" />
            </add>
        </sharedListeners>
    </system.diagnostics>
    <system.serviceModel>
      <!-- To turn on message logging, set these flags to "true" -->
        <diagnostics>
            <messageLogging logEntireMessage="false" logMalformedMessages="false"
                logMessagesAtTransportLevel="false" />
        </diagnostics>
        <bindings>
            <customBinding>
                <binding name="Soap11CustomBinding">
                    <textMessageEncoding messageVersion="Soap11" />
                    <security defaultAlgorithmSuite="Basic128" allowSerializedSigningTokenOnReply="true"
                        authenticationMode="MutualCertificate" requireDerivedKeys="false"
                        securityHeaderLayout="Lax" includeTimestamp="true" keyEntropyMode="CombinedEntropy"
                        messageProtectionOrder="SignBeforeEncrypt" messageSecurityVersion="WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10"
                        requireSecurityContextCancellation="false">
                        <secureConversationBootstrap />
                    </security>
                    <httpTransport />
                </binding>
            </customBinding>
        </bindings>
      <behaviors>
        <endpointBehaviors>
          <behavior name="NewBehavior">
            <clientCredentials>
              <clientCertificate findValue="CN=5" storeLocation="CurrentUser"
                storeName="My" x509FindType="FindBySubjectDistinguishedName" />
              <serviceCertificate>
                <defaultCertificate findValue="CN=alisoft" storeLocation="CurrentUser"
                  storeName="My" x509FindType="FindBySubjectDistinguishedName" />
                <authentication customCertificateValidatorType="" certificateValidationMode="PeerOrChainTrust" />
              </serviceCertificate>
              <issuedToken cacheIssuedTokens="false" />
            </clientCredentials>
          </behavior>
        </endpointBehaviors>
      </behaviors>
        <client>
            <!--121.0.18.160-->
            <endpoint address="http://localhost:1688/webservice/AppConsumeService"
                behaviorConfiguration="NewBehavior" binding="customBinding"
                bindingConfiguration="Soap11CustomBinding" contract="ServiceReference1.AppConsumeServicePortType"
                name="Soap11CustomBindingPort">
                <identity>
                    <dns value="alisoft" />
                    <certificateReference x509FindType="FindBySubjectName" />
                </identity>
            </endpoint>
        </client>
    </system.serviceModel>
</configuration>

客户端调用代码为:

ServiceReference1.AppConsumeServicePortTypeClient c = new ServiceReference1.AppConsumeServicePortTypeClient("Soap11CustomBindingPort");
//设置保护级别为签名
c.Endpoint.Contract.ProtectionLevel = System.Net.Security.ProtectionLevel.Sign;
string bal = c.checkBalance("85", "afc376c9-d14b-4820-bc77-e22878fa8ce3", 11);
Console.WriteLine("bal is:" + bal);

至于WCF配置中的细项我也还没有深入探讨过,以上的配置都是基于微软亚洲研究院联调成功的配置之上的;

posted @ 2016-05-06 14:41  J.Y  阅读(793)  评论(0编辑  收藏  举报