从 C# 调用 Java WS [英] Calling a Java WS from C#

查看:26
本文介绍了从 C# 调用 Java WS的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图从 C# 程序调用用 Java 编写的 WebService.如果我使用 SoapUi 调用 WS,我可以在 fiddler 中看到调用如下所示:

From a C# program I am trying to call a WebService written in Java. If I use SoapUi to call the WS I can see in fiddler that the call looks like this:

<soap:Envelope xmlns:ser="http://service.webservice.com" xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
       <soap:Header>
           <wsse:Security soap:mustUnderstand="true" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"   xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
               <wsse:UsernameToken wsu:Id="UsernameToken-8684DEB94ABXXXXXXXXXX362973">
                   <wsse:Username>MyUserName</wsse:Username>
                   <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">MyPassword</wsse:Password>
                   <wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">fjwe5h78k7vgheedRv21g==</wsse:Nonce>
                   <wsu:Created>2016-11-17T11:53:56.297Z</wsu:Created>
               </wsse:UsernameToken>
           </wsse:Security>
       </soap:Header>
       <soap:Body>
          <ser:getTrades>
                <ser:filter>
                <ser:fromValueDate>2015-03-23</ser:fromValueDate>
                <ser:toValueDate>2015-03-23</ser:toValueDate>
             </ser:filter>
          </ser:getTrades>
       </soap:Body>
</soap:Envelope>

问题是 VisualStudio 创建的类没有头选项

The Problem is that VisualStudio creates the classes without the header options

// CODEGEN: The optional WSDL extension element 'Policy' from namespace 
'http://schemas.xmlsoap.org/ws/2004/09/policy' was not handled.

那么如何添加带有用户名和密码的 Soap 标头以在 C# 中进行调用?

So how do I add the Soap header with the username and pw to make the call in C# ?

我也一直在寻找使用 WCF 和 wsdl(部分在这里)

I also been looking in using WCF, and from the wsdl (here in part)

    .
    .
    .
    <wsdl:binding name="TradeRetrieverServiceSOAP12Binding" type="ns0:TradeRetrieverServicePortType">
    <wsp:Policy xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" Id="UsernameTokenOverHTTPS">
    <wsp:ExactlyOne>
    <wsp:All>
    <sp:TransportBinding xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
    <wsp:Policy>
    <sp:TransportToken>
    <wsp:Policy>
    <sp:HttpsToken RequireClientCertificate="false"/>
    </wsp:Policy>
    </sp:TransportToken>
    <sp:AlgorithmSuite>
    <wsp:Policy>
    <sp:Basic256/>
    </wsp:Policy>
    </sp:AlgorithmSuite>
    <sp:Layout>
    <wsp:Policy>
    <sp:Lax/>
    </wsp:Policy>
    </sp:Layout>
    </wsp:Policy>
    </sp:TransportBinding>
    <sp:SignedSupportingTokens xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
    <wsp:Policy>
    <sp:UsernameToken sp:IncludeToken="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/AlwaysToRecipient"/>
    </wsp:Policy>
    </sp:SignedSupportingTokens>
    </wsp:All>
    </wsp:ExactlyOne>
    </wsp:Policy>
    .
    .

它创建了这个绑定:

    <system.serviceModel>
        <bindings>
          <customBinding>
            <binding name="TradeRetrieverServiceSOAP12Binding">
              <security defaultAlgorithmSuite="Default" authenticationMode="UserNameOverTransport"
                requireDerivedKeys="true" securityHeaderLayout="Lax" includeTimestamp="false">
                <localClientSettings detectReplays="false" />
                <localServiceSettings detectReplays="false" />
              </security>
              <textMessageEncoding messageVersion="Soap12" />
              <httpsTransport />
            </binding>
          </customBinding>
        </bindings>
        <client>
          <endpoint address="http://Myservices/TradeRetrieverService"
            binding="customBinding" bindingConfiguration="TradeRetrieverServiceSOAP12Binding"
            contract="ServiceReference1.TradeRetrieverServicePortType" name="TradeRetrieverServiceSOAP12port_http" />
        </client>
    </system.serviceModel>

但这给了我这个错误:

    The provided URI scheme 'http' is invalid; expected 'https'.
    Parameter name: via

网址是 http 而不是 https

The url is a http and not https

所以我在这里有货.有什么想法吗?

So I am stock here. Any ideas?

编辑

我现在得到数据,在 fiddler 中,在代码中我得到这个错误:来自命名空间http://docs.oasis- open.org/wss/2004/的标题Security"01/oasis-200401-wss-wssecurity-secext-1.0.xsd'此消息的接收者无法理解,导致无法处理该消息.
此错误通常表示此消息的发送方启用了接收方无法处理的通信协议.请确保客户端绑定的配置与服务的绑定一致.

I now get data, well in fiddler, in code I get this error: The header 'Security' from the namespace 'http://docs.oasis- open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd' was not understood by the recipient of this message, causing the message to not be processed.
This error typically indicates that the sender of this message has enabled a communication protocol that the receiver cannot process. Please ensure that the configuration of the client's binding is consistent with the service's binding.

推荐答案

遗憾的是,Microsoft 决定在 WCF 中移除对 WSSecurity/UsernameTokens 的支持.几周前我还在为同样的问题苦苦挣扎时,我深入研究了这个问题,但无法找出他们这样做的原因.但只要努力,就有希望.

Sadly, Microsoft decided to remove the support for WSSecurity / UsernameTokens in WCF. As i struggled with the same problem a few weeks ago, i digged a little into that issue, but couldn't find out why they did so. But there is hope, with a little bit effort.

首先,如果您不想使用HTTPS,则需要在绑定中指定(您指定了https).例如,我的绑定(也仅使用 HTTP)看起来非常简单:

First of all, if you do not want to use HTTPS, you need to specify that in the binding (You specified https). For example my binding (which is also using only HTTP) looks very simply like that:

<customBinding>
  <binding name="dummySoapBinding">
     <textMessageEncoding writeEncoding="UTF-8" messageVersion="Soap11" />
     <httpTransport />
  </binding>
</customBinding>

之后,为了在请求中创建必要的 WSS 安全标头,我定义了一个应用于端点的自定义行为.此行为将 MessageInspector 附加到发送请求的 ClientRuntime.此消息检查器在发送之前更改消息,并添加安全标头.您可以找到有关自定义行为的一些信息 这里.有关消息检查器的信息可在此处一>.

After that, to create the necessary WSS Security header in the request, i defined an custom behaviour which i applied to the endpoint. This behaviour attaches a MessageInspector to the ClientRuntime which sends the request. This message inspector alters the message before sending, and adds an Security Header. You can find some information about custom behaviours here. Information about message inspectors is available here.

我的 MessageInspector 目前看起来像这样:

My MessageInspector currently looks like this:

public string Username { get; set; }

public string Password { get; set; }

public PasswordDigestMessageInspector(string username, string password)
{
    Username = username;
    Password = password;
}

public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
{
    // do nothing
}

public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel)
{
    // generate token
    var usernameToken = new UsernameToken(this.Username, this.Password, PasswordOption.SendHashed);

    // save token as xml
    var securityToken = usernameToken.GetXml(new XmlDocument());
    var securityTokenText = securityToken.OuterXml;

    // remove vs data
    var limit = request.Headers.Count;
    for (var i = 0; i < limit; ++i)
    {
        if (!request.Headers[i].Name.Equals("VsDebuggerCausalityData")) continue;

        request.Headers.RemoveAt(i);
        break;
    }

    // set encoding type for nonce
    var nonceRegex = new Regex(@"<wsse:Nonce");
    securityTokenText = nonceRegex.Replace(securityTokenText,
        "<wsse:Nonce EncodingType=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary\"");

    var newDoc = new XmlDocument();
    newDoc.LoadXml(securityTokenText);

    // create security header from message
    var securityHeader = MessageHeader.CreateHeader("Security", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", newDoc.DocumentElement, false);

    // add header to request message
    request.Headers.Add(securityHeader);

    // complete
    return Convert.DBNull;
}

我在这里使用的 UsernameToken 类拥有您需要的一切.它在 WCF 之前被及时使用,但遗憾的是已被删除.此类定义在:Microsoft.Web.Services3.Security.Tokens.应该有 nuget 包.或者,您可以手动安装 Microsoft 的 WebService Enhancements (WSE) 3.0(此处).

The class UsernameToken i used here, has everything you need. It was used back in time, before WCF, but sadly has been removed. This class is defined in: Microsoft.Web.Services3.Security.Tokens. There should be nuget package for that. Alternatively you can manually install Microsoft's WebService Enhancements (WSE) 3.0 (here).

有一些额外的逻辑可以修改一些参数,以便我调用的 WebService 接受我的请求.它非常严格,我无法修改它.你可能可以忽略它.

There is some additional logic which modifies some arguments, so that the WebService i call, will accept my request. It's very strict, and i have no option to modify it. You probably can ignore that.

继续,我添加到端点的行为看起来像这样:

Continuing, my behaviour which i add to the endpoint, looks like that:

public class PasswordDigestBehaviour : IEndpointBehavior
{
    public string Username { get; set; }

    public string Password { get; set; }

    public PasswordDigestBehaviour(string username, string password)
    {
        Username = username;
        Password = password;
    }

    public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
    {
        // do nothing
    }

    public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
    {
        clientRuntime.MessageInspectors.Add(new PasswordDigestMessageInspector(this.Username, this.Password));
    }

    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
    {
        // do nothing
    }

    public void Validate(ServiceEndpoint endpoint)
    {
        // do nothing
    }
}

很简单.最后,您可以将行为附加到您的 WCF 客户端类:

Very simple. Finally, you can attach the behaviour to your WCF Client class:

client.Endpoint.Behaviors.Add(new PasswordDigestBehaviour("Testuser", "Testpassword"));

我希望它能为您节省一些时间,因为我最近在这个话题上遇到了很多麻烦.

I hope it saves you some time, because i recently had much trouble with that topic.

我刚刚看到您没有发送散列的密码.您必须在UsernameToken"的构造函数中考虑这一点,因为我正在对其进行哈希处理.

I just saw that you're not sending the password hashed. You have to think of that in the constructor of "UsernameToken" as i am doing it hashed.

这篇关于从 C# 调用 Java WS的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆