【问题标题】:Clueless about how to create SOAP <wsse:Security> header对如何创建 SOAP <wsse:Security> 标头一无所知
【发布时间】:2009-04-09 13:54:28
【问题描述】:

我几乎没有使用 SOAP 协议的经验。我需要连接到所需标头的服务。我认为这在 Java 中有点标准,但在 C# 中必须手动创建此标头。

这里有没有人能够连接到类似的服务:已经创建了标头,或者甚至可能知道一些可以简化标头创建的标准库?能否分享一些代码或参考资料?

我还发现了一个线索,如果使用 WS2005,可能会生成标题,因为它有 WS3 插件。有人可以对此发表评论吗?快速查看此插件后,我发现与安全标头中类似的字段,但仍然无法创建标头。

【问题讨论】:

    标签: c# .net soap


    【解决方案1】:

    我们可以用下面的代码解决它:

    public class SecurityHeader : System.ServiceModel.Channels.MessageHeader {
        public string userName;
        public string password;
    
        protected override void OnWriteStartHeader (System.Xml.XmlDictionaryWriter writer, System.ServiceModel.Channels.MessageVersion messageVersion)
        {
            writer.WriteStartElement("wsse", Name, Namespace);
            writer.WriteXmlnsAttribute("wsse", Namespace);
        }
    
        protected override void OnWriteHeaderContents (System.Xml.XmlDictionaryWriter writer, System.ServiceModel.Channels.MessageVersion messageVersion)
        {
            writer.WriteStartElement("wsse", "UsernameToken", Namespace);
    
            writer.WriteStartElement("wsse", "Username", Namespace);
            writer.WriteValue(userName);
            writer.WriteEndElement();
    
            writer.WriteStartElement("wsse", "Password", Namespace);
            writer.WriteValue(password);
            writer.WriteEndElement();
    
            writer.WriteEndElement();
    
        }
    
        public override string Name
        {
            get { return "Security"; }
        }
    
        public override string Namespace
        {
            get { return "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"; }
        }
    }
    

    这写了 DataPower 框所需的标头。

    如何使用类SecurityHeader

        public static void Main(string[] args)
        {
    
            var webService = new ServiceReference1.MyWebService();
            ....
           webService.Open();
    
    
            using (OperationContextScope scope = new OperationContextScope((IContextChannel)webService.InnerChannel))
            {
    
                var myObjRequest = GetMyObjRequest();
    
                MessageHeaders messageHeadersElement = OperationContext.Current.OutgoingMessageHeaders;
                messageHeadersElement.Add(SecurityHeader("UserName", "Password"))
    
    
                 var res = webService.MyServe(myObjRequest);
                Console.WriteLine(res.ToString());
            }
        }
    

    【讨论】:

    • 老兄,我爱你!我用了你的代码,它奏效了!我正在与“oracle on demand crm”集成。我只需要在“密码”StartElement 之后添加这一行:writer.WriteAttributeString("Type", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText"); 谢谢!
    • @David 我在传递 SOAP WSSE 身份验证标头时遇到了困难。当我尝试您的代码时,它说 Web 服务没有 Open() 方法。顺便问一下您是如何生成代理类的,外部引用是什么?
    • @LuísDeschampsRudge 你好,我也有类似的情况,但我无法让上述代码在 Visual Studio 2017 中运行。编译器抱怨 Web 服务中缺少 Open() 方法。您添加了哪些外部引用以及如何生成代理类(WSE 3.0 或 Visual Studio 本身)? stackoverflow.com/questions/50430398/…
    • 2020,它仍然有用,感谢您的概念
    【解决方案2】:

    有趣的是你应该提到 - 我最近一直在做正是

    我已经设法使用SoapExtension 来做到这一点,它使用ChainStream 来保留原始流的副本,只需在BeforeDeserialize 期间复制流并在AfterSerialize 期间添加标头。

    添加标头是将“新”流(从ChainStream 返回)的内容读入XML 文档(在我的情况下为XDocument),添加标头,然后将其写入原始文件的情况流传递到ChainStream

    不幸的是,这很脏,而且您不能(据我所知)在需要时使用具有适当身份验证信息的新实例。

    我已经大部分使用SoapHeader代替,为Web服务的每个方法添加一个适当的属性,以及一个带有所需标头实例的适当字段/属性- 但是 SOAP 序列化目前在指定正确的元素名称(带有命名空间)方面让我头疼。这是我一直计划在有时间的时候询问其他人的事情。

    很抱歉不能给你一个完整的答案 - 也很抱歉缺少代码,它属于公司而不是我 - 但希望它至少能给你一个起点。

    【讨论】:

      【解决方案3】:

      在 .Net 中将 SOAP 标头添加到您的 Web 服务代理通常非常容易。这是一个快速的代码示例。

      创建一个新的 SOAP 标头

      using System.Web.Services.Protocols;
      
      public class SoapAuthHeader : SoapHeader
      {
      public string Username;
      public string Password;
      }
      

      在您的网络服务代理类中:

      public class MyWebServicesProxy : System.Web.Services.Protocols.SoapHttpClientProtocol {
      
          public SoapAuthHeader AuthHeader;
      
          ...
      
      }
      

      然后使用:

      SoapAuthHeader authHeader = new SoapAuthHeader();
      authHeader.Username = "username";
      authHeader.Password = "password";
      
      MyWebServicesProxy myProxy = new MyWebServicesProxy();
      myProxy.AuthHeader = authHeader;
      

      编辑:还有其他方法可以做到这一点,Microsoft 确实有一个包含 WS-Security 的WSE library,它提供了比上面的简单示例更多的功能。如果您在 SOAP 标头中需要 Kerberos 令牌或证书签名,那么这是可行的方法。如果您只需要为通过 SSL 运行的 Web 服务添加简单的用户名和密码,那么示例可能就是您所需要的。

      编辑:关于 WSE 的简要介绍 在这十年的早些时候,当 Web 服务将接管世界时,一群行业参与者(微软、IBM、Sun 等)齐心协力制定标准对他们做事的方式。形成的主体是OASIS。从那时起,Microsoft 发布了其 WSE 库的多个版本以支持某些规范,但有趣的是,尽管第一个版本在 2003 年左右公开,但它们从未被合并到 .Net 框架中。

      Web 服务虽然仍然非常流行,并且在我看来是一种在不同 Internet 应用程序之间集成的好方法,但它已经有点失宠了。原因之一无疑是因为 AJAX 和 Web 服务并不是最好的伙伴,尽管情况有所改善。一旦你开始包含所有额外的 sWSE 规范,Web 服务也会变得相当复杂,并且 Web 服务应该解决的一个问题是其他 RPC 协议、CORBA 等的复杂性。与此同时,REST 以牺牲为代价获得了很大的普及Web 服务和 AJAX 库通常更喜欢它。

      Web 服务无论如何都不会很快消失,但它们可能也不会很快接管世界。

      【讨论】:

      • 据我所知,WSE 不适用于 Visual Studio 2008,这真的很烦人。 SoapHeader 路由在理论上很好,但在我的 particular 情况下,我在为标题中的元素获取正确的命名空间时遇到了问题:(
      • @Jon:这太糟糕了。过去我曾有过不得不深入研究 Remoting 和 SOAP 序列化链的情况,虽然这并不难,但绝对不如创建自定义标头或 WSE 优雅。
      • 只是为了说清楚 - 我找到了我自己类型的元素,我可以在其中使用 SoapTypeAttribute 并在那里指定名称/命名空间。 SoapElementAttribute 中缺少命名空间属性让我很苦恼:(
      • 您能详细介绍一下 WSE 库吗?
      • @VoodooChild 最初的问题是关于 SOAP 的,它是 WCF 可以使用的传输层之一。自从提出这个问题以来的 6.5 年中,WCF 确实使集成 Web 服务的某些方面变得更容易,但对于我自己托管的 WCF 服务,我仍然需要设置自定义身份验证标头。值得庆幸的是,现在这样做要容易得多,并且不需要 WSE 库。
      【解决方案4】:

      谢谢各位。您收集的所有代码和想法如下。

      类:

       public class SecurityHeader : System.ServiceModel.Channels.MessageHeader
      {
          public string userName;
          public string password;
      
          public SecurityHeader(string userName, string password)
          {
              this.userName = userName;
              this.password = password;
          }
      
          protected override void OnWriteStartHeader(System.Xml.XmlDictionaryWriter writer, System.ServiceModel.Channels.MessageVersion messageVersion)
          {
              writer.WriteStartElement("wsse", Name, Namespace);
              writer.WriteXmlnsAttribute("wsse", Namespace);
          }
      
          protected override void OnWriteHeaderContents(System.Xml.XmlDictionaryWriter writer, System.ServiceModel.Channels.MessageVersion messageVersion)
          {
              writer.WriteStartElement("wsse", "UsernameToken", Namespace);
              writer.WriteStartElement("wsse", "Username", Namespace);
              writer.WriteValue(userName);
              writer.WriteEndElement();
              writer.WriteStartElement("wsse", "Password", Namespace);
              writer.WriteAttributeString("Type", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText");
              writer.WriteValue(password);
              writer.WriteEndElement();
              writer.WriteEndElement();
          }
      
          public override string Name
          {
              get { return "Security"; }
          }
      
          public override string Namespace
          {
              get { return "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"; }
          }
      }
      

      使用:

           using (IdsTestSvc.MedulaIlacDagitimSistemiIslemleriClient client = new IdsTestSvc.MedulaIlacDagitimSistemiIslemleriClient("MedulaIlacDagitimSistemiIslemleriPort"))
                          {
                              using (OperationContextScope scope = new OperationContextScope(client.InnerChannel))
                              {
                                  MessageHeaders messageHeadersElement = OperationContext.Current.OutgoingMessageHeaders;
                                  messageHeadersElement.Add(new SecurityHeader(_main.IdsFirmCode, _main.IdsFirmCode));
       ....Do something
                              }
                          }
      

      【讨论】:

        【解决方案5】:

        有一个名为 ClearUserNameBinding 的开源自定义绑定,此绑定有助于在 Http 上将 UserNameToken 作为 clearText 传递。当需要使用 WCF 客户端使用基于 Java 的 Web 服务时,这对我有帮助。

        http://code.google.com/p/wcf-clear-username-binding/ http://webservices20.blogspot.com/2008/11/introducing-wcf-clearusernamebinding.html

        【讨论】:

          【解决方案6】:

          Domenico Zinzi 的解决方案对我有用,修改如下: 我在 SecurityHeader 类中创建了一个构造函数:

          public SecurityHeader(string userName, string password){ userName = userName; password = password; }

          在调用它时,我使用了“New”关键字,如下所述:

          messageHeadersElement.Add(New SecurityHeader("UserName", "Password"))

          它成功了:)

          附:无法将其添加为 cmets,这就是为什么在此处添加它。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多