(试图)从WSE 3.0迁移到WCF客户端代码 [英] (Attempting to) migrate from WSE 3.0 to WCF for client code

查看:230
本文介绍了(试图)从WSE 3.0迁移到WCF客户端代码的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直都在网为这一点。我刚刚有一个时间的魔鬼做的,其网络服务我想要消耗拒绝正式支持WCF作为消费的一个方法的供应商。

I have been all over the net for this. I've just been having a devil of a time doing it, and the vendor whose web service I'm trying to consume refuses to officially support WCF as a method of consumption.

我不是Web服务专家,所以我会尽我所能来记录以及与此战后初期解释,但通过各种手段,如果你需要它获得更多信息,并希望我能提供什么是必要的。

I'm no web services expert, so I'll do my best to document and explain with this initial post, but by all means, request more information if you need it, and hopefully I'll be able to supply whatever is necessary.

服务

在我的公司,我们使用一个供应商应用程序暴露的服务。该应用程序是用Java编写的,它看起来像WSDL与Apache Axis的1.2创建的。

At my company, we use a vendor application that exposes a service. The application is written in java, and it looks like the wsdl was created with Apache Axis 1.2.

中的代码

我的遗留代码使用WSE 3.0。尤其是,它使用已WSE末自动上涨的代理类。这让我用一个更简单的身份验证方案(我能得到它的工作的唯一途径)。我并不需要使用证书。我使用 SecurityPolicyAssertion 的衍生物,并且在被传递到策略对象把它包>客户端类的SetPolicy 方法。这里就是我需要做的创建客户端的工作实例:

My legacy code uses WSE 3.0. In particular, it uses the proxy classes that have "WSE" auto-tacked at the end. This allows me to use a much simpler authentication scheme (the only way I could get it to work). I don't need to use certificates. I use a derivative of SecurityPolicyAssertion, and wrap it in a Policy object that gets passed to the SetPolicy method of the client class. Here's all I need to do to create a working instance of the client:

MyWebServiceWse api = new MyWebServiceWse();
api.Url = myUrl;
api.SetPolicy(new Policy(new MyDerivedSecurityAssertion(user, pass)));



我默认情况下,外装即用的WCF代码(有服务引用生成)不不能接受的凭据,所以我知道有一个问题马上蝙蝠。我读过的各种事情在网上关于使用不同的安全或结合在我的的app.config ,但没有设置有过完全奏效。我丰富的修修补补后最常见的错误是 WSDoAllReceiver:请求不包含必需的安全性标题

My default, out-of-the-box code for WCF (generated with a service reference) does not accept credentials, so I know there's a problem right off the bat. I've read various things online about using different security or binding settings in my app.config, but nothing has ever completely worked. My most common error after copious tinkering is WSDoAllReceiver: Request does not contain required Security header.

下面的应用程序。配置。也许我们可以告诉我什么开始应该改变这里,以方便传递凭据 - 再次,我已经在网上看到不同的看法

Here's the app.config. Perhaps we could start by telling me what ought to change here to facilitate passing the credentials--again, I've seen varying opinions online.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.serviceModel>
        <bindings>
            <basicHttpBinding>
                <binding name="MySoapBinding" closeTimeout="00:01:00"
                    openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
                    allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
                    maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
                    messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
                    useDefaultWebProxy="true">
                    <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
                        maxBytesPerRead="4096" maxNameTableCharCount="16384" />
                    <security mode="None">
                        <transport clientCredentialType="None" proxyCredentialType="None"
                            realm="" />
                        <message clientCredentialType="UserName" algorithmSuite="Default" />
                    </security>
                </binding>
            </basicHttpBinding>
        </bindings>
        <client>
            <endpoint address="http://xyz:12345/services/MyService"
                binding="basicHttpBinding" bindingConfiguration="MySoapBinding"
                contract="MyNS.MyService" name="MyService" />
        </client>
    </system.serviceModel>
</configuration>



我已经改变了一些属性掩盖了特定的服务,我们正在使用(公司政策和所有。)

I have changed some of the attributes to obscure the specific service we are using (company policy and all that).

这是示例C#代码到目前为止(在一个控制台应用程序测试):

And here is the sample C# code so far (testing in a console app):

MyClient client = new MyClient();
client.listMethod();



更新

阅读SO职位: WCF安全。 。 。

我已经相应地更新我的app.config,和我现在过的用户名和pwd代码。我仍然收到了同样的错误:

I have updated my app.config accordingly, and am now passing username and pwd in code. I am still receiving the same error:

WSDoAllReceiver: Request does not contain required Security header

20120517更新

一个成功的请求(从WSE3 ):

A successful request (from WSE3):


  <soap:Header>
    <wsa:Action>
    </wsa:Action>
    <wsa:MessageID>urn:uuid:cb739422-c077-4eec-8cb2-686837b76878</wsa:MessageID>
    <wsa:ReplyTo>
      <wsa:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:Address>
    </wsa:ReplyTo>
    <wsa:To>http://removed-for-security</wsa:To>
    <wsse:Security soap:mustUnderstand="1">
      <wsu:Timestamp wsu:Id="Timestamp-e13feaf9-33d9-47bf-ab5b-60b4611eb81a">
        <wsu:Created>2012-05-17T11:25:41Z</wsu:Created>
        <wsu:Expires>2012-05-17T11:30:41Z</wsu:Expires>
      </wsu:Timestamp>
      <wsse:UsernameToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="SecurityToken-00c26e1a-3b3b-400f-a99a-3aa54cf8c8ff">
        <wsse:Username>change-to-protect-the-innocent</wsse:Username>
        <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">nice-try</wsse:Password>
        <wsse:Nonce>KJMvUuWF2eO2uIJCuxJC4A==</wsse:Nonce>
        <wsu:Created>2012-05-17T11:25:41Z</wsu:Created>
      </wsse:UsernameToken>
    </wsse:Security>
  </soap:Header>
  <soap:Body>
    <listChannels xmlns="http://removed-for-security">
      <rowfrom>0</rowfrom>
      <rowto>10</rowto>
    </listChannels>
  </soap:Body>
</soap:Envelope>

在获得WCF跟踪工作 - 将在短期内增加

Working on getting the WCF trace--will add shortly.

20120517更新2

和这里是从WCF信封:

And here's the envelope from WCF:

  <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
    <s:Header>
      <Action s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none"></Action>
    </s:Header>
    <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <listChannels xmlns="http://removed-for-security">
        <rowfrom>1</rowfrom>
        <rowto>2147483647</rowto>
      </listChannels>
    </s:Body>
  </s:Envelope>



20120518更新
我试图实施该解决方案张贴麦克 - 米勒链接到的意见。现在,我收到以下错误(没有消息最终得到发送,因为有些事情在barfing的方案):

20120518 UPDATE I have tried implementing the solution in the post that Mike Miller links to in the comments. Now I receive the following error (no message ends up getting sent because something's barfing on the scheme):

The provided URI scheme 'http' is invalid; expected 'https'.

和的情况下,想询问,是的,我需要发送通过HTTP,是的,我M意识到凭据作为加密字串发送: - )

And in case anyone wants to ask, yes, I need to send over http, and yes, I'm aware that credentials are sent as unencrypted strings :-)

推荐答案

您需要的是通过HTTP传输发送用户名令牌这是不是在WCF开箱即用的支持。除此之外,您的令牌使用现时/创建这也是没有开箱即用的。你有两个选择:

What you need is to send a username token over http transport which is not supported in wcf ootb. in addition your token uses nonce/created which is also not ootb. you have 2 options:


  1. 此的 OSS项目增加现时/创建用户名令牌。这 OSS项目增加了通过HTTP发送的用户名的能力。你需要这两个项目结合在一起。

  1. this oss project adds nonce/created to the username token. this oss project adds the ability to send username over http. you would need to combine both projects together.

WS-Security是通常被认为是复杂的,但你在最简单​​的形式(用户名)使用它。最简单的将开除任何WCF安全设置在一起,并通过自己在消息检查创建整个安防头的!正如你所看到的最头都只是静态的XML节点,而大多数值是很清楚的(你知道的用户名)。唯一棘手的两个是随机数,哪些是你可以看看如何在这做的 OSS项目(每一条线)。有这个选项,可能会更容易的一个变种 - 使用 CUB 毕竟和实施一个定制的编码器这推动timestmpa /当前时间。我会去为后者,但我因为我开发CUB偏见......

ws-security is usually considered complex, but you use it in its simplest form (username). the easiest would be to dismiss any wcf security setting all together and create the whole security header by yourself in a message inspector! As you can see most headers are just static xml nodes, and most values are pretty clear (you know the username). the only tricky two are the nonce and the timestamps which you could look how to do in this oss project (one line each). There is a variant of this option which may be easier - use CUB after all and implement a custom encoder which pushes the timestmpa/nonce. I would go for the latter but I'm biased since I developed CUB...

另外还有WS-解决,您可以在您的自定义编码messageVersion属性来配置标题。因为你忽略的WSA前缀定义信封头我不能告诉准确值。

There's also the ws-addressing headers which you can configure on your custom encoding "messageVersion" property. I can't tell the exact value since you omitted the envelope header with the wsa prefix definition.

如果你想私下帮助(因为你似乎有安全限制)由。一切手段给我发送电子邮件从我的博客

If you want help privately (since you seem to have security restrictions) by all means send me an email from my blog.

编辑:我已经实现它。请按照下列步骤操作:

I've implemented it for you. follow these steps:


  1. 并使自己熟悉它(而不是内部,只是如何使用它根据博客文章)

  1. download cub and make yourself familiar with it (not the internals, just how to use it according to the blog post)

要System.Runtime.Serialization.dll添加引用该项目ClearUsernameBinding

add reference to System.Runtime.Serialization.dll to the project ClearUsernameBinding

添加一个新的文件到该项目:UsernameExEncoder.cs。粘贴的内容:

add a new file to that project: UsernameExEncoder.cs. Paste this content:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel.Channels;
using System.IO;
using System.Xml;
using System.Security.Cryptography;

namespace Webservices20.BindingExtensions
   {
    class UsernameExEncoderBindingElement : MessageEncodingBindingElement
    {
    MessageEncodingBindingElement inner;        

    public UsernameExEncoderBindingElement(MessageEncodingBindingElement inner)
    {
        this.inner = inner;            
    }

    public override IChannelFactory<TChannel> BuildChannelFactory<TChannel>(BindingContext context)
    {
        context.BindingParameters.Add(this);
        var res = base.BuildChannelFactory<TChannel>(context);
        return res;
    }

    public override bool CanBuildChannelFactory<TChannel>(BindingContext context)
    {
        var res = base.CanBuildChannelFactory<TChannel>(context);
        return res;
    }

    public override MessageEncoderFactory CreateMessageEncoderFactory()
    {
        return new UsernameExEncoderFactory(this.inner.CreateMessageEncoderFactory());
    }      

    public override MessageVersion MessageVersion
    {
        get
        {
            return this.inner.MessageVersion;
        }
        set
        {
            this.inner.MessageVersion = value;
        }
    }

    public override BindingElement Clone()
    {
        var c = (MessageEncodingBindingElement)this.inner.Clone();
        var res = new UsernameExEncoderBindingElement(c);
        return res;
    }

    public override T GetProperty<T>(BindingContext context)
    {
        var res = this.inner.GetProperty<T>(context);
        return res;
    }
}

class UsernameExEncoderFactory : MessageEncoderFactory
{
    MessageEncoderFactory inner;        

    public UsernameExEncoderFactory(MessageEncoderFactory inner)
    {
        this.inner = inner;            
    }

    public override MessageEncoder Encoder
    {
        get { return new UsernameExEncoder(inner.Encoder); }
    }

    public override MessageVersion MessageVersion
    {
        get { return this.inner.MessageVersion; }
    }

}

class UsernameExEncoder : MessageEncoder
{
    MessageEncoder inner;

    public override T GetProperty<T>()
    {
        return inner.GetProperty<T>();
    }

    public UsernameExEncoder(MessageEncoder inner)
    {
        this.inner = inner;
    }

    public override string ContentType
    {
        get { return this.inner.ContentType; }
    }

    public override string MediaType
    {
        get { return this.inner.MediaType; }
    }

    public override MessageVersion MessageVersion
    {
        get { return this.inner.MessageVersion; }
    }

    public override bool IsContentTypeSupported(string contentType)
    {
        return this.inner.IsContentTypeSupported(contentType);
    } 

    public override Message ReadMessage(ArraySegment<byte> buffer, BufferManager bufferManager, string contentType)
    {
        return this.inner.ReadMessage(buffer, bufferManager, contentType);
    }

    public override Message ReadMessage(System.IO.Stream stream, int maxSizeOfHeaders, string contentType)
    {
        return this.inner.ReadMessage(stream, maxSizeOfHeaders, contentType);
    }

    public override ArraySegment<byte> WriteMessage(Message message, int maxMessageSize, BufferManager bufferManager, int messageOffset)
    {   
        //load the message to dom
        var mem = new MemoryStream();
        var x = XmlWriter.Create(mem);
        message.WriteMessage(x);
        x.Flush();
        mem.Flush();
        mem.Position = 0;
        XmlDocument doc = new XmlDocument();
        doc.Load(mem);

        //add the missing elements
        var token = doc.SelectSingleNode("//*[local-name(.)='UsernameToken']");
        var created = doc.CreateElement("Created", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
        var nonce = doc.CreateElement("Nonce", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
        token.AppendChild(created);
        token.AppendChild(nonce);

        //set nonce value
        byte[] nonce_bytes = new byte[16];
        RandomNumberGenerator rndGenerator = new RNGCryptoServiceProvider();
        rndGenerator.GetBytes(nonce_bytes);
        nonce.InnerText = Convert.ToBase64String(nonce_bytes);

        //set create value
        created.InnerText = XmlConvert.ToString(DateTime.Now.ToUniversalTime(), "yyyy-MM-ddTHH:mm:ssZ");

        //create a new message
        var r = XmlReader.Create(new StringReader(doc.OuterXml));
        var newMsg = Message.CreateMessage(message.Version, message.Headers.Action, r);

        return this.inner.WriteMessage(newMsg, maxMessageSize, bufferManager, messageOffset);
    }




    public override void WriteMessage(Message message, System.IO.Stream stream)
    {
        this.inner.WriteMessage(message, stream);
    }
}
}


  • 在该文件ClearUsernameBinding.cs替换此:

  • In the file ClearUsernameBinding.cs replace this:

    res.Add(新TextMessageEncodingBindingElement(){MessageVersion = this.messageVersion});

    res.Add(new TextMessageEncodingBindingElement() { MessageVersion = this.messageVersion});

    本:

    VAR textEncoder =新TextMessageEncodingBindingElement(){MessageVersion = this.messageVersion};
    res.Add(新UsernameExEncoderBindingElement(textEncoder));

    var textEncoder = new TextMessageEncodingBindingElement() { MessageVersion = this.messageVersion }; res.Add(new UsernameExEncoderBindingElement(textEncoder));

    在在的app.config项目TestClient的有绑定元素上的messageVersion属性。您尚未发布您的信封的根,所以我不能肯定知道,但可能需要将其设置为Soap11WSAddressingAugust2004或Soap11WSAddressing10(或SOAP12,而不是其中之一)。

    In the project TestClient in app.config there is a messageVersion property on the binding element. You have not published the root of your envelope so I cannot know for sure, but probably you need to set it to Soap11WSAddressingAugust2004 or Soap11WSAddressing10 (or one of these with Soap12 instead).

    祝你好运!

    这篇关于(试图)从WSE 3.0迁移到WCF客户端代码的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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