不知如何创建SOAP <wsse:Security>头文件

14

我对SOAP协议几乎没有任何经验。我需要连接的服务需要头文件。在Java中,我认为这是一种标准方法,但在C#中,必须手动创建此头文件。

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

我还发现一个线索,使用WS2005可能会生成头文件,因为它有一个WS3插件。有没有人能够评论一下这个?快速查看这个插件后,我发现与安全头部相似的字段,但仍然无法创建头文件。

6个回答

16

我们通过以下代码解决了这个问题:

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”。我只需要在“Password” StartElement之后添加这一行代码:writer.WriteAttributeString("Type", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText");谢谢! - Luís Deschamps Rudge
@David,我在传递SOAP WSSE身份验证头方面遇到了困难。当我尝试使用您的代码时,它说Web服务没有Open()方法。顺便问一下,您是如何生成代理类的?还有外部引用是什么? - Vesnog
@LuísDeschampsRudge 你好,我也遇到了类似的情况,但是我在Visual Studio 2017中无法使上述代码工作。编译器抱怨Web服务中缺少Open()方法。你添加了哪些外部引用,以及你是如何生成代理类(WSE 3.0还是Visual Studio本身)的? https://dev59.com/iqvka4cB1Zd3GeqPuYQk - Vesnog
1
2020年了,这个概念仍然很有用,感谢它。 - Sruit A.Suk
十多年过去了,这仍然是我能找到的最好方法。但我希望不必使用作用域部分。我想只需在行内与服务对象交互。 - Plater
2022年,它仍然非常有用! - Gareth

6

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

创建一个新的SOAP标头

using System.Web.Services.Protocols;

public class SoapAuthHeader : SoapHeader
{
public string Username;
public string Password;
}

在您的Web服务代理类中:

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也提供了一个WSE库,其中包括WS-Security,它比上面简单示例提供了更多的功能。如果您需要在SOAP标头中添加Kerberos令牌或证书签名,那么这就是您要走的路。如果您只需要为通过SSL操作的Web服务添加一个简单的用户名和密码,则可能仅需使用此示例即可。
编辑:关于WSE的简短介绍 早在本十年初期,当Web服务要接管世界时,一些行业参与者(Microsoft、IBM、Sun等)聚在一起,以标准化的方式来处理它们。成立的机构是OASIS。自那时以来,微软发布了许多版本的WSE库来支持某些规范,但有趣的是,即使第一个版本是公开发布的,在.Net框架中它们也从未被纳入。
虽然Web服务仍然非常受欢迎,而且在我看来也是不同互联网应用程序之间集成的好方法,但现在已经不太流行了。毫无疑问,其中一个原因是AJAX和Web服务并不是最好的床上伴侣,尽管这种情况已经有所改善。一旦您开始包括所有其他sWSE规范,Web服务也变得非常复杂,而Web服务应该解决的问题之一是其他RPC协议(CORBA等)中的复杂性。与此同时,REST在牺牲Web服务的情况下获得了很大的流行度,并且AJAX库通常更喜欢它。
Web服务不会很快消失,但它们也不太可能很快接管世界。

3
据我所知,WSE在Visual Studio 2008上无法使用,这真的很烦人。理论上SoapHeader路线不错,但在我的特定情况下,我遇到了一个问题,无法获取头文件中元素的正确命名空间:( - Jon Skeet
1
只是为了明确 - 我正在寻找属于自己类型的元素,在那里我可以使用SoapTypeAttribute并指定名称/命名空间。正是由于SoapElementAttribute中缺少命名空间属性,这让我感到困扰 :( - Jon Skeet
1
你能多说一些关于WSE库的内容吗? - Sergej Andrejev
@sipwiz:我知道这个问题已经几年了,但我很好奇为什么你不能在这里使用WCF?另外,http://stackoverflow.com/q/32703632/247184 - 这是否意味着这里也需要使用WSE?这没有道理 - 你能否提供一些见解? - VoodooChild
1
@VoodooChild 嗯,最初的问题是关于SOAP的,它是WCF可以使用的传输层之一。在这个问题被提出的6.5年里,WCF确实使得集成Web服务的某些方面更加容易了,但对于我自己托管的WCF服务,我仍然需要设置自定义身份验证头。值得庆幸的是,现在做到这一点要容易得多,而且不需要WSE库。 - sipsorcery
显示剩余2条评论

6
有趣的是,你提到这个 - 我最近一直在做正是那样的事情。
我使用了一个SoapExtension来实现它,它使用ChainStream来保留原始流的副本,在BeforeDeserialize期间仅复制流,并在AfterSerialize期间添加头文件。
添加头部的方法是将“新”流(从ChainStream返回)的内容读入XML文档(在我的情况下是XDocument),添加头部,然后将其写入传递给ChainStream的原始流中。
不幸的是,这相当肮脏,而且在需要时无法(据我所知)使用具有适当身份验证信息的新实例。
我已经使用SoapHeader大部分地方代替了上述方法,为Web服务的每个方法添加了适当的属性和字段/属性,其中包含所需标头的实例 - 但是SOAP序列化目前让我头疼,因为要指定正确的元素名称(带命名空间)。 这是我计划在有时间时向其他人询问的问题。
很抱歉无法给出完整的答案 - 也很抱歉没有代码,因为它属于公司而不是我 - 但希望至少能为您提供一个起点。

2

谢谢大家。以下是您所有的代码和想法。

类:

 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
                        }
                    }

1

0

在编程相关内容中,以下是由Domenico Zinzi提出的解决方案,对我非常有用:

我在SecurityHeader类中创建了一个构造函数:

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

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

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

然后就可以正常工作啦 :)

P.S. 无法添加评论,所以在此处添加。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接