向WSDL添加SOAP隐式头

17

我的问题与如何在WSDL未定义SOAP头时传递SOAP头?类似,但又不同。

我使用的 web service 中,所有方法都需要认证信息,这些信息以明文形式发送到 SOAP 头中。然而,我的 WSDL 没有包含任何 SOAP 头信息。我有一个必须使用来自 WSDL 生成代码的自定义平台工具。由于没有可用的头信息,我无法直接使用生成的类 - 我不想手动修改代码以适应头信息。

我尝试在 WSDL 中指定 SOAP 头,但未能获得正确的命名空间。该 WSDL 在此处:https://stage.totalcheck.sensis.com.au/service/webservice?wsdl,SOAP 头如下:

    <soapenv:Header>
        <wsse:Security>
            <wsse:UsernameToken>
                <wsse:Username>username</wsse:Username>
                <wsse:Password>password</wsse:Password>
            </wsse:UsernameToken>
        </wsse:Security>
   </soapenv:Header>

有人能帮助我吗?谢谢!


“wsse”看起来是WS-Security通常使用的前缀。此外,由于SOAP服务应该是自描述的,我找不到在WSDL中未定义头的有效原因。 - John Saunders
可以。但是像我说的,我不想去修改自动生成的类。我只是修改了一下以生成该类。 - LVS
2
自动生成的类是一个部分类。也许你可以将头文件添加到另一个类部分中。这样做不会影响自动生成的文件再次生成时的结果。 - John Saunders
3个回答

20
从概念上讲,WSDL不应该定义头信息。WSDL仅用于定义服务的功能方面,如操作、消息、绑定和端点。消息和绑定定义了消息有效载荷的编码和格式化方式。
然而,SOAP消息的头部不属于有效载荷。它们通常用于配置SOAP处理器的非功能属性。安全性就是这样一种非功能属性。有效载荷的功能方面不受影响。只有确保通信是安全的,并且WS工具栈而不是服务实现应该负责这一点。
因此,现在缺失的部分是一种标准,允许将一些非功能要求附加到WSDL服务中,以便代码生成器可以自动推导需要发送和/或理解哪些头文件以满足所需的非功能属性——无需手动处理头字段。这个标准存在,称为WS-Policy。策略通常包含一组替代方案,公开一组提供者和使用者都应该能够满足的要求。当两个服务打算相互交互时,会采取两个策略,并计算所谓的“有效策略”。它定义了共同的非功能要求。利用这些信息,提供者和使用者可以配置自己以添加所需的头文件,例如WS-Security头文件。WS-SecurityPolicy也定义了一组可用的策略。WS-PolicyAttachment定义了如何将这些策略附加到WSDL上。
有一些代码生成器可以处理WS-Policies,例如Metro或Axis2。

2
头部元素在wsdl/soap模式中已经定义,因此很难说它不应该被使用:http://schemas.xmlsoap.org/wsdl/soap/。您当然可以使用WS-SecurityPolicy,但它只是一个不同的、更具体的规范。 - b_levitt
3
同意,在绑定时,WSDL详细说明某些部分如何映射到SOAP头。然而,从概念上来讲,我认为使用这种方法是不好的实践,因为它把你的WSDL绑定到特定的绑定上。我同意,问题也被问到了特定的SOAP绑定,所以使用头部绑定可能是可以的,但是使用WS-Policy更可靠(即它表达了非功能性关注点“确保请求经过身份验证”,而不是发送具体的头部)。在前一种情况下,WS堆栈已经知道该怎么做了。 - vanto
尽管我仍然不认为这是“不良做法”(过时可能是更好的评估),但我现在确实看到你在这种情况下指向WS-SecurityPolicy是正确的,因为OP中的标头是WS-Security的标头。+1 - b_levitt

4
您可以通过在从wsdl生成的代理类中使用SoapHeader属性来向方法调用添加SOAP头信息。
例如,当您“添加Web引用”时,wsdl.exe将为Web服务引用生成客户端代理类Reference.cs。在上面提到的链接https://stage.totalcheck.sensis.com.au/service/webservice?wsdl中,有一个名为suggestAddress的消息,它将被翻译成在Visual Studio中添加Web引用时在生成的reference.cs客户端代理代码文件中的一个方法。默认情况下,当调用此方法时,SOAP信封中不会包含头信息。要向此请求的envelope中添加SoapHeader,请在Reference.cs生成的类中,即SuggestAddress方法的顶部添加[SoapHeader("Security")]属性,其中“Security”是继承自SoapHeader基类的类。
对于上述所需的安全SoapHeader示例,您需要创建以下类:
public partial class Security : SoapHeader
{
    public UserNameToken UserNameToken { get; set; }
}

public partial class UserNameToken
{
    public string UserName { get; set; }
    public string Password { get; set; }
}

那么你需要在 reference.cs 中像下面这样装饰 SuggestAddress 方法:

[SoapHeader("Security")]
public suggestAddressesResult suggestAddresses([System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified)] addressSearch search) {
        object[] results = this.Invoke("suggestAddresses", new object[] {search});
        return ((suggestAddressesResult)(results[0]));
    }

这将确保每当调用 suggestAddress 方法创建信封时,都会包含类似于上述安全头的安全头部。
<soapenv:Header>
    <wsse:Security>
        <wsse:UsernameToken>
            <wsse:Username>username</wsse:Username>
            <wsse:Password>password</wsse:Password>
        </wsse:UsernameToken>
    </wsse:Security>


suggestAddresses 是什么意思?我无法理解。在实现时,我遇到了错误:“SoapwbApp.withHeader”不包含“Invoke”的定义,也没有接受类型为“SoapwbApp.withHeader”的第一个参数的扩展方法“Invoke”可以找到(是否缺少使用指令或程序集引用?)。 - Sagotharan
这是哪种语言? - Marco

1

对我使用这个问题来帮助自己的关键是要认识到(正如一些人指出的那样),问题中的标头是WS-Security标准的标头。

如果您的代理生成工具是“自定义”的,那么似乎有逻辑可以添加用于WS-Security的标头的开关。但是,如果您使用的是WSDL.exe(Visual Studio中的“添加Web引用”),请考虑改用svcutil.exe(“添加服务引用”)。

如果你使用WCF代理,你可以覆盖给定的配置并允许WCF为你添加标头:

<security mode="TransportWithMessageCredential">
    <transport clientCredentialType="None" proxyCredentialType="None" realm="" />
    <message clientCredentialType="UserName" algorithmSuite="Default" />
</security>

从那里您可以指定密码:

RemoteSvcProxy.TheirClient client = new RemoteSvcProxy.TheirClient();
client.ClientCredentials.UserName.UserName = "uname";
client.ClientCredentials.UserName.Password = "pwd";

我不知道你的自定义工具是什么,但也许它所基于的框架也有类似的配置选项。


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