(尝试)从WSE 3.0迁移到WCF以获取客户端代码

Bri*_*haw 10 c# wcf wse web-services

为此,我一直都在网上.我刚刚做了一段时间的恶魔,我试图消费的网络服务供应商拒绝正式支持WCF作为一种消费方法.

我不是网络服务专家,所以我会尽力记录和解释这篇文章,但无论如何,如果你需要的话,请求更多的信息,希望我能够提供任何必要的东西.

服务

在我的公司,我们使用公开服务的供应商应用程序.该应用程序是用java编写的,看起来wsdl是用Apache Axis 1.2创建的.

代码

我的遗留代码使用WSE 3.0.特别是,它使用最后自动添加"WSE"的代理类.这允许我使用更简单的身份验证方案(我可以让它工作的唯一方法).我不需要使用证书.我使用的衍生物SecurityPolicyAssertion,并将其包装在一个Policy传递给SetPolicy客户端类的方法的对象中.以下是创建客户端工作实例所需的全部内容:

MyWebServiceWse api = new MyWebServiceWse();
api.Url = myUrl;
api.SetPolicy(new Policy(new MyDerivedSecurityAssertion(user, pass)));
Run Code Online (Sandbox Code Playgroud)

我的默认,开箱即用的WCF代码(使用服务引用生成)不接受凭据,所以我知道这是一个问题.我已经在网上阅读了关于security在我的网站中使用不同或绑定设置的各种内容app.config,但没有任何内容完全奏效.经过大量修补后,我最常见的错误是WSDoAllReceiver: Request does not contain required Security header.

这是app.config.也许我们可以先告诉我这里应该改变什么来促进传递证书 - 再次,我在网上看到了不同的意见.

<?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>
Run Code Online (Sandbox Code Playgroud)

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

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

MyClient client = new MyClient();
client.listMethod();
Run Code Online (Sandbox Code Playgroud)

UPDATE

阅读此SO帖子:wcf security....

我已相应更新了我的app.config,现在正在代码中传递用户名和密码.我仍然收到同样的错误:

WSDoAllReceiver: Request does not contain required Security header
Run Code Online (Sandbox Code Playgroud)

20120517更新

一个成功的请求(来自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>
Run Code Online (Sandbox Code Playgroud)

致力于获取WCF跟踪 - 将很快添加.

20120517更新2

这是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>
Run Code Online (Sandbox Code Playgroud)

20120518更新 我尝试在Mike Miller在评论中链接的帖子中实现解决方案.现在我收到以下错误(没有消息最终被发送,因为有些东西正在计划中):

The provided URI scheme 'http' is invalid; expected 'https'.
Run Code Online (Sandbox Code Playgroud)

如果有人想问,是的,我需要通过http发送,是的,我知道凭据是以未加密的字符串形式发送的:-)

Yar*_*veh 11

您需要的是通过http传输发送用户名令牌,这在wcf ootb中是不受支持的.此外,您的令牌使用nonce/created,这也不是ootb.你有2个选择:

  1. 这个oss项目将nonce/created添加到用户名令牌中.这个oss项目增加了通过http发送用户名的功能.你需要将两个项目结合在一起.

  2. ws-security通常被认为是复杂的,但你以最简单的形式(用户名)使用它.最简单的方法是将所有wcf安全设置一起解除,并在消息检查器中自己创建整个安全标头!正如您所看到的,大多数标头只是静态xml节点,并且大多数值非常清楚(您知道用户名).唯一棘手的两个是nonce和时间戳,你可以看看在这个oss项目中如何做(每行一行).这个选项的变体可能更容易 - 毕竟使用CUB并实现一个推送timestmpa/nonce 的自定义编码器.我会选择后者但是因为我开发了CUB而我有偏见...

您还可以在自定义编码"messageVersion"属性上配置ws-addressing标头.由于您省略了带有wsa前缀定义的信封标头,因此无法确切地说出确切的值.

如果您私下想要帮助(因为您似乎有安全限制),请通过我的博客向我发送电子邮件.

编辑:我已经为你实现了它.跟着这些步骤:

  1. 下载幼崽并让自己熟悉它(不是内部,根据博客文章如何使用它)

  2. 将System.Runtime.Serialization.dll的引用添加到项目ClearUsernameBinding

  3. 向该项目添加一个新文件:UsernameExEncoder.cs.粘贴此内容:

    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);
        }
    }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  4. 在ClearUsernameBinding.cs文件中替换为:

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

    有了这个:

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

  5. 在app.config中的项目TestClient中,绑定元素上有一个messageVersion属性.您尚未发布信封的根目录,因此我无法确定,但您可能需要将其设置为Soap11WSAddressingAugust2004或Soap11WSAddressing10(或其中一个使用Soap12).

祝好运!