如何让 WCF 和 Java 客户端工作。需要专家帮助
背景(有点长):
几年前,我为客户编写了一个 WCF 服务,该服务配置为使用 basicHttpBinding,安全模式 =“Transport”,clientCredentialType =“Certificate”,即客户端使用(客户端)证书对自己进行身份验证。该服务使用自定义的 AuthorizationManager 来检查传入证书的指纹是否存在于有效证书指纹的预定义列表中。如果传入的证书被认为是有效的,则允许操作继续(如果不是,则抛出异常)。
这项服务已经完美运行了大约四年,每个人都很满意。然而,由于需求经常发生变化,最近开发人员联系了我的客户,希望将他们的应用程序连接到我客户的服务。唯一的问题是这些开发人员正在使用 Java 的某些变体作为他们的首选平台,现在我们面临着一些严重的问题。长话短说,没有人设法让他们的 Java 实现(例如 Metro、Axis2)与当前配置的服务一起工作。
上周,我们尝试通过更改与安全模式 =“TransportWithMessageCredential”和消息 clientCeredentialType =“UserName”的 wsHttpBinding 的绑定,使其与用 Java(Metro、JAX-WS)编写的客户端一起使用。我还在配置文件中的服务凭据元素中添加了自定义 UserNamePassWordValidatorType。
然后,我注释掉了自定义 AuthorizationManager,因为没有来自客户端的证书。
你瞧,这一次我们同时获得了 SoapUI 和 Java 客户端来与服务进行对话。
(顺便说一句,我们的服务是在 Windows 服务中自托管的)
很高兴,我们决定使用两个绑定来配置该服务,一个是长期正常工作的现有 basicHttpBinding,另一个是新测试的 wsHttpBinding。所以我们会得到这样的内容:
<services>
<service name="SuperDuperService" behaviorConfiguration="superDuperBehaviour">
<endpoint binding="basicHttpBinding" contract="ISuperDuperService" bindingConfiguration="SecureTransport"/>
<endpoint binding="wsHttpBinding" address="stws" contract="ISuperDuperService" bindingConfiguration="SecureTransportAndSoap"/>
<endpoint binding="mexHttpsBinding" address="mex" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="https://<url + port>/SuperDuperService"/>
</baseAddresses>
</host>
</service>
</services>
<bindings>
<basicHttpBinding>
<binding name="SecureTransport" maxBufferSize="2065536" maxBufferPoolSize="524288" maxReceivedMessageSize="2065536">
<security mode="Transport">
<transport clientCredentialType="Certificate"/>
</security>
<readerQuotas maxDepth="32" maxStringContentLength="6553600" maxArrayLength="2065536"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
</binding>
</basicHttpBinding>
<wsHttpBinding>
<binding name="SecureTransportAndSoap">
<security mode="TransportWithMessageCredential">
<message clientCredentialType="UserName"/>
</security>
</binding>
</wsHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="superDuperBehaviour">
<serviceCredentials>
<!-- The following element specifies the certificate use by this service for HTTPS (SSL) based transport security -->
<serviceCertificate findValue="<some identifier>"
storeLocation="LocalMachine"
storeName="My"
x509FindType="FindBySubjectName" />
<userNameAuthentication userNamePasswordValidationMode ="Custom" customUserNamePasswordValidatorType
="SupportClasses.CustomUserNameValidator,SupportClasses"/>
</serviceCredentials>
<!-- The following element specifies how we're authorizing based on the client certificates received -->
<serviceAuthorization serviceAuthorizationManagerType="SupportClasses.AuthorizationManager, SupportClasses"/>
<serviceMetadata httpsGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
那么您可能会问的问题在哪里?好吧,看到
元素了吗?您可能知道,此元素全局应用于服务,这意味着在运行时将调用 CustomUserNameValidator 和 AuthorizationManager。当客户端使用 wsHttpBinding 调用服务时,后者会抱怨不存在证书!
啊!
替代解决方案。
到目前为止,这些是我提出的替代解决方案:
替代方案 1) 创建另一个 Windows 服务,在不同的 URL 上托管我们的 WCF 服务。然后这两个服务将有一个单独的配置。
替代方案 2) 创建托管在同一 Windows 服务中的两个服务实现,并在元素中公开它们,每个实现都有自己的绑定和 serviceBehaviour
替代方案 3) 弄清楚它是否位于尽一切可能保持当前配置并使 CustomUserNameValidator 和 AuthorizationManager 和平共存
抱歉这篇长文章,但在提供我的问题的背景时我需要彻底。
问题 1) 有没有人使用 WCF 使用重要的配置来与 Java 客户端一起工作?
问题 2) 有人对如何解决替代方案 3 有建议吗? (如果可能的话)
问题 3 您会推荐上述替代方案中的哪一种(如果有)?
问题 4) 还有您知道但我没有想到的其他替代方案吗?
根据记录,我研究了 WCF 互操作性工具,但我真的看不出它可以如何帮助我们。如果阅读本文的任何人使用互操作性“向导”获得了良好的结果,
请告诉我。
提前致谢。
——诺吉
Background (a wee bit long):
A few years back I wrote for a customer a WCF service that is configured to use the basicHttpBinding with security mode = "Transport" with clientCredentialType = "Certificate", i.e. the clients are authenticating themselves using a (client) certificate. The service employs a custom AuthorizationManager that checks if the thumbprint of the incoming certificate is present in a predefined list of valid certificate thumbprints. If the incoming certificate is deemed valid, the operation is allowed to continue (if not, an exception is thrown).
The service has worked flawlessly for approximately four years and everyone's been happy. However, as so often happens requirements change and recently my customer has been approached by developers wishing to connect their applications to my customer's service. The only problem is that these developers are using some variant of Java as their preferred platform and now we are facing some serious problems. To make a long story short, no one has managed to get their Java implementation (e.g. Metro, Axis2) to work with the service as it is presently configured.
Last week we tried getting it to work with a client written in Java (Metro, JAX-WS) by changing the binding to the wsHttpBinding with security mode = "TransportWithMessageCredential" with Message clientCeredentialType = "UserName". I also added a custom UserNamePassWordValidatorType to the service credentials element in the config file.
I then commented out the custom AuthorizationManager since no certificate was coming from the client.
Lo' and behold, this time we got both SoapUI and the Java client proper to talk to the service.
(Btw, our service is self-hosted in a Windows Service)
Happy as can be, we decided to configure the service with two bindings, the existing basicHttpBinding that's been working without a glitch for a long time, and the newly tested wsHttpBinding. So we would have something like:
<services>
<service name="SuperDuperService" behaviorConfiguration="superDuperBehaviour">
<endpoint binding="basicHttpBinding" contract="ISuperDuperService" bindingConfiguration="SecureTransport"/>
<endpoint binding="wsHttpBinding" address="stws" contract="ISuperDuperService" bindingConfiguration="SecureTransportAndSoap"/>
<endpoint binding="mexHttpsBinding" address="mex" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="https://<url + port>/SuperDuperService"/>
</baseAddresses>
</host>
</service>
</services>
<bindings>
<basicHttpBinding>
<binding name="SecureTransport" maxBufferSize="2065536" maxBufferPoolSize="524288" maxReceivedMessageSize="2065536">
<security mode="Transport">
<transport clientCredentialType="Certificate"/>
</security>
<readerQuotas maxDepth="32" maxStringContentLength="6553600" maxArrayLength="2065536"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
</binding>
</basicHttpBinding>
<wsHttpBinding>
<binding name="SecureTransportAndSoap">
<security mode="TransportWithMessageCredential">
<message clientCredentialType="UserName"/>
</security>
</binding>
</wsHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="superDuperBehaviour">
<serviceCredentials>
<!-- The following element specifies the certificate use by this service for HTTPS (SSL) based transport security -->
<serviceCertificate findValue="<some identifier>"
storeLocation="LocalMachine"
storeName="My"
x509FindType="FindBySubjectName" />
<userNameAuthentication userNamePasswordValidationMode ="Custom" customUserNamePasswordValidatorType
="SupportClasses.CustomUserNameValidator,SupportClasses"/>
</serviceCredentials>
<!-- The following element specifies how we're authorizing based on the client certificates received -->
<serviceAuthorization serviceAuthorizationManagerType="SupportClasses.AuthorizationManager, SupportClasses"/>
<serviceMetadata httpsGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
So where is the problem you may ask? Well, see that <serviceBehavior>
element? As you well may know this element applies globally to the service meaning that during run-time both the CustomUserNameValidator and the AuthorizationManager will be called. The latter will complain that there is no certificate present when a client calls the service using the wsHttpBinding!
Argh!
Alternative solutions.
So far, these are the alternative solutions I have come up with:
Alternative 1) Create another Windows Service hosting our WCF service on a different URL. Then both services will have a separate configuration.
Alternative 2) Create two service implementations hosted in the same Windows Service and expose them both in the element each with their own binding and serviceBehaviour
Alternative 3) Figure out if it is at all possible to keep the current configuration and have the CustomUserNameValidator and the AuthorizationManager coexist peacefully
Sorry for this long post, but I needed to be thorough when providing the background for my question(s).
Question 1) Has anyone gotten WCF to work with Java clients using nontrivial configurations?
Question 2) Has anyone got a suggestion on how to solve alternative 3? (if at all possible)
Question 3) Which one, if any, of the above alternatives, would you recommend?
Question 4) Is there other alternatives you know of that I haven't thought of?
For the record, I've looked into the WCF interoperability tool, but I can't really see how it can help us. If anyone reading this has had good results using the interoperability "wizard",
please let me know.
Thanks in advance.
--norgie
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

我将首先在 //services 中添加第二个元素,并使用新的 @bindingConfiguration 属性和不同的 @address 属性对其进行配置。我认为这比替代方案 1、2 或 3 更简单;或者也许是替代方案3,我不知道。
Web 服务的存在是为了提供语言、供应商、平台和供应商中立的互操作。 WCF 和 Java 每天都在现实世界的解决方案中互操作。
您是否看过 WCF 和Java 互操作?
另外,听起来您会受益于使用一些诊断工具,这些工具可以让您看到 .NET/WCF 客户端消息和 Java 客户端消息之间的差异。使用 Fiddler 或其他嗅探器实用程序查看线路上的消息。打开 WCF 跟踪以查看 WCF 在收到消息后执行的操作。
I would start by adding a second element in //services and configure that with a new @bindingConfiguration attribute and a distinct @address attribute. I think that will be simpler that alternatives 1, 2, or 3; or maybe it is alternative 3, I can't tell.
Web services exist to provide interoperation that is language-, vendor-, platform-, and vendor-neutral. WCF and Java interoperate in real world solutions every day.
Have you looked at this series on WCF and Java interoperation?
Also, it sounds like you would benefit from using some diagnostics tools that will let you see the different between the .NET/WCF client message and Java client messages. Use Fiddler or some other sniffer utility to see the messages on the wire. Turn on WCF tracing to see what WCF does once it receives the message.