使用 WSE 2.0 从 WS-Routing 转移到 WS-Addressing
适用于:
Web Services Enhancements 2.0 for Microsoft.NET
WS-Routing 规范
WS-Addressing 规范
摘要:介绍 Web 服务路由遇到的问题和对传输中立寻址的需要,重点讨论了从 WS-Routing 向新的 WS-Addressing 规范的转移。介绍核心 WS-Addressing 概念以及使用 Web Service Enhancements 2.0 实现安全路由。(21 页打印页)
![*](http://www.microsoft.com/library/gallery/templates/MNP2.Common/images/3squares.gif)
本页内容
![]() | 简介 |
![]() | 传输中立性 |
![]() | 传输依赖性 |
![]() | WS-Routing 回顾 |
![]() | “下一个跃点”路由 |
![]() | 输入 WS-Addressing |
![]() | 终结点引用 |
![]() | 将终结点引用绑定到标头 |
![]() | WSE 2.0 中的 WS-Addressing |
![]() | 使用 WSE 2.0 实现路由 |
![]() | 使用 WSE 2.0 保护路由的安全 |
![]() | 我们所处的位置 |
![]() | 参考资料 |
![]() | 相关书籍 |
简介
本文介绍了 Web 服务路由遇到的问题和对传输中立寻址的需要,重点讨论了从 WS-Routing 向新的 WS-Addressing 规范的转移。为此,我们将讨论核心 WS-Addressing 概念,并介绍如何使用 Web Services Enhancements (WSE) 2.0 实现安全路由。
传输中立性
在深入讨论 WS-Addressing 和路由的具体细节之前,理解一个关键的 Web 服务概念是很重要的,这个概念激发了 SOAP 的设计和各种分层 WS-* 规范(包括 WS-Addressing)。这个概念就是“传输中立性”。
尽管大多数开发人员认为 Web 服务与 HTTP 具有天生的联系,但基本的体系结构以及更高级的层仍然被设计为是传输中立的。可以在 元素中看到这一点的证据,该元素提供了一个框架,从而将 XML 有效负载从伴随的标头中分离开来:
有效负载和标头之间 SOAP 的差异使我们能够在不创建对传输协议的依赖性的情况下捕获应用程序协议信息。只要发送方和接收方对于放在 SOAP 消息中的 XML 标头的理解是相同的,它们就可以通过各种不同的传输来交换 SOAP 消息。
传输依赖性
尽管重点介绍传输中立性,但如果没有传输协议的帮助,大多数现有的 SOAP 基础结构就不能提供用于引用终结点和调度消息的机制。
例如,使用 SOAP over HTTP 时,HTTP 请求消息的目标 URI 代表了 SOAP 终结点的地址。这是唯一引用终结地址的地方。一旦消息到达需要处理时,SOAP 基础结构就会使用 HTTP SOAPAction 标头来确定响应该消息所执行的操作。大多数 SOAP 实现使用该信息将消息调度给特殊的方法(参见图 1)。
![wsroutetowsadd_01.gif](https://i-blog.csdnimg.cn/blog_migrate/a75a32915e0aa45ec17c0240902abc10.png)
图 1 SOAP 地址和操作
将该信息放在传输协议中会使 SOAP 路由和调度与传输细节联系在一起,因此会向开发人员提出更多的要求。如果要通过不同的传输手段发送该消息,就必须找到一种用新的协议携带相同信息的方式,这样就不可能采用标准方式。另外,对于在其旅程的不同部分使用不同传输协议的消息来说(例如,一段路程使用 HTTP 而另一段路程使用电子邮件),将信息放在传输协议中是一种障碍。最后,在传输协议中,应用于 SOAP 消息的安全机制不会保护消息外部的信息的安全。
在 SOAP 标头部分携带该信息更加独立、可重用以及安全。这会使 SOAP 基础结构有可能适应范围更广的传输手段,同时简化编程模型。最后,删除传输依赖性将提高 SOAP 的总体简单性和灵活性。
WS-Routing 回顾
WS-Routing 是解决该问题的第一个规范。WS-Routing 使您能够以传输中立的方式指定消息路由和调度信息。请阅读 Routing SOAP Messages with Web Services Enhancements 1.0 以了解其他背景信息。
该规范定义了要在 SOAP 标头块中使用的新元素(名为 )。这个路径元素有几个子元素,它们可以用来指定路由和调度信息,包括 和 。这些元素可以用来捕获与以前相同的信息,但与传输协议没有任何关系,如图 2 所示。
![wsroutetowsadd_02.gif](https://i-blog.csdnimg.cn/blog_migrate/c27ffa33b3b6ac0ab7b2f75c3d0039e8.png)
图 2 WS-Routing 路径头
除了 和 ,WS-Routing 还定义了几个为更复杂的消息路由情形而设计的可选元素。代码清单 1 显示了 WS-Routing 所定义的 的完整结构(星号“*”意味着可以有多次重复)。
代码清单 1。WS-Routing“path”元素的结构
* * ...
这些元素允许消息描述它们自己的路由。消息可以定义前进路径 ( )、相应响应消息的相反路径 ( ) 以及使消息相互关联的方式 ( 和 )。旨在处理路径信息的路由器必须在将消息发送到路径中下一个节点之前对 WS-Routing 标头进行更改。例如,路由器必须删除第一个 元素并将消息转发给由下一个 元素所标识的节点。这个过程将继续,直到消息到达 元素所标识的节点。路由器还可能沿路生成返回路径。
尽管这种消息内路由功能提供了一些有趣的可能性,但它也带来一些严重的安全问题。路由器必须修改标头这一事实使消息签名复杂化。原始的发送方无法签署消息(包括 WS-Routing 标头)并在不破坏原始签名的情况下通过 WS-Routing 路径发送该消息。
加密引入了一组与路由有关的类似问题,因为路由器需要在再次发送消息之前读取、修改然后重新加密消息和标头。为了使传递继续,中间节点将需要得到读取、签名和重新加密消息所必需的安全令牌。这本身就有问题。
可以避免签署和加密 WS-Routing 标头,但这样会使您的系统容易受到攻击。这些安全性问题激发了一种更简单的基于 SOAP 路由的方法。
“下一个跃点”路由
一种简化 WS-Routing 并减少上述安全问题的简单方法是完全避免使用路径元素( 和 )。不使用这些元素也就不需要中间节点修改消息。
有人可能认为丢失消息内路径信息是一个大的牺牲,但其他人会争辩说无论如何这不是实现路由的正确方式。他们的争论基础是,发送方可以不必知道用来生成消息路径所需的体系结构详细信息。反之,发送方可以只需要知道一个终结点,这样就可以秘密的顾及其他路由需要。可以将这种方式当作“下一个跃点”路由。
“下一个跃点”路由本质上是 TCP/IP 的工作方式。TCP/IP 消息中包含有关消息来源和去向的信息,但不包含消息怎样到达的详细信息。当 TCP/IP 消息在网络上传输时,每个节点都会检查 TCP/IP 的标头以确定其目的地,然后该节点将消息发送到下一个与目标最靠近的 TCP/IP 节点。这个过程一直继续,直到节点到达其目的地。
这就解释了在设计 WS-Addressing 时的某些推理,而 WS-Addressing 则提供了安全的“下一个跃点”路由机制。
输入 WS-Addressing
作为 WS-Routing 的改进,WS-Addressing 为 Web 服务寻址提供了传输中立的机制。WS-Addressing 使上述 WS-Routing 的简化形式化,并且添加了少量其他功能,我们将在下面讨论这些功能。
WS-Addressing 正式放弃了与消息路径相关的 WS-Routing 元素(包括 、 和 ),并且假定用户将依靠“下一个跃点”方式来解决路由需要。WS-Addressing 定义的大多数元素在语义上等价于最初在 WS-Routing 中定义的元素。
表 1 描述了 WS-Addressing 消息的信息头,并显示了它们如何映射到其 WS-Routing 祖先。所有这些元素都与如下 URI 所标识的 WS-Addressing 命名空间关联:http://schemas.xmlsoap.org/ws/2003/03/addressing。
表 1。WS-Addressing 消息的信息头
名称 | WS-Routing 祖先 | 类型 | 说明 |
wsa:MessageID | r:path/r:id | xsd:anyURI | 用于在时间和空间上唯一标识该消息的 URI。 |
wsa:RelatesTo | r:path/r:relatesTo | xsd:anyURI | 一对值,说明该消息与另一个消息如何相关。关系类型用 QName 来标识。相关的消息由对应于相关消息 MessageID 值的 URI 来标识。答复消息必须包含 wsa:RelatesTo 标头,该标头由 wsa:Reply 和请求消息的消息 ID 属性组成。 |
wsa:To | r:path/r:to | xsd:anyURI | 该消息的预期接收方的地址。 |
wsa:Action | r:path/r:action | xsd:anyURI | 标识符,用于唯一地(和不透明地)标识该消息所暗示的语义。 |
wsa:From | r:path/r:from | endpoint-ref | 对作为消息来源的终结点的引用。 |
wsa:ReplyTo | N/A | endpoint-ref | 终结点引用,标识答复该消息的预期接收方。如果期望得到答复,则消息必须包含 wsa:ReplyTo 标头。发送方必须使用 wsa:ReplyTo 标头的内容来表示答复消息。如果没有 wsa:ReplyTo 标头,则可能使用 wsa:From 标头的内容来编制到来源的消息。如果消息无有意义的答复,则可能不存在该属性。如果有该属性,则 wsa:MessageID 标头是 REQUIRED。 |
wsa:FaultTo | N/A | endpoint-ref | 终结点引用,用于标识接收与该消息相关的错误的预期接收方。表示错误消息时,发送方必须使用该引用的内容来表示错误消息。如果没有该引用,发送方可能使用 wsa:ReplyTo 的内容来表示错误消息。如果该引用和 wsa:ReplyTo 都不存在,则发送方可能使用 wsa:From 的内容来表示错误消息。如果有该属性,则 [message id] 属性是 REQUIRED。 |
使用 指定消息的目标,使用 指定操作,并使用 为消息指定唯一的 ID。如果这是答复消息,则使用 标头和它的 RelationshipType 属性来表示该答复消息与请求消息之间的关系。
由于 WS-Addressing 避开了消息路径,所以它没有定义另一个类似 的容器元素(请参见阅 代码清单 1)。相反,WS-Addressing 元素只是作为 的直接子元素放置。图 3 说明了如何将 WS-Routing 信息映射到 WS-Addressing。
![wsroutetowsadd_03.gif](https://i-blog.csdnimg.cn/blog_migrate/29b0dcae18333d1e12285561927fc55f.png)
图 3 将 WS-Routing 映射到 WS-Addressing
使用 WS-Addressing 时,在请求消息中只有 和 是必需的(其他元素是可选的)。支持 WS-Addressing 的 SOAP 基础结构应当基于这两个值来调度消息。使用这两个值,还可以实现简单的“下一个跃点”路由机制。在中间路由节点,可以将 视为逻辑地址,并提供到物理地址的简单转换(稍后将有详细阐述)。
除了从 WS-Routing 延续下来的功能外,WS-Addressing 还以标准的、语义丰富的方式,为引用终结点引入了新的机制。这使得在消息中传递终结点引用(叫做“按引用传递”)成为可能,这样就可以告诉接收方未来的消息将发送到哪里。为此,WS-Addressing 引入了一个新的架构类型,称为“终结点引用”。
终结点引用
在 WS-Routing 中, 只是 URI。这使得在消息中传递发送方的地址成为可能。然后,接收方可以将响应消息发送回发送方地址。这种方式为在消息中传递终结点引用提供了有限的机制。但是,为了使用 所引用的服务,接收方必须事前知道各种服务的详细信息(例如,服务合约和策略)。
在 WS-Addressing 中, 元素的类型是 终结点引用。终结点引用使得将其他终结点元数据与地址一起传递成为可能。理想情况下,终结点引用将包含足够的信息,以便客户能够自动协商交互方式。
为了完成这个操作,终结点引用必须将地址与用来发现合约详细信息和策略的其他属性包含在一起。这些其他属性可能包括服务名称、端口名称、端口类型以及用来描述服务的要求、功能和首选项的 WS-Policy 语句。引用还应当能够包含所需数量的应用程序特有的属性。
WS-Addressing 规范定义了一个复杂的类型定义,该定义提供了终结点引用信息的结构。它的名称是 wsa:EndpointReferenceType(请参见代码清单 2)。
代码清单 2:WS-AddressingXML 架构定义
... ? ? ? ? ...
是 wsa:EndpointReferenceType 中唯一的必需元素。 元素基本上是其他自定义属性的容器(它包含一个 xsd:any 元素的序列)。 元素与 PortName 属性一起出现,尽管它没有在代码清单 2 中出现。 后面的 xsd:any 序列是可扩展点,其他元素可能放在这里。这是放置策略元素的位置。请参见表 2 以了解每个构造的完整说明。
表 2:EndpointReferenceType 元素
名称 | 说明 |
地址 | 用于标识终结点的地址 URI。它可能是网络地址或逻辑地址。 |
ReferenceProperties | 一个引用可能包含很多个用于标识正在传送的实体或资源的单个属性。 |
PortType | 正在传送的终结点的主 portType 的 QName。 |
ServiceName | 这是用来标识 WSDL 服务元素(该元素包含正在传送的终结点的定义)的 QName。服务名称提供到服务终结点全部说明的链接。可选的非限定名称标识了在对应于终结点的服务中的具体端口。 |
[policy] | XML 策略元素的可变数目,该数目在 WS-Policy 中有详细说明,WS-Policy 用于描述终结点的行为、要求和功能。策略可能包括在终结点中,这是为了让使用策略的应用程序更容易处理,或者因为策略是动态生成的。 |
WS-Addressing 架构还定义了名为 、类型为 wsa:EndpointReferenceType 的元素(请参见代码清单 2)。下面显示了完整的 示例:
http://claimserver/ins/p.asmx 123456 987654 c:ClaimsPortType c:ClaimsService ...
不必在所有情况下使用 。在架构中提供它是为了操作方便。可以定义类型为 wsa:EndpointReferenceType 的新元素。这正好解释了在 表 1 中最后三个消息信息标头( 、 和 )是如何定义的。
与前面一样, 是对最初发出消息的终结点的引用。当 不存在时,接收方应当向该终结点发送响应消息。请务必注意各种网络技术(例如,NAT、DHCP 和防火墙)使环境难以向发送消息的节点分配有意义的地址。WS-Addressing 定义了一个众所周知的 URI 来标识这些“匿名”端点:http://schemas.xmlsoap.org/ws/2003/03/addressing/role/anonymous。当应用程序使用该值时,它们必须提供某些其他严格遵守的机制来中继响应消息。
元素是对应当向其发送答复消息的终结点的引用,而 元素是对应当向其发送与消息有关的错误的终结点的引用。这意味着这些元素中的每一个都可以包含与上面显示的内容相似的结构。
代码清单 3 显示的示例使用了类型为 wsa:EndpointReferenceType 的一些消息标头。
代码清单 3:完整的 WS-Addressing 示例
http://skonnard.com/SubmitClaim http://skonnard.com/Claims/Submit.asmx http://skonnard.com/main/sub.asmx 123456 987654 http://skonnard.com/resp/resp.asmx 123456 987654 http://skonnard.com/fault/err.asmx 123456 987654 ...
可以看到,终结点引用可能包含很多内容,而不只是地址。额外的信息使节点能够以丰富的方式与其他终结点协商交互方式。
将终结点引用绑定到标头
当应用程序决定使用由终结点引用所描述的终结点时,它必须使用一个绑定将信息从终结点引用映射到合适的消息标头。
WS-Addressing 规范只为 SOAP 消息定义了一个绑定。SOAP 绑定声明:地址属性将映射到 标头,并且每个引用属性都成为独立的标头块,如图 4 所示。
![wsroutetowsadd_04.gif](https://i-blog.csdnimg.cn/blog_migrate/3ddf2a47528e4f180f8aebb5289c0493.png)
图 4 终结点引用 SOAP 绑定
WSE 2.0 中的 WS-Addressing
WSE 2.0 中的一个最基本的更改是放弃了对 WS-Routing 的支持,并添加了对 WS-Addressing 的支持。WSE 2.0 在 Microsoft.Web.Services.Addressing 命名空间中为 WS-Addressing 提供了多个新类。整个消息 API 已经重新设计为围绕终结点引用,而不是 URI。WSE 2.0 引入了一个名为 EndpointReference 的新类,这个类在整个 WSE 2.0 API 中替换了 System.Uri。
例如,SoapSender 和 SoapReceiver 类现在直接处理 EndpointReference 对象。下面的代码片段演示了在一个基于 TCP 的简单聊天应用程序中如何利用 SoapSender 类来使用 EndpointReference 对象:
...SoapEnvelope env = new SoapEnvelope();env.Context.Addressing.Action = String.Format("urn:chat:message");EndpointReference epr = new EndpointReference("soap.tcp://askonnard:123/aaron"));env.Context.Addressing.ReplyTo = new ReplyTo(epr);env.CreateBody(); // must create body before setting itenv.Body.InnerXml = String.Format( " {0} {1} ", user, msg);...EndpointReference epr = new EndpointReference( new Uri("soap.tcp://askonnard:456/monica"));SoapSender ss = new SoapSender(epr);ss.Send(env);...
一旦使用 WSE 2.0 发送消息,WSE 2.0 就会自动添加必需的 WS-Addressing 消息信息标头。例如,上面的代码片段将产生下面的 SOAP 消息:
urn:chat:message soap.tcp://askonnard:123/aaron uuid:59bc1ebb-40aa-4508-9a69-5b148d04d697 soap.tcp://askonnard:456/monica ... aaron hi
在上面的示例中,EndpointReference 对象只包含地址。可以用其他属性填充 EndPointReference,如下所示:
...EndpointReference epr = new EndpointReference( new Uri("soap.tcp://askonnard:456/monica"));epr.ServiceName = new ServiceName( new XmlQualifiedName("WseChat", "urn:chat"));epr.ServiceName.PortName = "ChatPort";epr.PortType = new PortType( new XmlQualifiedName("WseChatSoapPort", "urn:chat"));// set some custom reference properties...epr.ReferenceProperties = new ReferenceProperties();epr.ReferenceProperties.LoadXml(GetReferenceProperties());env.Context.Addressing.ReplyTo = new ReplyTo(epr);...
发送该 SoapEnvelope 时,WSE 基础结构将产生下面的 SOAP 消息:
urn:chat:message uuid:7e44dd92-ec1a-4b75-aa5d-8744698a5d39 soap.tcp://localhost:123/aaron Foo Bar prefix0:WseChatSoapPort prefix1:WseChat soap.tcp://askonnard:456/monica ... aaron hi
然后,在 SoapReceiver 实现的内部,可以使用 引用作为响应消息的目标:
public class ChatReceiver : SoapReceiver{ protected override void Receive(SoapEnvelope e) { // extract message fields from SOAP envelope SoapSender ss = new SoapSender(e.Context.Addressing.ReplyTo); ... // send response message }}
WSE 基础结构将自动应用 SOAP 终结点引用绑定,并将 信息映射到合适的消息标头。在该例中,响应消息如下所示:
urn:chat:message uuid:7e44dd92-ec1a-4b75-aa5d-8744698a5d40 Foo Bar soap.tcp://askonnard:123/aaron 7e44dd92-ec1a-4b75-aa5d-8744698a5d39 ... aaron hi
注意, 地址被映射到 标头,并且自定义引用属性成为独立的标头块( 和 )。
使用 WSE 2.0 实现路由
使用 WS-Addressing 和 WSE 2.0,可以有几种方式来实现“下一个跃点”路由。一种方式是提供一个从 地址转换到另一个地址的简单转换。WS-Referral 为这种类型的地址转换提供了一个机制。
下面的 WS-Referral 语句指出:目标为 http://skonnard.com/Claims/Submit.asmx 的消息应当路由到 http://claimserver/ins/sub.asmx 进行处理:
http://skonnard.com/Claims/Submit.asmx http://claimserver/ins/sub.asmx
WSE 2.0 通过 Microsoft.Web.Services.Messaging.SoapHttpRouter 处理程序来支持基于引用的路由。通过将下面的代码段添加到 web.config 文件中,可以将一个终结点配置为与引用缓存一起使用该处理程序(注意:考虑到可读性,代码添加了分行符):
将消息发送到该虚拟目录中的 .ashx 终结点时,SoapHttpRouter 将检查 WS-Referral 缓存,以确定下一个跃点的地址。 地址为 http://localhost/endpoints/echo.ashx 的消息将按照 WS-Referral 语句的指引被自动路由到 http://claimserver/ins/sub.asmx:
实现“下一个跃点”路由的另一个方式是编写自定义路由器类来检查消息的内容,以确定路由决定。这称为“基于内容的路由”。详细信息,请参阅 WSE 2.0 文档。
使用 WSE 2.0 保护路由的安全
既然 WSE 2.0 支持 WS-Addressing,那么,您可以安全地签署和加密 SOAP 消息,而不会面对 WS-Routing 所带来的问题。这是因为,WS-Addressing 标头是不可变的,中间节点无法修改它们。因此,您可以签署和加密消息(包括 WS-Addressing 标头)以达到您需要的、合适的安全级别。
下面的示例演示了如何使用 WSE 2.0 代理类签署整个 SOAP 消息:
...MyClaimsService e = new MyClaimsService();e.RequestSoapContext.Security.Tokens.Add(GetSecurityToken());e.RequestSoapContext.Security.Elements.Add( new Signature(tok));e.Submit(claim);...
可以使用前一节所描述的技术来路由该代码产生的签名的消息,但现在不会有安全问题了。强烈建议签署(必要时加密)WS-Addressing 标头,不这样做可能降低系统的安全性。
我们所处的位置
在本文中,我们讨论了 WS-Addressing 的必要性,以及从 WS-Routing 转移到 WS-Addressing 的理由。我们讨论了 WS-Addressing 如何使用标准的 WS-Security 技术实现安全的消息路由。WSE 2.0 为 WS-Addressing 和“下一个跃点”路由提供了完整的支持。