© 2005-2020 原作者。
本文档的副本可以供您自己使用和分发给其他人,前提是您不对此类副本收取任何费用,并且进一步前提是每份副本都包含本版权声明,无论是印刷版还是电子版。 |
前言
在当前面向服务架构的时代,越来越多的人使用 Web 服务来连接以前未连接的系统。最初,Web 服务被认为只是进行远程过程调用 (RPC) 的另一种方式。然而,随着时间的推移,人们发现 RPC 和 Web 服务之间存在很大差异。特别是当与其他平台的互操作性很重要时,通常最好发送包含处理请求所需的所有数据的封装 XML 文档。从概念上讲,与消息队列相比,基于 XML 的 Web 服务比远程解决方案更好。总的来说,XML 应该被认为是数据的平台中立表示,是 SOA 的通用语言。在开发或使用 Web 服务时,重点应该放在这个 XML 而不是 Java。
Spring Web Services 专注于创建这些文档驱动的 Web 服务。Spring Web Services 促进了契约优先的 SOAP 服务开发,允许通过使用多种方式中的一种来操作 XML 有效负载来创建灵活的 Web 服务。Spring-WS 提供了一个强大的消息调度框架,一个与您现有的应用程序安全解决方案集成的WS-Security解决方案,以及一个遵循熟悉的 Spring 模板模式的客户端 API 。
1.简介
1.什么是Spring Web Services?
1.1。介绍
Spring Web Services (Spring-WS) 是 Spring 社区的产品,专注于创建文档驱动的 Web 服务。Spring Web Services 旨在促进契约优先的 SOAP 服务开发,允许通过使用多种方式之一来操作 XML 有效负载来创建灵活的 Web 服务。该产品基于 Spring 本身,这意味着您可以使用 Spring 概念(例如依赖注入)作为 Web 服务的一个组成部分。
人们使用 Spring-WS 的原因有很多,但大多数人在发现在遵循 Web 服务最佳实践方面缺乏替代 SOAP 堆栈后被它所吸引。Spring-WS 使最佳实践成为一种简单的实践。这包括诸如 WS-I 基本概要、契约优先开发以及契约和实现之间的松散耦合等实践。Spring Web Services 的其他关键特性是:
1.1.3。灵活的 XML 编组
Spring Web Services 基于 Spring Framework 中的 Object/XML Mapping 模块构建,该模块支持 JAXB 1 和 2、Castor、XMLBeans、JiBX 和 XStream。
1.1.4。重用您的 Spring 专业知识
Spring-WS 使用 Spring 应用程序上下文进行所有配置,这应该有助于 Spring 开发人员快速上手。此外,Spring-WS 的架构类似于 Spring-MVC 的架构。
1.2. 运行环境
Spring Web Services 需要标准的 Java 8 运行时环境。Spring-WS 基于 Spring Framework 4.0.9 构建,但支持更高版本。
Spring-WS 由许多模块组成,这些模块将在本节的其余部分进行描述。
-
XML 模块 (
spring-xml.jar
) 包含 Spring Web 服务的各种 XML 支持类。该模块主要面向 Spring-WS 框架本身,而不是 Web 服务开发人员。 -
核心模块 (
spring-ws-core.jar
) 是 Spring Web 服务功能的核心部分。它提供了中心WebServiceMessage
和SoapMessage
接口、服务器端框架(具有强大的消息分发功能)、用于实现 Web 服务端点的各种支持类以及客户端WebServiceTemplate
. -
支持模块 (
spring-ws-support.jar
) 包含其他传输(JMS、电子邮件等)。 -
Security包 (
spring-ws-security.jar
) 提供与核心 Web 服务包集成的 WS-Security 实现。它允许您对 SOAP 消息进行签名、解密和加密以及添加主体令牌。此外,它允许您使用现有的 Spring Security 安全实现进行身份验证和授权。
下图显示了 Spring-WS 模块之间的依赖关系。箭头表示依赖关系(即 Spring-WS Core 依赖于 Spring-XML 和 Spring 3 及更高版本中的 OXM 模块)。
2. 为什么要先签约?
在创建 Web 服务时,有两种开发风格:契约后和契约优先。当您使用最后契约的方法时,您从 Java 代码开始,并从中生成 Web 服务契约(在 WSDL 中 — 见边栏)。使用契约优先时,您从 WSDL 契约开始,并使用 Java 来实现契约。
Spring-WS 仅支持契约优先的开发风格,本节将解释原因。
2.1。对象/XML 阻抗不匹配
类似于 ORM 领域,我们有一个Object/Relational 阻抗不匹配,将 Java 对象转换为 XML 也有类似的问题。乍一看,O/X 映射问题看起来很简单:为每个 Java 对象创建一个 XML 元素,以将所有 Java 属性和字段转换为子元素或属性。然而,事情并不像看起来那么简单,因为分层语言(例如 XML(尤其是 XSD))与 Java 的图形模型之间存在根本区别。
本节大部分内容的灵感来自[alpine]和[effective-enterprise-java]。 |
2.1.1。XSD 扩展
在 Java 中,改变类行为的唯一方法是将其子类化以将新行为添加到该子类。在 XSD 中,您可以通过限制数据类型来扩展数据类型,即约束元素和属性的有效值。例如,考虑以下示例:
<simpleType name="AirportCode">
<restriction base="string">
<pattern value="[A-Z][A-Z][A-Z]"/>
</restriction>
</simpleType>
此类型通过正则表达式限制 XSD 字符串,只允许三个大写字母。如果将此类型转换为 Java,我们最终会得到一个普通的java.lang.String
. 正则表达式在转换过程中丢失,因为 Java 不允许这些类型的扩展。
2.1.2. 不可移植的类型
Web 服务最重要的目标之一是可互操作:支持多种平台,例如 Java、.NET、Python 等。因为所有这些语言都有不同的类库,所以您必须使用一些通用的跨语言格式在它们之间进行通信。该格式是 XML,所有这些语言都支持它。
由于这种转换,您必须确保在服务实现中使用可移植类型。例如,考虑一个返回 a 的服务java.util.TreeMap
:
public Map getFlights() {
// use a tree map, to make sure it's sorted
TreeMap map = new TreeMap();
map.put("KL1117", "Stockholm");
...
return map;
}
毫无疑问,该地图的内容可以转换为某种 XML,但由于没有标准的方式来用 XML 描述地图,因此它将是专有的。此外,即使可以转换为 XML,许多平台也没有类似于TreeMap
. 因此,当 .NET 客户端访问您的 Web 服务时,它可能以System.Collections.Hashtable
具有不同语义的 .NET 结尾。
在客户端工作时也会出现此问题。考虑下面的 XSD 片段,它描述了一个服务契约:
<element name="GetFlightsRequest">
<complexType>
<all>
<element name="departureDate" type="date"/>
<element name="from" type="string"/>
<element name="to" type="string"/>
</all>
</complexType>
</element>
此协定定义了一个接受 的请求date
,它是一个表示年、月和日的 XSD 数据类型。如果我们从 Java 调用此服务,我们可能会使用 ajava.util.Date
或java.util.Calendar
. 然而,这两个类实际上描述的是时间,而不是日期。因此,我们实际上最终发送了代表 2007 年 4 月 4 日午夜 ( 2007-04-04T00:00:00
) 的数据,这与2007-04-04
.
2.1.3。循环图
假设我们有以下类结构:
public class Flight {
private String number;
private List<Passenger> passengers;
// getters and setters omitted
}
public class Passenger {
private String name;
private Flight flight;
// getters and setters omitted
}
这是一个循环图: theFlight
指的是 the Passenger
,它指的是 the Flight
again。像这样的循环图在 Java 中很常见。如果我们采取一种天真的方法将其转换为 XML,我们最终会得到如下结果:
<flight number="KL1117">
<passengers>
<passenger>
<name>Arjen Poutsma</name>
<flight number="KL1117">
<passengers>
<passenger>
<name>Arjen Poutsma</name>
<flight number="KL1117">
<passengers>
<passenger>
<name>Arjen Poutsma</name>
...
处理这样的结构很可能需要很长时间才能完成,因为这个循环没有停止条件。
解决此问题的一种方法是使用对已编组的对象的引用:
<flight number="KL1117">
<passengers>
<passenger>
<name>Arjen Poutsma</name>
<flight href="KL1117" />
</passenger>
...
</passengers>
</flight>
这解决了递归问题,但引入了新问题。一方面,您不能使用 XML 验证器来验证此结构。另一个问题是,在 SOAP(RPC/编码)中使用这些引用的标准方式已被弃用,取而代之的是 document/literal(请参阅 WS-I Basic Profile)。
这些只是处理 O/X 映射时的一些问题。在编写 Web 服务时尊重这些问题很重要。尊重它们的最佳方式是完全专注于 XML,同时使用 Java 作为实现语言。这就是合同优先的意义所在。
2.2. 合同优先与合同最后
除了上一节提到的对象/XML 映射问题之外,还有其他原因更喜欢契约优先的开发风格。
2.2.1。脆弱性
如前所述,契约后开发风格导致您的 Web 服务契约(WSDL 和 XSD)是从 Java 契约(通常是一个接口)生成的。如果您使用这种方法,您无法保证合同随着时间的推移保持不变。每次更改 Java 合约并重新部署它时,Web 服务合约都可能发生后续更改。
此外,并非所有 SOAP 堆栈都从 Java 合同生成相同的 Web 服务合同。这意味着将当前的 SOAP 堆栈更改为不同的堆栈(无论出于何种原因)也可能会更改您的 Web 服务合同。
当 Web 服务合同发生更改时,必须指示合同的用户获取新合同并可能更改他们的代码以适应合同中的任何更改。
为了使合约有用,它必须尽可能长时间地保持不变。如果合同发生变化,您必须联系您服务的所有用户并指示他们获取新版本的合同。
2.2.2。表现
当 Java 对象自动转换为 XML 时,无法确定通过网络发送的内容。一个对象可能引用另一个对象,该对象又引用另一个对象,依此类推。最后,虚拟机中堆上的一半对象可能会转换为 XML,从而导致响应时间变慢。
使用契约优先时,您明确地描述了将什么 XML 发送到哪里,从而确保它正是您想要的。
3. 编写契约优先的 Web 服务
本教程向您展示如何编写契约优先的 Web 服务 ,即如何开发首先以 XML Schema 或 WSDL 契约开始,然后是 Java 代码的 Web 服务。Spring-WS 专注于这种开发风格,本教程应该可以帮助您入门。请注意,本教程的第一部分几乎不包含 Spring-WS 特定信息。它主要是关于 XML、XSD 和 WSDL。第二部分侧重于使用 Spring-WS 实现这个契约。
在进行契约优先的 Web 服务开发时,最重要的事情是考虑 XML。这意味着 Java 语言概念的重要性较低。它是通过网络发送的 XML,您应该关注它。用于实现 Web 服务的 Java 是一个实现细节。
在本教程中,我们定义了一个由人力资源部门创建的 Web 服务。客户可以向此服务发送假期申请表来预订假期。
3.1。留言
在本节中,我们将重点关注发送到 Web 服务和从 Web 服务发送的实际 XML 消息。我们首先确定这些消息的外观。
3.1.1。假期
在场景中,我们必须处理假期请求,因此确定假期在 XML 中的样子是有意义的:
<Holiday xmlns="http://mycompany.com/hr/schemas">
<StartDate>2006-07-03</StartDate>
<EndDate>2006-07-07</EndDate>
</Holiday>
假期由开始日期和结束日期组成。我们还决定对日期使用标准的ISO 8601日期格式,因为这样可以节省大量的解析麻烦。我们还为元素添加了命名空间,以确保我们的元素可以在其他 XML 文档中使用。
3.1.2。员工
场景中还有员工的概念。这是它在 XML 中的样子:
<Employee xmlns="http://mycompany.com/hr/schemas">
<Number>42</Number>
<FirstName>Arjen</FirstName>
<LastName>Poutsma</LastName>
</Employee>
我们使用了与以前相同的命名空间。如果此<Employee/>
元素可以在其他场景中使用,那么使用不同的命名空间可能是有意义的,例如http://example.com/employees/schemas
.
3.1.3. 假期申请
holiday
element 和element都employee
可以放在 a 中<HolidayRequest/>
:
<HolidayRequest xmlns="http://mycompany.com/hr/schemas">
<Holiday>
<StartDate>2006-07-03</StartDate>
<EndDate>2006-07-07</EndDate>
</Holiday>
<Employee>
<Number>42</Number>
<FirstName>Arjen</FirstName>
<LastName>Poutsma</LastName>
</Employee>
</HolidayRequest>
这两个元素的顺序无关紧要:<Employee/>
可能是第一个元素。重要的是所有数据都在那里。事实上,数据是唯一重要的东西:我们采用数据驱动的方法。
3.2. 数据合约
现在我们已经看到了一些我们可以使用的 XML 数据示例,将其形式化为模式是有意义的。该数据合同定义了我们接受的消息格式。有四种不同的方式可以为 XML 定义这样的契约:
-
DTD
DTD 对命名空间的支持有限,因此它们不适用于 Web 服务。Relax NG 和 Schematron 比 XML Schema 更容易。不幸的是,它们并没有得到跨平台的广泛支持。因此,我们使用 XML Schema。
到目前为止,创建 XSD 的最简单方法是从示例文档中推断出它。任何优秀的 XML 编辑器或 Java IDE 都提供此功能。基本上,这些工具使用一些示例 XML 文档来生成验证它们的模式。最终结果当然需要完善,但这是一个很好的起点。
使用前面描述的示例,我们最终得到以下生成的模式:
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified"
targetNamespace="http://mycompany.com/hr/schemas"
xmlns:hr="http://mycompany.com/hr/schemas">
<xs:element name="HolidayRequest">
<xs:complexType>
<xs:sequence>
<xs:element ref="hr:Holiday"/>
<xs:element ref="hr:Employee"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Holiday">
<xs:complexType>
<xs:sequence>
<xs:element ref="hr:StartDate"/>
<xs:element ref="hr:EndDate"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="StartDate" type="xs:NMTOKEN"/>
<xs:element name="EndDate" type="xs:NMTOKEN"/>
<xs:element name="Employee">
<xs:complexType>
<xs:sequence>
<xs:element ref="hr:Number"/>
<xs:element ref="hr:FirstName"/>
<xs:element ref="hr:LastName"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Number" type="xs:integer"/>
<xs:element name="FirstName" type="xs:NCName"/>
<xs:element name="LastName" type="xs:NCName"/>
</xs:schema>
可以改进此生成的模式。首先要注意的是每种类型都有一个根级元素声明。这意味着 Web 服务应该能够接受所有这些元素作为数据。这是不可取的:我们只想接受一个<HolidayRequest/>
. 通过删除包装元素标签(从而保留类型)并内联结果,我们可以做到这一点,如下所示:
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:hr="http://mycompany.com/hr/schemas"
elementFormDefault="qualified"
targetNamespace="http://mycompany.com/hr/schemas">
<xs:element name="HolidayRequest">
<xs:complexType>
<xs:sequence>
<xs:element name="Holiday" type="hr:HolidayType"/>
<xs:element name="Employee" type="hr:EmployeeType"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType name="HolidayType">
<xs:sequence>
<xs:element name="StartDate" type="xs:NMTOKEN"/>
<xs:element name="EndDate" type="xs:NMTOKEN"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="EmployeeType">
<xs:sequence>
<xs:element name="Number" type="xs:integer"/>
<xs:element name="FirstName" type="xs:NCName"/>
<xs:element name="LastName" type="xs:NCName"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
该模式仍然存在一个问题:使用这样的模式,您可以期望以下消息得到验证:
<HolidayRequest xmlns="http://mycompany.com/hr/schemas">
<Holiday>
<StartDate>this is not a date</StartDate>
<EndDate>neither is this</EndDate>
</Holiday>
PlainText Section qName:lineannotation level:4, chunks:[<, !-- ... --, >] attrs:[:]
</HolidayRequest>
显然,我们必须确保开始日期和结束日期是真正的日期。XML Schema 有一个我们可以使用的优秀的内置date
类型。我们还将NCName
s 更改为string
实例。最后,我们将sequence
in更改<HolidayRequest/>
为all
. 这告诉 XML 解析器 和 的顺序<Holiday/>
不重要<Employee/>
。我们最终的 XSD 现在看起来像下面的清单:
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:hr="http://mycompany.com/hr/schemas"
elementFormDefault="qualified"
targetNamespace="http://mycompany.com/hr/schemas">
<xs:element name="HolidayRequest">
<xs:complexType>
<xs:all>
<xs:element name="Holiday" type="hr:HolidayType"/> (1)
<xs:element name="Employee" type="hr:EmployeeType"/> (1)
</xs:all>
</xs:complexType>
</xs:element>
<xs:complexType name="HolidayType">
<xs:sequence>
<xs:element name="StartDate" type="xs:date"/> (2)
<xs:element name="EndDate" type="xs:date"/> (2)
</xs:sequence>
</xs:complexType>
<xs:complexType name="EmployeeType">
<xs:sequence>
<xs:element name="Number" type="xs:integer"/>
<xs:element name="FirstName" type="xs:string"/> (3)
<xs:element name="LastName" type="xs:string"/> (3)
</xs:sequence>
</xs:complexType>
</xs:schema>
1 | all 告诉 XML 解析器 和 的顺序<Holiday/> 不重要<Employee/> 。 |
2 | 我们使用xs:date 数据类型(包括年、月和日)<StartDate/> 和<EndDate/> 。 |
3 | xs:string 用于名字和姓氏。 |
我们将此文件存储为hr.xsd
.
3.3. 服务合同
服务契约通常表示为WSDL文件。请注意,在 Spring-WS 中,不需要手动编写 WSDL。基于 XSD 和一些约定,Spring-WS 可以为您创建 WSDL,如“实现端点”部分所述。本节的其余部分展示了如何手动编写 WSDL。您可能想跳到下一节。
我们从标准序言开始我们的 WSDL,并通过导入我们现有的 XSD。为了将模式与定义分开,我们为 WSDL 定义使用单独的命名空间:http://mycompany.com/hr/definitions
. 以下清单显示了序言:
<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:schema="http://mycompany.com/hr/schemas"
xmlns:tns="http://mycompany.com/hr/definitions"
targetNamespace="http://mycompany.com/hr/definitions">
<wsdl:types>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:import namespace="http://mycompany.com/hr/schemas" schemaLocation="hr.xsd"/>
</xsd:schema>
</wsdl:types>
接下来,我们根据编写的模式类型添加我们的消息。我们只有一条消息,<HolidayRequest/>
我们将其放入架构中:
<wsdl:message name="HolidayRequest">
<wsdl:part element="schema:HolidayRequest" name="HolidayRequest"/>
</wsdl:message>
我们将消息添加到端口类型作为操作:
<wsdl:portType name="HumanResource">
<wsdl:operation name="Holiday">
<wsdl:input message="tns:HolidayRequest" name="HolidayRequest"/>
</wsdl:operation>
</wsdl:portType>
该消息完成了 WSDL 的抽象部分(可以说是接口)并留下具体部分。具体部分由a binding
(告诉客户端如何调用您刚刚定义的操作)和a service
(告诉客户端在哪里调用它)组成。
添加一个具体的部分是非常标准的。为此,请参考您之前定义的抽象部分,确保您使用document/literal
元素soap:binding
(rpc/encoded
不推荐使用),soapAction
为操作选择一个(在这种情况下http://mycompany.com/RequestHoliday
,,但任何 URI 都可以),并确定location
您想要的 URL请求到达(在这种情况下,http://mycompany.com/humanresources
):
<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:schema="http://mycompany.com/hr/schemas"
xmlns:tns="http://mycompany.com/hr/definitions"
targetNamespace="http://mycompany.com/hr/definitions">
<wsdl:types>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:import namespace="http://mycompany.com/hr/schemas" (1)
schemaLocation="hr.xsd"/>
</xsd:schema>
</wsdl:types>
<wsdl:message name="HolidayRequest"> (2)
<wsdl:part element="schema:HolidayRequest" name="HolidayRequest"/> (3)
</wsdl:message>
<wsdl:portType name="HumanResource"> (4)
<wsdl:operation name="Holiday">
<wsdl:input message="tns:HolidayRequest" name="HolidayRequest"/> (2)
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="HumanResourceBinding" type="tns:HumanResource"> (4)(5)
<soap:binding style="document" (6)
transport="http://schemas.xmlsoap.org/soap/http"/> (7)
<wsdl:operation name="Holiday">
<soap:operation soapAction="http://mycompany.com/RequestHoliday"/> (8)
<wsdl:input name="HolidayRequest">
<soap:body use="literal"/> (6)
</wsdl:input>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="HumanResourceService">
<wsdl:port binding="tns:HumanResourceBinding" name="HumanResourcePort"> (5)
<soap:address location="http://localhost:8080/holidayService/"/> (9)
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
1 | 我们导入Data Contract中定义的模式。 |
2 | 我们定义了HolidayRequest 消息,它在portType . |
3 | HolidayRequest 类型在模式中定义。 |
4 | 我们定义HumanResource 端口类型,在binding . |
5 | 我们定义了HumanResourceBinding 绑定,它在port . |
6 | 我们使用文档/文字样式。 |
7 | 字面http://schemas.xmlsoap.org/soap/http 量表示 HTTP 传输。 |
8 | 该soapAction 属性表示SOAPAction 将随每个请求发送的 HTTP 标头。 |
9 | http://localhost:8080/holidayService/ 地址是可以调用 Web 服务的 URL 。 |
前面的清单显示了最终的 WSDL。我们将在下一节中描述如何实现生成的模式和 WSDL。
3.4. 创建项目
在本节中,我们使用Maven为我们创建初始项目结构。这样做不是必需的,但可以大大减少我们为设置 HolidayService 而编写的代码量。
以下命令使用 Spring-WS 原型(即项目模板)为我们创建一个 Maven Web 应用程序项目:
mvn 原型:创建 -DarchetypeGroupId=org.springframework.ws \ -DarchetypeArtifactId=spring-ws-archetype \ -DarchetypeVersion=\ -DgroupId=com.mycompany.hr\ -DartifactId=holidayService
前面的命令创建了一个名为holidayService
. 此目录中有一个src/main/webapp
目录,其中包含 WAR 文件的根目录。您可以在此处找到标准 Web 应用程序部署描述符 ( 'WEB-INF/web.xml'
),它定义了 Spring-WSMessageDispatcherServlet
并将所有传入请求映射到此 servlet:
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">
<display-name>MyCompany HR Holiday Service</display-name>
<!-- take special notice of the name of this servlet -->
<servlet>
<servlet-name>spring-ws</servlet-name>
<servlet-class>org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>spring-ws</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
除了前面的WEB-INF/web.xml
文件,您还需要另一个 Spring-WS 特定的配置文件,名为WEB-INF/spring-ws-servlet.xml
. 该文件包含所有 Spring-WS 特定的 bean,例如EndPoints
和WebServiceMessageReceivers
,用于创建新的 Spring 容器。此文件的名称源自附加 servlet 的名称(在本例中'spring-ws'
)-servlet.xml
。因此,如果您MessageDispatcherServlet
使用 name定义 a 'dynamite'
,则 Spring-WS 特定配置文件的名称将变为WEB-INF/dynamite-servlet.xml
.
(您可以在[tutorial.example.sws-conf-file]WEB-INF/spring-ws-servlet.xml
中查看此示例的文件内容。)
创建项目结构后,您可以将上一节中的模式和 WSDL 放入'WEB-INF/'
文件夹中。
3.5. 实现端点
在 Spring-WS 中,您实现端点来处理传入的 XML 消息。端点通常是通过使用注释对类进行@Endpoint
注释来创建的。在此端点类中,您可以创建一个或多个处理传入请求的方法。方法签名可以非常灵活。正如我们在本章后面解释的那样,您几乎可以包含与传入 XML 消息相关的任何类型的参数类型。
3.5.1。处理 XML 消息
以下清单显示了定义我们的假期端点的类:
package com.mycompany.hr.ws;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
import org.springframework.ws.server.endpoint.annotation.RequestPayload;
import com.mycompany.hr.service.HumanResourceService;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.Namespace;
import org.jdom2.filter.Filters;
import org.jdom2.xpath.XPathExpression;
import org.jdom2.xpath.XPathFactory;
@Endpoint (1)
public class HolidayEndpoint {
private static final String NAMESPACE_URI = "http://mycompany.com/hr/schemas";
private XPathExpression<Element> startDateExpression;
private XPathExpression<Element> endDateExpression;
private XPathExpression<Element> firstNameExpression;
private XPathExpression<Element> lastNameExpression;
private HumanResourceService humanResourceService;
@Autowired (2)
public HolidayEndpoint(HumanResourceService humanResourceService) throws JDOMException {
this.humanResourceService = humanResourceService;
Namespace namespace = Namespace.getNamespace("hr", NAMESPACE_URI);
XPathFactory xPathFactory = XPathFactory.instance();
startDateExpression = xPathFactory.compile("//hr:StartDate", Filters.element(), null, namespace);
endDateExpression = xPathFactory.compile("//hr:EndDate", Filters.element(), null, namespace);
firstNameExpression = xPathFactory.compile("//hr:FirstName", Filters.element(), null, namespace);
lastNameExpression = xPathFactory.compile("//hr:LastName", Filters.element(), null, namespace);
}
@PayloadRoot(namespace = NAMESPACE_URI, localPart = "HolidayRequest") (3)
public void handleHolidayRequest(@RequestPayload Element holidayRequest) throws Exception {(4)
Date startDate = parseDate(startDateExpression, holidayRequest);
Date endDate = parseDate(endDateExpression, holidayRequest);
String name = firstNameExpression.evaluateFirst(holidayRequest).getText() + " " + lastNameExpression.evaluateFirst(holidayRequest).getText();
humanResourceService.bookHoliday(startDate, endDate, name);
}
private Date parseDate(XPathExpression<Element> expression, Element element) throws ParseException {
Element result = expression.evaluateFirst(element);
if (result != null) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
return dateFormat.parse(result.getText());
} else {
throw new IllegalArgumentException("Could not evaluate [" + expression + "] on [" + element + "]");
}
}
}
1 | 用HolidayEndpoint 注释@Endpoint 。这标志着该类是一种特殊的类@Component ,适合在 Spring-WS 中处理 XML 消息,也使其适合于组件扫描。 |
2 | HolidayEndpoint 需要业务服务才能运行,所以我们在HumanResourceService 构造函数中注入依赖并用@Autowired . 接下来,我们使用 JDOM2 API 设置 XPath 表达式。有四种表达式://hr:StartDate 用于提取<StartDate> 文本值,//hr:EndDate 用于提取结束日期,以及用于提取员工姓名的两种。 |
3 | @PayloadRoot 注解告诉 Spring-WS 该方法handleHolidayRequest 适用于处理 XML 消息。此方法可以处理的消息类型由注释值指示。在这种情况下,它可以处理具有HolidayRequest 本地部分和http://mycompany.com/hr/schemas 名称空间的 XML 元素。下一节将提供有关将消息映射到端点的更多信息。 |
4 | 该handleHolidayRequest(..) 方法是主要的处理方法,它<HolidayRequest/>
从传入的 XML 消息中获取元素。@RequestPayload 注解表示该参数holidayRequest 应该映射到请求消息的有效负载。我们使用 XPath 表达式从 XML 消息中提取字符串值,并Date 使用 a
SimpleDateFormat (parseData 方法)将这些值转换为对象。使用这些值,我们调用业务服务上的方法。通常,这会导致启动数据库事务并更改数据库中的某些记录。最后,我们定义一个void 返回类型,向 Spring-WS 指示我们不想发送响应消息。如果我们想要响应消息,我们可以返回一个 JDOM 元素来表示响应消息的负载。 |
使用 JDOM 只是处理 XML 的选项之一。其他选项包括 DOM、dom4j、XOM、SAX 和 StAX,还包括编组技术,如 JAXB、Castor、XMLBeans、JiBX 和 XStream,下一章将对此进行说明。我们选择 JDOM 是因为它使我们能够访问原始 XML,并且因为它基于类(而不是 W3C DOM 和 dom4j 那样的接口和工厂方法),这使得代码不那么冗长。我们使用 XPath 是因为它没有编组技术那么脆弱。只要我们能找到日期和名称,我们就不需要严格的模式一致性。
因为我们使用 JDOM,所以我们必须pom.xml
在项目目录的根目录下的 Maven 中添加一些依赖项。这是POM的相关部分:
<dependencies>
<dependency>
<groupId>org.springframework.ws</groupId>
<artifactId>spring-ws-core</artifactId>
<version></version>
</dependency>
<dependency>
<groupId>jdom</groupId>
<artifactId>jdom</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
<version>1.1</version>
</dependency>
</dependencies>
下面是我们如何spring-ws-servlet.xml
使用组件扫描在 Spring XML 配置文件中配置这些类。我们还指示 Spring-WS 将注释驱动的端点与<sws:annotation-driven>
元素一起使用。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:sws="http://www.springframework.org/schema/web-services"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/web-services http://www.springframework.org/schema/web-services/web-services-2.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:component-scan base-package="com.mycompany.hr"/>
<sws:annotation-driven/>
</beans>
3.5.2. 将消息路由到端点
作为编写端点的一部分,我们还使用@PayloadRoot
注释来指示该方法可以处理哪种类型的消息handleHolidayRequest
。在 Spring-WS 中,这个过程是由EndpointMapping
. 在这里,我们使用PayloadRootAnnotationMethodEndpointMapping
. 以下清单显示了我们之前使用的注释:
@PayloadRoot(namespace = "http://mycompany.com/hr/schemas", localPart = "HolidayRequest")
前面示例中显示的注释基本上意味着每当接收到带有名称空间http://mycompany.com/hr/schemas
和HolidayRequest
本地名称的 XML 消息时,它都会被路由到handleHolidayRequest
方法。通过<sws:annotation-driven>
在我们的配置中使用该元素,我们可以检测@PayloadRoot
注释。在一个端点中有多个相关的处理方法是可能的(并且很常见),每个方法处理不同的 XML 消息。
还有其他方法可以将端点映射到 XML 消息,这将在下一章中介绍。
3.5.3. 提供服务和存根实现
现在我们有了端点,我们需要HumanResourceService
它的实现以供HolidayEndpoint
. 以下清单显示了该HumanResourceService
界面:
package com.mycompany.hr.service;
import java.util.Date;
public interface HumanResourceService {
void bookHoliday(Date startDate, Date endDate, String name);
}
出于教程目的,我们使用一个简单的存根实现HumanResourceService
:
package com.mycompany.hr.service;
import java.util.Date;
import org.springframework.stereotype.Service;
@Service (1)
public class StubHumanResourceService implements HumanResourceService {
public void bookHoliday(Date startDate, Date endDate, String name) {
System.out.println("Booking holiday for [" + startDate + "-" + endDate + "] for [" + name + "] ");
}
}
1 | 用StubHumanResourceService 注释@Service 。@Autowired 这将类标记为业务门面,这使得它成为in注入的候选对象HolidayEndpoint 。 |
3.6. 发布 WSDL
最后,我们需要发布 WSDL。如Service Contract中所述,我们不需要自己编写 WSDL。Spring-WS 可以根据一些约定生成一个。以下是我们如何定义世代:
<sws:dynamic-wsdl id="holiday" (1)
portTypeName="HumanResource" (3)
locationUri="/holidayService/" (4)
targetNamespace="http://mycompany.com/hr/definitions"> (5)
<sws:xsd location="/WEB-INF/hr.xsd"/> (2)
</sws:dynamic-wsdl>
1 | id 确定可以检索 WSDL 的 URL 。在这种情况下,id is holiday ,这意味着可以holiday.wsdl 在 servlet 上下文中检索 WSDL。完整的 URL 是http://localhost:8080/holidayService/holiday.wsdl 。 |
2 | 接下来,我们将 WSDL 端口类型设置为HumanResource . |
3 | 我们设置服务可以到达的位置:/holidayService/ . 我们使用相对 URI,并指示框架将其动态转换为绝对 URI。因此,如果服务部署到不同的上下文,我们不必手动更改 URI。有关更多信息,请参阅名为“自动 WSDL 公开”的部分。为了使位置转换起作用,我们需要在spring-ws
servlet 中添加一个 init 参数web.xml (如下面的清单所示)。 |
4 | 我们为 WSDL 定义本身定义目标名称空间。不需要设置此属性。如果未设置,则 WSDL 具有与 XSD 模式相同的名称空间。 |
5 | 该xsd 元素指的是我们在Data Contract中定义的人力资源模式。我们将模式放在WEB-INF 应用程序的目录中。 |
以下清单显示了如何添加 init 参数:
<init-param>
<param-name>transformWsdlLocations</param-name>
<param-value>true</param-value>
</init-param>
您可以使用mvn install
. 如果您部署应用程序(到 Tomcat、Jetty 等)并将浏览器指向此位置,您会看到生成的 WSDL。此 WSDL 可供客户端使用,例如soapUI或其他 SOAP 框架。
本教程到此结束。教程代码可以在 Spring-WS 的完整发行版中找到。如果您希望继续,请查看作为分发的一部分的 echo 示例应用程序。之后,看一下航空公司示例,它有点复杂,因为它使用 JAXB、WS-Security、Hibernate 和事务服务层。最后,您可以阅读其余的参考文档。
2.参考
4. 共享组件
本章探讨在客户端和服务器端 Spring-WS 开发之间共享的组件。这些接口和类代表了 Spring-WS 的构建块,因此您需要了解它们的作用,即使您不直接使用它们。
4.1。Web 服务消息
本节介绍 Spring-WS 使用的消息和消息工厂。
4.1.1。WebServiceMessage
Spring Web Services 的核心接口之一是WebServiceMessage
. 此接口表示与协议无关的 XML 消息。javax.xml.transform.Source
该接口包含以 a或 a的形式提供对消息有效负载的访问的方法javax.xml.transform.Result
。Source
并且Result
是标记接口,表示对 XML 输入和输出的抽象。具体实现包装了各种 XML 表示,如下表所示:
源或结果实现 | 包装的 XML 表示 |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
除了读取和写入有效负载之外,Web 服务消息还可以将自身写入输出流。
4.1.2.SoapMessage
SoapMessage
是 的子类WebServiceMessage
。它包含特定于 SOAP 的方法,例如获取 SOAP 标头、SOAP 错误等。一般来说,你的代码不应该依赖于,因为 SOAP Body 的内容(消息的SoapMessage
负载)可以通过在. 只有在需要执行特定于 SOAP 的操作(例如添加标头、获取附件等)时,您才需要强制转换为.getPayloadSource()
getPayloadResult()
WebServiceMessage
WebServiceMessage
SoapMessage
4.1.3。消息工厂
具体的消息实现由WebServiceMessageFactory
. 该工厂可以创建空消息或从输入流中读取消息。有两种具体的实现WebServiceMessageFactory
。一种是基于 SAAJ,即用于 Java 的带有附件 API 的 SOAP。另一种是基于 Axis 2 的 AXIOM(AXis 对象模型)。
SaajSoapMessageFactory
使用SaajSoapMessageFactory
SOAP with Attachments API for Java (SAAJ) 创建SoapMessage
实现。SAAJ 是 J2EE 1.4 的一部分,因此大多数现代应用程序服务器都应该支持它。以下是常见应用程序服务器提供的 SAAJ 版本的概述:
应用服务器 | SAAJ版 |
---|---|
东亚网络逻辑 8 |
1.1 |
东亚网络逻辑 9 |
1.1/1.2 1 |
IBM WebSphere 6 |
1.2 |
太阳玻璃鱼 1 |
1.3 |
1 Weblogic 9 在 SAAJ 1.2 实现中存在一个已知错误:它实现了所有 1.2 接口,但 |
此外,Java SE 6 包括 SAAJ 1.3。您可以SaajSoapMessageFactory
按如下方式连接:
<bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory" />
SAAJ 基于 DOM,即文档对象模型。这意味着所有 SOAP 消息都存储在内存中。对于较大的 SOAP 消息,这可能无法执行。在这种情况下,AxiomSoapMessageFactory 可能更适用。
|
AxiomSoapMessageFactory
使用AxiomSoapMessageFactory
AXis 2 对象模型 (AXIOM) 创建SoapMessage
实现。AXIOM 基于 StAX,即 XML 流 API。StAX 提供了一种基于拉取的机制来读取 XML 消息,这对于较大的消息可能更有效。
要提高 的读取性能AxiomSoapMessageFactory
,您可以将该payloadCaching
属性设置为 false(默认为 true)。这样做会导致直接从套接字流中读取 SOAP 主体的内容。启用此设置后,有效负载只能读取一次。这意味着您必须确保消息的任何预处理(日志记录或其他工作)都不会消耗它。
您可以使用AxiomSoapMessageFactory
如下:
<bean id="messageFactory" class="org.springframework.ws.soap.axiom.AxiomSoapMessageFactory">
<property name="payloadCaching" value="true"/>
</bean>
除了有效负载缓存之外,AXIOM 还支持完整的流式消息,如StreamingWebServiceMessage
. 这意味着您可以直接在响应消息上设置有效负载,而不是将其写入 DOM 树或缓冲区。
当处理程序方法返回 JAXB2 支持的对象时,将使用 AXIOM 的完整流。它会自动将此编组的对象设置到响应消息中,并在响应发出时将其写入传出套接字流。
有关完整流的更多信息,请参阅 和 的类级StreamingWebServiceMessage
Javadoc StreamingPayload
。
SOAP 1.1 或 1.2
theSaajSoapMessageFactory
和 theAxiomSoapMessageFactory
都有一个soapVersion
属性,您可以在其中注入一个SoapVersion
常量。默认情况下,版本为 1.1,但您可以将其设置为 1.2:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-2.0.xsd">
<bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory">
<property name="soapVersion">
<util:constant static-field="org.springframework.ws.soap.SoapVersion.SOAP_12"/>
</property>
</bean>
</beans>
在前面的示例中,我们定义了SaajSoapMessageFactory
只接受 SOAP 1.2 消息的 a。
尽管 SOAP 的两个版本在格式上非常相似,但 1.2 版本并不向后兼容 1.1,因为它使用不同的 XML 名称空间。 SOAP 版本号(或一般的 WS-* 规范版本号)需要注意的一件重要事情是,最新版本的规范通常不是最流行的版本。对于 SOAP,这意味着(当前)最好使用的版本是 1.1。1.2 版将来可能会变得更流行,但 1.1 版目前是最安全的选择。 |
4.1.4。MessageContext
通常,消息成对出现:请求和响应。在客户端创建一个请求,该请求通过某种传输方式发送到服务器端,并在服务器端生成响应。这个响应被发送回客户端,在那里被读取。
在 Spring Web Services 中,这样的对话包含在 a 中MessageContext
,它具有获取请求和响应消息的属性。在客户端,消息上下文由WebServiceTemplate
. 在服务器端,从特定于传输的输入流中读取消息上下文。例如,在 HTTP 中,它是从 读取的HttpServletRequest
,并且响应被写回到HttpServletResponse
.
4.2.TransportContext
SOAP 协议的关键属性之一是它试图与传输无关。这就是为什么,例如,Spring-WS 不支持通过 HTTP 请求 URL 而是通过消息内容将消息映射到端点。
但是,有时需要在客户端或服务器端访问底层传输。为此,Spring Web 服务具有TransportContext
. 传输上下文允许访问底层的WebServiceConnection
,通常是HttpServletConnection
服务器端的 aHttpUrlConnection
或CommonsHttpConnection
客户端的 a 。例如,您可以在服务器端端点或拦截器中获取当前请求的 IP 地址:
TransportContext context = TransportContextHolder.getTransportContext();
HttpServletConnection connection = (HttpServletConnection )context.getConnection();
HttpServletRequest request = connection.getHttpServletRequest();
String ipAddress = request.getRemoteAddr();
4.3. 使用 XPath 处理 XML
处理 XML 的最佳方法之一是使用 XPath。引用[effective-xml],第 35 项:
XPath 是第四代声明性语言,它允许您指定要处理的节点,而无需准确指定处理器应该如何导航到这些节点。XPath 的数据模型设计得非常好,可以准确地支持几乎所有开发人员希望从 XML 中得到的东西。例如,它合并所有相邻文本,包括 CDATA 部分中的文本,允许计算跳过注释和处理指令的值,并包括来自子元素和后代元素的文本,并要求解析所有外部实体引用。在实践中,XPath 表达式往往对输入文档中的意外但可能微不足道的更改更加健壮。
Spring Web Services 有两种在应用程序中使用 XPath 的方法:更快XPathExpression
或更灵活XPathTemplate
。
4.3.1。XPathExpression
这XPathExpression
是对已编译 XPath 表达式(例如 Java 5javax.xml.xpath.XPathExpression
接口或 JaxenXPath
类)的抽象。要在应用程序上下文中构造表达式,您可以使用XPathExpressionFactoryBean
. 以下示例使用此工厂 bean:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<bean id="nameExpression" class="org.springframework.xml.xpath.XPathExpressionFactoryBean">
<property name="expression" value="/Contacts/Contact/Name"/>
</bean>
<bean id="myEndpoint" class="sample.MyXPathClass">
<constructor-arg ref="nameExpression"/>
</bean>
</beans>
前面的表达式没有使用命名空间,但我们可以使用namespaces
工厂 bean 的属性来设置它们。该表达式可以在代码中使用如下:
package sample;
public class MyXPathClass {
private final XPathExpression nameExpression;
public MyXPathClass(XPathExpression nameExpression) {
this.nameExpression = nameExpression;
}
public void doXPath(Document document) {
String name = nameExpression.evaluateAsString(document.getDocumentElement());
System.out.println("Name: " + name);
}
}
对于更灵活的方法,您可以使用NodeMapper
类似于RowMapper
Spring 的 JDBC 支持中的 。以下示例显示了如何使用它:
package sample;
public class MyXPathClass {
private final XPathExpression contactExpression;
public MyXPathClass(XPathExpression contactExpression) {
this.contactExpression = contactExpression;
}
public void doXPath(Document document) {
List contacts = contactExpression.evaluate(document,
new NodeMapper() {
public Object mapNode(Node node, int nodeNum) throws DOMException {
Element contactElement = (Element) node;
Element nameElement = (Element) contactElement.getElementsByTagName("Name").item(0);
Element phoneElement = (Element) contactElement.getElementsByTagName("Phone").item(0);
return new Contact(nameElement.getTextContent(), phoneElement.getTextContent());
}
});
PlainText Section qName; // do something with the list of Contact objects
}
}
与 Spring JDBC 中的映射行类似RowMapper
,每个结果节点都使用匿名内部类进行映射。在这种情况下,我们创建了一个Contact
对象,我们稍后会使用它。
4.3.2.XPathTemplate
XPathExpression
允许您仅评估单个预编译的表达式。一个更灵活但速度较慢的替代方案是XpathTemplate
. 此类遵循 Spring 中使用的通用模板模式(JdbcTemplate
、JmsTemplate
等)。以下清单显示了一个示例:
package sample;
public class MyXPathClass {
private XPathOperations template = new Jaxp13XPathTemplate();
public void doXPath(Source source) {
String name = template.evaluateAsString("/Contacts/Contact/Name", request);
// do something with name
}
}
4.4. 消息记录和跟踪
在开发或调试 Web 服务时,查看 (SOAP) 消息到达时或发送前的内容会非常有用。Spring Web Services 通过标准的 Commons Logging 接口提供此功能。
确保使用 Commons Logging 1.1 或更高版本。早期版本存在类加载问题,并且不与 Log4J TRACE 级别集成。 |
要记录所有服务器端消息,请将org.springframework.ws.server.MessageTracing
记录器级别设置为DEBUG
或TRACE
。在DEBUG
级别上,仅记录有效负载根元素。在TRACE
级别上,将记录整个消息内容。如果您只想记录发送的消息,请使用org.springframework.ws.server.MessageTracing.sent
记录器。同样,您可以使用org.springframework.ws.server.MessageTracing.received
仅记录收到的消息。
在客户端,存在类似的记录器:org.springframework.ws.client.MessageTracing.sent
和org.springframework.ws.client.MessageTracing.received
.
以下log4j.properties
配置文件示例记录了客户端发送消息的全部内容,并且仅记录了客户端接收消息的有效负载根元素。在服务器端,记录了发送和接收消息的有效负载根:
log4j.rootCategory=INFO, stdout
log4j.logger.org.springframework.ws.client.MessageTracing.sent=TRACE
log4j.logger.org.springframework.ws.client.MessageTracing.received=DEBUG
log4j.logger.org.springframework.ws.server.MessageTracing=DEBUG
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%p [%c{3}] %m%n
使用此配置,典型输出为:
TRACE [client.MessageTracing.sent] 发送请求 [<SOAP-ENV:Envelope xmlns:SOAP-ENV="... 调试 [server.MessageTracing.received] 收到请求 [SaajSoapMessage {http://example.com}request] ... 调试 [server.MessageTracing.sent] 发送响应 [SaajSoapMessage {http://example.com}response] ... 调试 [client.MessageTracing.received] 收到响应 [SaajSoapMessage {http://example.com}response] ...
5. 使用 Spring-WS 创建 Web 服务
Spring-WS 的服务器端支持是围绕MessageDispatcher
将传入消息分派到端点而设计的,具有可配置的端点映射、响应生成和端点拦截。端点通常使用注释进行@Endpoint
注释,并具有一种或多种处理方法。这些方法通过检查部分消息(通常是有效负载)来处理传入的 XML 请求消息并创建某种响应。您可以使用另一个注释来注释该方法,通常是@PayloadRoot
,以指示它可以处理哪种类型的消息。
Spring-WS 的 XML 处理非常灵活。端点可以从 Spring-WS 支持的大量 XML 处理库中进行选择,包括:
-
DOM 家族:W3C DOM、JDOM、dom4j 和 XOM
-
SAX 或 StAX:为了更快的性能
-
XPath:从消息中提取信息
-
编组技术(JAXB、Castor、XMLBeans、JiBX 或 XStream):将 XML 转换为对象,反之亦然
5.1。这MessageDispatcher
Spring-WS 的服务器端是围绕一个中心类设计的,该类将传入的 XML 消息分派到端点。Spring-WSMessageDispatcher
非常灵活,允许您使用任何类型的类作为端点,只要它可以在 Spring IoC 容器中进行配置。在某种程度上,消息调度程序类似于 Spring 的DispatcherServlet
,即 Spring Web MVC 中使用的“前端控制器”。
以下序列图显示了 的处理和调度流程MessageDispatcher
:
当MessageDispatcher
设置 a 以供使用并且该特定调度程序收到请求时,将MessageDispatcher
开始处理该请求。以下过程描述了如何MessageDispatcher
处理请求:
-
搜索配置
EndpointMapping(s)
的适当端点。如果找到端点,则调用与端点(预处理器、后处理器和端点)关联的调用链以创建响应。 -
为端点找到合适的适配器。对此适配器的
MessageDispatcher
委托以调用端点。 -
如果返回响应,则会在途中发送。如果没有返回响应(这可能是由于前处理器或后处理器拦截了请求,例如出于安全原因),则不会发送响应。
在处理请求期间抛出的异常被应用程序上下文中声明的任何端点异常解析器拾取。使用这些异常解析器可以定义自定义行为(例如返回 SOAP 错误),以防引发此类异常。
有MessageDispatcher
几个属性用于设置端点适配器、映射、异常解析器。但是,不需要设置这些属性,因为调度程序会自动检测在应用程序上下文中注册的所有类型。只有在需要覆盖检测时才应设置这些属性。
消息调度程序在消息上下文上运行,而不是在特定于传输的输入流和输出流上运行。因此,特定于传输的请求需要读入MessageContext
. 对于 HTTP,这是通过 a WebServiceMessageReceiverHandlerAdapter
(这是 Spring Web HandlerInterceptor
)完成的,因此MessageDispatcher
可以连接到标准DispatcherServlet
. 但是,有一种更方便的方法可以做到这一点,如图所示MessageDispatcherServlet
。
5.2. 运输
Spring Web Services 支持多种传输协议。最常见的是 HTTP 传输,它提供了一个自定义 servlet,但您也可以通过 JMS 甚至电子邮件发送消息。
5.2.1。MessageDispatcherServlet
这MessageDispatcherServlet
是一个标准Servlet
,它方便地从标准 Spring Web 扩展DispatcherServlet
并包装了MessageDispatcher
. 结果,它将这些属性合二为一。作为一个MessageDispatcher
,它遵循与上一节中描述的相同的请求处理流程。作为 servlet,在您的 Web 应用程序MessageDispatcherServlet
中进行配置。web.xml
您希望处理的请求必须通过同一文件MessageDispatcherServlet
中的 URL 映射进行映射。web.xml
这是标准的 Java EE servlet 配置。以下示例显示了这样的MessageDispatcherServlet
声明和映射:
<web-app>
<servlet>
<servlet-name>spring-ws</servlet-name>
<servlet-class>org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring-ws</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
在前面的示例中,所有请求都由spring-ws
MessageDispatcherServlet
. 这只是设置 Spring Web Services 的第一步,因为 Spring-WS 框架使用的各种组件 bean 也需要进行配置。此配置由标准 Spring XML<bean/>
定义组成。因为MessageDispatcherServlet
是一个标准的 Spring DispatcherServlet
,它会在你的 web 应用程序的目录中查找一个名为 [servlet-name]-servlet.xml 的WEB-INF
文件,并在 Spring 容器中创建定义的 bean。在前面的示例中,它查找“/WEB-INF/spring-ws-servlet.xml”。此文件包含所有 Spring Web 服务 bean,例如端点、编组器等。
作为替代方案web.xml
,如果您在 Servlet 3+ 环境中运行,您可以通过编程方式配置 Spring-WS。为此,Spring-WS 提供了许多抽象基类,这些基类扩展WebApplicationInitializer
了 Spring 框架中的接口。如果您还@Configuration
为您的 bean 定义使用类,您应该扩展AbstractAnnotationConfigMessageDispatcherServletInitializer
:
public class MyServletInitializer
extends AbstractAnnotationConfigMessageDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{MyRootConfig.class};
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{MyEndpointConfig.class};
}
}
在前面的示例中,我们告诉 Spring 端点 bean 定义可以在MyEndpointConfig
类(这是一个@Configuration
类)中找到。可以在MyRootConfig
该类中找到其他 bean 定义(通常是服务、存储库等)。默认情况下,AbstractAnnotationConfigMessageDispatcherServletInitializer
将 servlet 映射到两种模式:/services
和*.wsdl
,尽管您可以通过覆盖该getServletMappings()
方法来更改它。有关 的编程配置的更多详细信息,请参阅和MessageDispatcherServlet
的 Javadoc 。AbstractMessageDispatcherServletInitializer
AbstractAnnotationConfigMessageDispatcherServletInitializer
自动 WSDL 暴露
MessageDispatcherServlet
自动检测在其 Spring 容器中定义的任何bean WsdlDefinition
。所有WsdlDefinition
检测到的 bean 也通过WsdlDefinitionHandlerAdapter
. 这是通过定义一些 bean 向客户端公开 WSDL 的便捷方式。
作为示例,请考虑在 Spring-WS 配置文件 ( )<static-wsdl>
中定义的以下定义。/WEB-INF/[servlet-name]-servlet.xml
注意id
属性的值,因为它在公开 WSDL 时使用。
<sws:static-wsdl id="orders" location="orders.wsdl"/>
或者,它可以是类中的@Bean
方法@Configuration
:
@Bean
public SimpleWsdl11Definition orders() {
return new SimpleWsdl11Definition(new ClassPathResource("orders.wsdl"));
}
您可以通过对以下形式的 URL 的请求访问orders.wsdl
类路径上文件中定义的 WSDL (根据需要替换主机、端口和 servlet 上下文路径):GET
http://localhost:8080/spring-ws/orders.wsdl
所有WsdlDefinition bean 定义都通过MessageDispatcherServlet bean 名称下的 .wsdl 后缀公开。因此,如果 bean 名称为echo ,主机名称为server ,并且 Servlet 上下文(war 名称)为spring-ws ,则 WSDL 可以在 中找到http://server/spring-ws/echo.wsdl 。
|
MessageDispatcherServlet
(或者更准确地说是)的另一个很好的特性WsdlDefinitionHandlerAdapter
是它可以转换location
它公开的所有 WSDL 的值,以反映传入请求的 URL。
请注意,此location
转换功能默认关闭。要打开此功能,您需要指定一个初始化参数MessageDispatcherServlet
:
<web-app>
<servlet>
<servlet-name>spring-ws</servlet-name>
<servlet-class>org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class>
<init-param>
<param-name>transformWsdlLocations</param-name>
<param-value>true</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>spring-ws</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
如果使用AbstractAnnotationConfigMessageDispatcherServletInitializer
,启用转换就像重写isTransformWsdlLocations()
返回方法一样简单true
。
查阅类上的类级 JavadocWsdlDefinitionHandlerAdapter
以了解有关整个转换过程的更多信息。
作为手动编写 WSDL 并使用 公开它的替代方法<static-wsdl>
,Spring Web Services 还可以从 XSD 模式生成 WSDL。这是发布 WSDL中所示的方法。下一个应用程序上下文片段展示了如何创建这样一个动态 WSDL 文件:
<sws:dynamic-wsdl id="orders"
portTypeName="Orders"
locationUri="http://localhost:8080/ordersService/">
<sws:xsd location="Orders.xsd"/>
</sws:dynamic-wsdl>
或者,您可以使用 Java@Bean
方法:
@Bean
public DefaultWsdl11Definition orders() {
DefaultWsdl11Definition definition = new DefaultWsdl11Definition();
definition.setPortTypeName("Orders");
definition.setLocationUri("http://localhost:8080/ordersService/");
definition.setSchema(new SimpleXsdSchema(new ClassPathResource("echo.xsd")));
return definition;
}
<dynamic-wsdl>
元素取决于类DefaultWsdl11Definition
。此定义类使用org.springframework.ws.wsdl.wsdl11.provider
包中的 WSDL 提供程序和ProviderBasedWsdl4jDefinition
该类在第一次被请求时生成 WSDL。如有必要,请参阅这些类的类级别 Javadoc 以了解如何扩展此机制。
(DefaultWsdl11Definition
因此,<dynamic-wsdl>
标签)使用约定从 XSD 模式构建 WSDL。它遍历element
模式中找到的所有元素并为所有元素创建一个message
。operation
接下来,它为所有以定义的请求或响应后缀结尾的消息创建一个 WSDL 。默认请求后缀是Request
. 默认响应后缀是Response
,尽管可以通过分别设置requestSuffix
和responseSuffix
属性来更改这些后缀<dynamic-wsdl />
。它还基于操作构建portType
、binding
和。service
例如,如果我们的Orders.xsd
模式定义了GetOrdersRequest
andGetOrdersResponse
元素,<dynamic-wsdl>
创建一个GetOrdersRequest
andGetOrdersResponse
消息和一个GetOrders
操作,它被放入一个Orders
端口类型中。
要通过包含或导入使用多个模式,您可以将 Commons XMLSchema 放在类路径上。如果 Commons XMLSchema 在类路径上,则该<dynamic-wsdl>
元素遵循所有 XSD 导入,并将它们作为单个 XSD 包含并内联到 WSDL 中。这极大地简化了模式的部署,同时仍然可以单独编辑它们。
尽管在运行时从 XSD 创建 WSDL 很方便,但这种方法有几个缺点。首先,尽管我们尝试在不同版本之间保持 WSDL 生成过程的一致性,但它仍然有可能(稍微)发生变化。其次,生成有点慢,不过,一旦生成,WSDL 会被缓存以供以后参考。 |
因此,您应该<dynamic-wsdl>
只在项目的开发阶段使用。我们建议使用您的浏览器下载生成的 WSDL,将其存储在项目中,并使用<static-wsdl>
. 这是真正确保 WSDL 不会随时间变化的唯一方法。
5.2.2. 在一个中连接 Spring-WSDispatcherServlet
作为 的替代方案MessageDispatcherServlet
,您可以将 a 连接MessageDispatcher
到标准的 Spring-Web MVCDispatcherServlet
中。默认情况下,DispatcherServlet
只能委托给,但我们可以通过将 a 添加到 servlet 的 Web 应用程序上下文中Controllers
来指示它委托给 a :MessageDispatcher
WebServiceMessageReceiverHandlerAdapter
<beans>
<bean class="org.springframework.ws.transport.http.WebServiceMessageReceiverHandlerAdapter"/>
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="defaultHandler" ref="messageDispatcher"/>
</bean
<bean id="messageDispatcher" class="org.springframework.ws.soap.server.SoapMessageDispatcher"/>
...
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>
</beans>
请注意,通过显式添加WebServiceMessageReceiverHandlerAdapter
,调度程序 servlet 不会加载默认适配器并且无法处理标准 Spring-MVC @Controllers
。因此,我们RequestMappingHandlerAdapter
在末尾添加了。
以类似的方式,您可以连接 aWsdlDefinitionHandlerAdapter
以确保DispatcherServlet
可以处理WsdlDefinition
接口的实现:
<beans>
<bean class="org.springframework.ws.transport.http.WebServiceMessageReceiverHandlerAdapter"/>
<bean class="org.springframework.ws.transport.http.WsdlDefinitionHandlerAdapter"/>
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="*.wsdl">myServiceDefinition</prop>
</props>
</property>
<property name="defaultHandler" ref="messageDispatcher"/>
</bean>
<bean id="messageDispatcher" class="org.springframework.ws.soap.server.SoapMessageDispatcher"/>
<bean id="myServiceDefinition" class="org.springframework.ws.wsdl.wsdl11.SimpleWsdl11Definition">
<prop name="wsdl" value="/WEB-INF/myServiceDefintion.wsdl"/>
</bean>
...
</beans>
5.2.3。JMS 传输
Spring Web Services 通过 Spring 框架中提供的 JMS 功能支持服务器端 JMS 处理。Spring Web Services 提供了WebServiceMessageListener
插件到MessageListenerContainer
. 这个消息监听器需要一个WebServiceMessageFactory
andMessageDispatcher
来操作。以下配置示例显示了这一点:
<beans>
<bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="vm://localhost?broker.persistent=false"/>
</bean>
<bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory"/>
<bean class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="destinationName" value="RequestQueue"/>
<property name="messageListener">
<bean class="org.springframework.ws.transport.jms.WebServiceMessageListener">
<property name="messageFactory" ref="messageFactory"/>
<property name="messageReceiver" ref="messageDispatcher"/>
</bean>
</property>
</bean>
<bean id="messageDispatcher" class="org.springframework.ws.soap.server.SoapMessageDispatcher">
<property name="endpointMappings">
<bean
class="org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping">
<property name="defaultEndpoint">
<bean class="com.example.MyEndpoint"/>
</property>
</bean>
</property>
</bean>
</beans>
5.2.4。电子邮件传输
除了 HTTP 和 JMS,Spring Web Services 还提供服务器端电子邮件处理。这个功能是通过MailMessageReceiver
类提供的。此类监视 POP3 或 IMAP 文件夹,将电子邮件转换为WebServiceMessage
,并使用 SMTP 发送任何响应。您可以通过 配置主机名storeUri
,指示要监视请求的邮件文件夹(通常是 POP3 或 IMAP 文件夹),以及transportUri
指示用于发送响应的服务器(通常是 SMTP 服务器)。
您可以MailMessageReceiver
使用可插拔策略配置监控传入消息的方式:MonitoringStrategy
. 默认情况下,使用轮询策略,其中每五分钟轮询传入文件夹以查找新邮件。pollingInterval
您可以通过设置策略的属性来更改此间隔。默认情况下,所有MonitoringStrategy
实现都会删除已处理的消息。您可以通过设置deleteMessages
属性来更改此设置。
作为非常低效的轮询方法的替代方法,有一种使用 IMAP IDLE 的监视策略。MailMessageReceiver
IDLE 命令是 IMAP 电子邮件协议的可选扩展,它允许邮件服务器向异步发送新消息更新。如果您使用支持 IDLE 命令的 IMAP 服务器,则可以将其ImapIdleMonitoringStrategy
插入monitoringStrategy
属性。除了支持服务器,您还需要使用 JavaMail 1.4.1 或更高版本。
以下配置显示如何使用服务器端电子邮件支持,覆盖默认轮询间隔以每 30 秒(30.000 毫秒)检查一次:
<beans>
<bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory"/>
<bean id="messagingReceiver" class="org.springframework.ws.transport.mail.MailMessageReceiver">
<property name="messageFactory" ref="messageFactory"/>
<property name="from" value="Spring-WS SOAP Server <server@example.com>"/>
<property name="storeUri" value="imap://server:s04p@imap.example.com/INBOX"/>
<property name="transportUri" value="smtp://smtp.example.com"/>
<property name="messageReceiver" ref="messageDispatcher"/>
<property name="monitoringStrategy">
<bean class="org.springframework.ws.transport.mail.monitor.PollingMonitoringStrategy">
<property name="pollingInterval" value="30000"/>
</bean>
</property>
</bean>
<bean id="messageDispatcher" class="org.springframework.ws.soap.server.SoapMessageDispatcher">
<property name="endpointMappings">
<bean
class="org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping">
<property name="defaultEndpoint">
<bean class="com.example.MyEndpoint"/>
</property>
</bean>
</property>
</bean>
</beans>
5.2.5。嵌入式 HTTP 服务器传输
Spring Web Services 提供基于 Sun 的 JRE 1.6 HTTP 服务器的传输。嵌入式 HTTP 服务器是一个易于配置的独立服务器。它为传统的 servlet 容器提供了一种更轻量级的替代方案。
使用嵌入式 HTTP 服务器时,您不需要外部部署描述符 ( web.xml
)。您只需要定义一个服务器实例并将其配置为处理传入请求。Core Spring Framework 中的远程处理模块包含一个方便的 HTTP 服务器工厂 bean SimpleHttpServerFactoryBean
:. 最重要的属性是contexts
,它将上下文路径映射到相应的HttpHandler
实例。
Spring Web Services 提供了两种HttpHandler
接口实现:WsdlDefinitionHttpHandler
和WebServiceMessageReceiverHttpHandler
. 前者将传入的 GET 请求映射到WsdlDefinition
. 后者负责处理 Web 服务消息的 POST 请求,因此需要一个WebServiceMessageFactory
(通常是 a SaajSoapMessageFactory
)和一个WebServiceMessageReceiver
(通常是SoapMessageDispatcher
)来完成其任务。
为了与 servlet 世界进行类比,该contexts
属性在 中扮演 servlet 映射的角色,web.xml
并且WebServiceMessageReceiverHttpHandler
相当于 a MessageDispatcherServlet
。
以下片段显示了 HTTP 服务器传输的配置示例:
<beans>
<bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory"/>
<bean id="messageReceiver" class="org.springframework.ws.soap.server.SoapMessageDispatcher">
<property name="endpointMappings" ref="endpointMapping"/>
</bean>
<bean id="endpointMapping" class="org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping">
<property name="defaultEndpoint" ref="stockEndpoint"/>
</bean>
<bean id="httpServer" class="org.springframework.remoting.support.SimpleHttpServerFactoryBean">
<property name="contexts">
<map>
<entry key="/StockService.wsdl" value-ref="wsdlHandler"/>
<entry key="/StockService" value-ref="soapHandler"/>
</map>
</property>
</bean>
<bean id="soapHandler" class="org.springframework.ws.transport.http.WebServiceMessageReceiverHttpHandler">
<property name="messageFactory" ref="messageFactory"/>
<property name="messageReceiver" ref="messageReceiver"/>
</bean>
<bean id="wsdlHandler" class="org.springframework.ws.transport.http.WsdlDefinitionHttpHandler">
<property name="definition" ref="wsdlDefinition"/>
</bean>
</beans>
有关 的更多信息SimpleHttpServerFactoryBean
,请参阅Javadoc。
5.2.6。XMPP 传输
Spring Web Services 2.0 引入了对 XMPP 的支持,也称为 Jabber。该支持基于Smack库。
Spring Web Services 对 XMPP 的支持与其他传输非常相似:有XmppMessageSender
用于 的aaWebServiceTemplate
和XmppMessageReceiver
用于MessageDispatcher
.
以下示例显示了如何设置服务器端 XMPP 组件:
<beans>
<bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory"/>
<bean id="connection" class="org.springframework.ws.transport.xmpp.support.XmppConnectionFactoryBean">
<property name="host" value="jabber.org"/>
<property name="username" value="username"/>
<property name="password" value="password"/>
</bean>
<bean id="messagingReceiver" class="org.springframework.ws.transport.xmpp.XmppMessageReceiver">
<property name="messageFactory" ref="messageFactory"/>
<property name="connection" ref="connection"/>
<property name="messageReceiver" ref="messageDispatcher"/>
</bean>
<bean id="messageDispatcher" class="org.springframework.ws.soap.server.SoapMessageDispatcher">
<property name="endpointMappings">
<bean
class="org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping">
<property name="defaultEndpoint">
<bean class="com.example.MyEndpoint"/>
</property>
</bean>
</property>
</bean>
</beans>
5.2.7. MTOM
MTOM是一种向 Web 服务发送二进制数据和从 Web 服务发送二进制数据的机制。您可以通过MTOM 示例了解如何使用 Spring WS 实现这一点。
5.3. 端点
端点是 Spring-WS 服务器端支持的核心概念。端点提供对应用程序行为的访问,这通常由业务服务接口定义。端点解释 XML 请求消息并使用该输入(通常)调用业务服务上的方法。该服务调用的结果表示为响应消息。Spring-WS 有各种各样的端点,并使用各种方法来处理 XML 消息和创建响应。
您可以通过使用注释对类进行注释来创建端点@Endpoint
。在该类中,您可以使用多种参数类型(例如 DOM 元素、JAXB2 对象等)定义一个或多个处理传入 XML 请求的方法。@PayloadRoot
您可以通过使用另一个注解(通常是)来指示方法可以处理的消息类型。
考虑以下示例端点:
package samples;
import org.w3c.dom.Element;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
import org.springframework.ws.soap.SoapHeader;
@Endpoint (1)
public class AnnotationOrderEndpoint {
private final OrderService orderService;
@Autowired (2)
public AnnotationOrderEndpoint(OrderService orderService) {
this.orderService = orderService;
}
@PayloadRoot(localPart = "order", namespace = "http://samples") (5)
public void order(@RequestPayload Element orderElement) { (3)
Order order = createOrder(orderElement);
orderService.createOrder(order);
}
@PayloadRoot(localPart = "orderRequest", namespace = "http://samples") (5)
@ResponsePayload
public Order getOrder(@RequestPayload OrderRequest orderRequest, SoapHeader header) { (4)
checkSoapHeaderForSomething(header);
return orderService.getOrder(orderRequest.getId());
}
...
}
1 | 该类使用 注释@Endpoint ,将其标记为 Spring-WS 端点。 |
2 | 构造函数带有标记,@Autowired 以便将OrderService 业务服务注入此端点。 |
3 | 该order 方法将Element (用 注释@RequestPayload )作为参数。这意味着消息的有效负载作为 DOM 元素在此方法上传递。该方法有void 返回类型,表示不发送响应消息。有关端点方法的更多信息,请参阅@Endpoint 处理方法。 |
4 | 该getOrder 方法采用OrderRequest (也用 注释@RequestPayload )作为参数。此参数是 JAXB2 支持的对象(用 注释@XmlRootElement )。这意味着消息的有效负载作为未编组的对象传递给此方法。SoapHeader 类型也作为参数给出。在调用时,此参数包含请求消息的 SOAP 标头。该方法还用 注释@ResponsePayload ,表示返回值(Order )用作响应消息的有效负载。有关端点方法的更多信息,请参阅@Endpoint 处理方法。 |
5 | 该端点的两个处理方法用 标记@PayloadRoot ,表示该方法可以处理哪种类型的请求消息:该getOrder 方法是针对具有orderRequest 本地名称和http://samples 命名空间 URI 的请求调用的。对具有order 本地名称的请求调用 order 方法。有关详细信息@PayloadRoot ,请参阅端点映射。 |
要启用对@Endpoint
相关 Spring-WS 注释的支持,您需要将以下内容添加到 Spring 应用程序上下文中:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:sws="http://www.springframework.org/schema/web-services"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/web-services
http://www.springframework.org/schema/web-services/web-services.xsd">
*<sws:annotation-driven />
</beans>
或者,如果您使用@Configuration
类而不是 Spring XML,则可以使用以下命令注释您的配置类@EnableWs
:
@EnableWs
@Configuration
public class EchoConfig {
// @Bean definitions go here
}
要自定义@EnableWs
配置,您可以实现WsConfigurer
,或者更好的是,扩展WsConfigurerAdapter
:
@Configuration
@EnableWs
@ComponentScan(basePackageClasses = { MyConfiguration.class })
public class MyConfiguration extends WsConfigurerAdapter {
@Override
public void addInterceptors(List<EndpointInterceptor> interceptors) {
interceptors.add(new MyInterceptor());
}
@Override
public void addArgumentResolvers(List<MethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new MyArgumentResolver());
}
// More overridden methods ...
}
在接下来的几节中,将对@Endpoint
编程模型进行更详细的描述。
端点,与任何其他 Spring Bean 一样,默认情况下被限定为单例。也就是说,每个容器都会创建一个 bean 定义的实例。作为单例意味着多个线程可以同时使用它,因此端点必须是线程安全的。如果您想使用不同的范围,例如原型,请参阅Spring 参考文档。 |
请注意,Spring-WS 中提供的所有抽象基类都是线程安全的,除非在类级别 Javadoc 中另有说明。
5.3.1。@Endpoint
处理方法
要让端点实际处理传入的 XML 消息,它需要有一个或多个处理方法。处理方法可以采用广泛的参数和返回类型。但是,它们通常具有一个包含消息有效负载的参数,并且它们返回响应消息的有效负载(如果有)。本节介绍支持哪些参数和返回类型。
为了指示一个方法可以处理哪种类型的消息,该方法通常使用 the@PayloadRoot
或注解进行@SoapAction
注解。您可以在Endpoint mappings中了解有关这些注释的更多信息。
以下示例显示了一种处理方法:
@PayloadRoot(localPart = "order", namespace = "http://samples")
public void order(@RequestPayload Element orderElement) {
Order order = createOrder(orderElement);
orderService.createOrder(order);
}
该order
方法将Element
(用 注释@RequestPayload
)作为参数。这意味着消息的有效负载作为 DOM 元素在此方法上传递。该方法有void
返回类型,表示不发送响应消息。
处理方法参数
处理方法通常具有一个或多个参数,这些参数引用传入 XML 消息的各个部分。最常见的是,处理方法有一个参数映射到消息的有效负载,但它也可以映射到请求消息的其他部分,例如 SOAP 标头。本节介绍您可以在处理方法签名中使用的参数。
要将参数映射到请求消息的有效负载,您需要使用注解对该参数进行@RequestPayload
注解。这个注解告诉 Spring-WS 参数需要绑定到请求负载。
下表描述了支持的参数类型。它显示了支持的类型,参数是否应该用 注释@RequestPayload
,以及任何其他注释。
姓名 | 支持的参数类型 | @RequestPayload 必需的? |
补充说明 |
---|---|---|---|
TrAX |
|
是的 |
默认启用。 |
W3C DOM |
|
是的 |
默认启用 |
dom4j |
|
是的 |
当 dom4j 在类路径上时启用。 |
JDOM |
|
是的 |
当 JDOM 在类路径上时启用。 |
XOM |
|
是的 |
当 XOM 在类路径上时启用。 |
斯塔克斯 |
|
是的 |
当 StAX 在类路径上时启用。 |
XPath |
任何布尔值、双精度、、、、 |
不 |
默认启用,请参阅名为 |
消息上下文 |
|
不 |
默认启用。 |
肥皂 |
|
不 |
默认启用。 |
JAXB2 |
|
是的 |
当 JAXB2 在类路径上时启用。 |
OXM |
Spring OXM 支持的任何类型 |
是的 |
|
接下来的几个示例显示了可能的方法签名。使用请求消息的有效负载作为 DOM 调用以下方法org.w3c.dom.Element
:
public void handle(@RequestPayload Element element)
使用请求消息的有效负载作为javax.xml.transform.dom.DOMSource
. 该header
参数绑定到请求消息的 SOAP 标头。
public void handle(@RequestPayload DOMSource domSource, SoapHeader header)
调用以下方法,请求消息的有效负载被解组为 a MyJaxb2Object
(用 注释@XmlRootElement
)。消息的有效负载也以 DOM 形式给出Element
。整个消息上下文作为第三个参数传递。
public void handle(@RequestPayload MyJaxb2Object requestObject, @RequestPayload Element element, Message messageContext)
如您所见,在定义如何处理方法签名时有很多可能性。您甚至可以扩展此机制以支持您自己的参数类型。请参阅 JavadocDefaultMethodEndpointAdapter
并MethodArgumentResolver
了解如何操作。
@XPathParam
一种参数类型需要一些额外的解释:@XPathParam
. 这里的想法是使用 XPath 表达式注释一个或多个方法参数,并且每个这样的注释参数都绑定到表达式的评估。以下示例显示了如何执行此操作:
package samples;
import javax.xml.transform.Source;
import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.Namespace;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
import org.springframework.ws.server.endpoint.annotation.XPathParam;
@Endpoint
public class AnnotationOrderEndpoint {
private final OrderService orderService;
public AnnotationOrderEndpoint(OrderService orderService) {
this.orderService = orderService;
}
@PayloadRoot(localPart = "orderRequest", namespace = "http://samples")
@Namespace(prefix = "s", uri="http://samples")
public Order getOrder(@XPathParam("/s:orderRequest/@id") int orderId) {
Order order = orderService.getOrder(orderId);
// create Source from order and return it
}
}
因为我们s
在 XPath 表达式中使用了前缀,所以我们必须将它绑定到http://samples
命名空间。这是通过@Namespace
注释完成的。或者,我们可以将此注释放置在类型级别上,以便为所有处理程序方法使用相同的命名空间映射,甚至可以在包级别 (in package-info.java
) 上将其用于多个端点。
通过使用@XPathParam
,您可以绑定到 XPath 支持的所有数据类型:
-
boolean
或者Boolean
-
double
或者Double
-
String
-
Node
-
NodeList
除了这个列表之外,您还可以使用任何可以String
通过 Spring转换服务从 a 转换的类型。
处理方法返回类型
要发送响应消息,处理需要指定返回类型。如果不需要响应消息,该方法可以声明void
返回类型。最常见的是,返回类型用于创建响应消息的有效负载。但是,您也可以映射到响应消息的其他部分。本节描述您可以在处理方法签名中使用的返回类型。
要将返回值映射到响应消息的有效负载,您需要使用注解对方法进行@ResponsePayload
注解。这个注解告诉 Spring-WS 返回值需要绑定到响应负载。
下表描述了支持的返回类型。它显示了支持的类型,参数是否应该用 注释@ResponsePayload
,以及任何其他注释。
姓名 | 支持的返回类型 | @ResponsePayload 必需的? |
补充说明 |
---|---|---|---|
没有反应 |
|
不 |
默认启用。 |
TrAX |
|
是的 |
默认启用。 |
W3C DOM |
|
是的 |
默认启用 |
dom4j |
|
是的 |
当 dom4j 在类路径上时启用。 |
JDOM |
|
是的 |
当 JDOM 在类路径上时启用。 |
XOM |
|
是的 |
当 XOM 在类路径上时启用。 |
JAXB2 |
|
是的 |
当 JAXB2 在类路径上时启用。 |
OXM |
Spring OXM 支持的任何类型 |
是的 |
|
在定义处理方法签名时有很多可能性。甚至可以扩展此机制以支持您自己的参数类型。请参阅 的类级别 JavadocDefaultMethodEndpointAdapter
并MethodReturnValueHandler
了解如何操作。
5.4. 端点映射
端点映射负责将传入消息映射到适当的端点。默认情况下会启用一些端点映射——例如,PayloadRootAnnotationMethodEndpointMapping
或SoapActionAnnotationMethodEndpointMapping
. 但是,我们首先需要检查 a 的一般概念EndpointMapping
。
AnEndpointMapping
传递 a EndpointInvocationChain
,其中包含与传入请求匹配的端点,还可能包含应用于请求和响应的端点拦截器列表。当请求进来时,将MessageDispatcher
其交给端点映射,让它检查请求并提出适当的EndpointInvocationChain
. 然后MessageDispatcher
调用链中的端点和任何拦截器。
可以选择包含拦截器(反过来,可以操纵请求、响应或两者)的可配置端点映射的概念非常强大。许多支持功能可以内置到自定义EndpointMapping
实现中。例如,自定义端点映射不仅可以基于消息的内容,还可以基于特定的 SOAP 标头(或者实际上是多个 SOAP 标头)来选择端点。
大多数端点映射都继承自AbstractEndpointMapping
,它提供了一个“拦截器”属性,即要使用的拦截器列表。EndpointInterceptors
在拦截请求 -EndpointInterceptor
接口中进行了讨论。此外,还有defaultEndpoint
,这是在此端点映射未产生匹配端点时使用的默认端点。
如Endpoints中所述,该@Endpoint
样式允许您在一个端点类中处理多个请求。这是 的责任MethodEndpointMapping
。此映射确定要为传入的请求消息调用哪个方法。
有两个端点映射可以将请求定向到方法:PayloadRootAnnotationMethodEndpointMapping
和SoapActionAnnotationMethodEndpointMapping
您可以通过<sws:annotation-driven/>
在应用程序上下文中使用来启用这两种方法。
使用带有and元素PayloadRootAnnotationMethodEndpointMapping
的@PayloadRoot
注释来标记具有特定限定名称的方法。每当带有此有效负载根元素的限定名称的消息进入时,就会调用该方法。例如,请参见上文。localPart
namespace
或者,SoapActionAnnotationMethodEndpointMapping
使用@SoapAction
注释来标记具有特定 SOAP 操作的方法。每当带有此SOAPAction
标头的消息进入时,都会调用该方法。
5.4.1。WS-寻址
WS-Addressing 指定了一种与传输无关的路由机制。它基于To
和Action
SOAP 标头,它们分别指示 SOAP 消息的目的地和意图。此外,WS-Addressing 允许您定义返回地址(用于正常消息和故障)和唯一消息标识符,可用于关联。有关 WS-Addressing 的更多信息,请参阅https://en.wikipedia.org/wiki/WS-Addressing。以下示例显示了 WS-Addressing 消息:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope"
xmlns:wsa="http://www.w3.org/2005/08/addressing">
<SOAP-ENV::Header>
<wsa:MessageID>urn:uuid:21363e0d-2645-4eb7-8afd-2f5ee1bb25cf</wsa:MessageID>
<wsa:ReplyTo>
<wsa:Address>http://example.com/business/client1</wsa:Address>
</wsa:ReplyTo>
<wsa:To S:mustUnderstand="true">http://example/com/fabrikam</wsa:To>
<wsa:Action>http://example.com/fabrikam/mail/Delete</wsa:Action>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<f:Delete xmlns:f="http://example.com/fabrikam">
<f:maxCount>42</f:maxCount>
</f:Delete>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
在前面的示例中,目标设置为http://example/com/fabrikam
,而操作设置为http://example.com/fabrikam/mail/Delete
。此外,还有一个消息标识符和一个回复地址。默认情况下,该地址为“匿名”地址,表示应使用与请求相同的通道(即 HTTP 响应)发送响应,但也可以是其他地址,如本例所示。
在 Spring Web Services 中,WS-Addressing 被实现为端点映射。通过使用此映射,您可以将 WS-Addressing 操作与端点相关联,类似于SoapActionAnnotationMethodEndpointMapping
前面所述。
使用AnnotationActionEndpointMapping
AnnotationActionEndpointMapping
类似于SoapActionAnnotationMethodEndpointMapping
WS-Addressing 头而不是 SOAP Action 传输头。
要使用AnnotationActionEndpointMapping
,请使用注解对处理方法进行@Action
注解,类似于处理方法和端点映射中描述的@PayloadRoot
和注解。以下示例显示了如何执行此操作:@SoapAction
@Endpoint
package samples;
import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.soap.addressing.server.annotation.Action
@Endpoint
public class AnnotationOrderEndpoint {
private final OrderService orderService;
public AnnotationOrderEndpoint(OrderService orderService) {
this.orderService = orderService;
}
@Action("http://samples/RequestOrder")
public Order getOrder(OrderRequest orderRequest) {
return orderService.getOrder(orderRequest.getId());
}
@Action("http://samples/CreateOrder")
public void order(Order order) {
orderService.createOrder(order);
}
}
前面的映射将具有 WS-AddressingAction
的请求路由http://samples/RequestOrder
到该getOrder
方法。请求http://samples/CreateOrder
被路由到order
方法..
默认情况下,AnnotationActionEndpointMapping
支持 1.0(2006 年 5 月)和 2004 年 8 月版本的 WS-Addressing。这两个版本最受欢迎,并且可与 Axis 1 和 2、JAX-WS、XFire、Windows Communication Foundation (WCF) 和 Windows Services Enhancements (WSE) 3.0 互操作。如有必要,可以将规范的特定版本注入到versions
属性中。
除了@Action
注解之外,您还可以使用注解对类进行@Address
注解。如果设置,则将该值与To
传入消息的标头属性进行比较。
最后,还有一个messageSenders
属性,它是向非匿名、越界地址发送响应消息所必需的。您可以MessageSender
在此属性中设置实现,就像在WebServiceTemplate
. 请参阅URI 和传输。
5.4.2. 拦截请求——EndpointInterceptor
接口
端点映射机制具有端点拦截器的概念。当您想将特定功能应用于某些请求时,这些功能非常有用——例如,处理与安全相关的 SOAP 标头或记录请求和响应消息。
<sws:interceptors>
端点拦截器通常通过在应用程序上下文中使用元素来定义。在此元素中,您可以定义应用于该应用程序上下文中定义的所有端点的端点拦截器 bean。或者,您可以使用<sws:payloadRoot>
或<sws:soapAction>
元素来指定拦截器应应用的有效负载根名称或 SOAP 操作。以下示例显示了如何执行此操作:
<sws:interceptors>
<bean class="samples.MyGlobalInterceptor"/>
<sws:payloadRoot namespaceUri="http://www.example.com">
<bean class="samples.MyPayloadRootInterceptor"/>
</sws:payloadRoot>
<sws:soapAction value="http://www.example.com/SoapAction">
<bean class="samples.MySoapActionInterceptor1"/>
<ref bean="mySoapActionInterceptor2"/>
</sws:soapAction>
</sws:interceptors>
<bean id="mySoapActionInterceptor2" class="samples.MySoapActionInterceptor2"/>
在前面的示例中,我们定义了一个“全局”拦截器 ( MyGlobalInterceptor
) 来拦截所有请求和响应。我们还定义了一个拦截器,该拦截器仅适用于具有http://www.example.com
作为有效负载根命名空间的 XML 消息。除了 之外,我们还可以定义一个localPart
属性namespaceUri
来进一步限制拦截器适用的消息。最后,我们定义了两个在消息具有http://www.example.com/SoapAction
SOAP 操作时应用的拦截器。注意第二个拦截器实际上是对<interceptors>
元素外部 bean 定义的引用。您可以在<interceptors>
元素内的任何位置使用 bean 引用。
当您使用@Configuration
类时,您可以扩展 fromWsConfigurerAdapter
以添加拦截器:
@Configuration
@EnableWs
public class MyWsConfiguration extends WsConfigurerAdapter {
@Override
public void addInterceptors(List<EndpointInterceptor> interceptors) {
interceptors.add(new MyPayloadRootInterceptor());
}
}
拦截器必须实现包中的EndpointInterceptor
接口org.springframework.ws.server
。该接口定义了三种方法,一种可用于在处理实际端点之前处理请求消息,一种可用于处理正常响应消息,另一种可用于处理故障消息。处理完端点后调用后两个。这三种方法应该提供足够的灵活性来进行各种预处理和后处理。
拦截器上的handleRequest(..)
方法返回一个布尔值。您可以使用此方法中断或继续处理调用链。当此方法返回true
时,端点处理链将继续。当它返回时false
,MessageDispatcher
解释这意味着拦截器本身已经处理好事情并且不会继续处理其他拦截器和调用链中的实际端点。handleResponse(..)
andhandleFault(..)
方法也有一个布尔返回值。当这些方法返回false
时,响应将不会被发送回客户端。
EndpointInterceptor
您可以在 Web 服务中使用许多标准实现。此外,还有XwsSecurityInterceptor
,在 中描述XwsSecurityInterceptor
。
PayloadLoggingInterceptor
和SoapEnvelopeLoggingInterceptor
在开发 Web 服务时,记录传入和传出的 XML 消息会很有用。Spring WS 通过PayloadLoggingInterceptor
和SoapEnvelopeLoggingInterceptor
类促进了这一点。前者仅将消息的有效负载记录到 Commons Logging Log。后者记录整个 SOAP 信封,包括 SOAP 标头。以下示例显示了如何PayloadLoggingInterceptor
在端点映射中定义:
<sws:interceptors>
<bean class="org.springframework.ws.server.endpoint.interceptor.PayloadLoggingInterceptor"/>
</sws:interceptors>
这两个拦截器都有两个属性,logRequest
和logResponse
,可以设置false
为禁用请求或响应消息的日志记录。
WsConfigurerAdapter
如前所述,您也可以使用该方法PayloadLoggingInterceptor
。
PayloadValidatingInterceptor
使用契约优先开发风格的好处之一是我们可以使用模式来验证传入和传出的 XML 消息。Spring-WS 通过PayloadValidatingInterceptor
. 此拦截器需要一个或多个 W3C XML 或 RELAX NG 模式的引用,并且可以设置为验证请求、响应或两者。
请注意,请求验证听起来可能是个好主意,但它使生成的 Web 服务非常严格。通常,请求是否验证并不重要,只有端点能够获得足够的信息来完成请求。验证响应是一个好主意,因为端点应该遵守它的模式。记住 Postel 定律:“在你所做的事情上保持保守;在你接受他人的事情上保持自由。” |
以下示例使用PayloadValidatingInterceptor
. 在此示例中,我们使用架构/WEB-INF/orders.xsd
来验证响应而不是请求。请注意,PayloadValidatingInterceptor
还可以通过设置schemas
属性来接受多个模式。
<bean id="validatingInterceptor"
class="org.springframework.ws.soap.server.endpoint.interceptor.PayloadValidatingInterceptor">
<property name="schema" value="/WEB-INF/orders.xsd"/>
<property name="validateRequest" value="false"/>
<property name="validateResponse" value="true"/>
</bean>
当然,您也可以使用WsConfigurerAdapter
前面所述的方法PayloadValidatingInterceptor
。
使用PayloadTransformingInterceptor
为了将有效负载转换为另一种 XML 格式,Spring Web Services 提供了PayloadTransformingInterceptor
. 此端点拦截器基于 XSLT 样式表,在支持 Web 服务的多个版本时特别有用,因为您可以将旧消息格式转换为新格式。以下示例使用PayloadTransformingInterceptor
:
<bean id="transformingInterceptor"
class="org.springframework.ws.server.endpoint.interceptor.PayloadTransformingInterceptor">
<property name="requestXslt" value="/WEB-INF/oldRequests.xslt"/>
<property name="responseXslt" value="/WEB-INF/oldResponses.xslt"/>
</bean>
在前面的示例中,我们使用 转换请求,使用 转换/WEB-INF/oldRequests.xslt
响应消息/WEB-INF/oldResponses.xslt
。请注意,由于端点拦截器是在端点映射级别注册的,因此您可以创建一个应用于“旧式”消息的端点映射并将拦截器添加到该映射中。因此,转换仅适用于这些“旧式”消息。
WsConfigurerAdapter
如前所述,您也可以使用该方法PayloadTransformingInterceptor
。
5.5. 处理异常
Spring-WS 提供EndpointExceptionResolvers
了缓解在匹配请求的端点处理消息时发生意外异常的痛苦。端点异常解析器有点类似于可以在 Web 应用程序描述符中定义的异常映射web.xml
。但是,它们提供了一种更灵活的方式来处理异常。它们提供有关引发异常时调用的端点的信息。此外,处理异常的程序化方式为您提供了更多选项来正确响应。您可以通过任何方式处理异常,而不是通过提供异常和堆栈跟踪来暴露应用程序的内部结构——例如,通过返回带有特定故障代码和字符串的 SOAP 故障。
端点异常解析器由 自动获取MessageDispatcher
,因此不需要显式配置。
除了实现EndpointExceptionResolver
接口(这只是实现resolveException(MessageContext, endpoint, Exception)
方法的问题)之外,您还可以使用提供的实现之一。最简单的实现是SimpleSoapExceptionResolver
,它创建一个 SOAP 1.1 Server 或 SOAP 1.2 Receiver 故障并将异常消息用作故障字符串。这SimpleSoapExceptionResolver
是默认值,但可以通过显式添加另一个解析器来覆盖它。
5.5.1。SoapFaultMappingExceptionResolver
这SoapFaultMappingExceptionResolver
是一个更复杂的实现。此解析器允许您获取可能引发的任何异常的类名并将其映射到 SOAP 故障:
<beans>
<bean id="exceptionResolver"
class="org.springframework.ws.soap.server.endpoint.SoapFaultMappingExceptionResolver">
<property name="defaultFault" value="SERVER"/>
<property name="exceptionMappings">
<value>
org.springframework.oxm.ValidationFailureException=CLIENT,Invalid request
</value>
</property>
</bean>
</beans>
键值和默认端点使用 的格式faultCode,faultString,locale
,其中只需要故障代码。如果未设置故障字符串,则默认为异常消息。如果未设置语言,则默认为英语。上述配置将类型异常映射ValidationFailureException
到故障字符串为 的客户端 SOAP 故障Invalid request
,如下所示:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Body>
<SOAP-ENV:Fault>
<faultcode>SOAP-ENV:Client</faultcode>
<faultstring>Invalid request</faultstring>
</SOAP-ENV:Fault>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
如果发生任何其他异常,则返回默认故障:以异常消息作为故障字符串的服务器端故障。
5.5.2. 使用SoapFaultAnnotationExceptionResolver
您还可以使用注释对异常类进行@SoapFault
注释,以指示在抛出该异常时应返回的 SOAP 错误。要获取这些注释,您需要将 添加SoapFaultAnnotationExceptionResolver
到您的应用程序上下文中。注释的元素包括故障代码枚举、故障字符串或原因以及语言。以下示例显示了此类异常:
package samples;
import org.springframework.ws.soap.server.endpoint.annotation.FaultCode;
import org.springframework.ws.soap.server.endpoint.annotation.SoapFault;
@SoapFault(faultCode = FaultCode.SERVER)
public class MyBusinessException extends Exception {
public MyClientException(String message) {
super(message);
}
}
每当在端点调用期间MyBusinessException
与构造函数字符串一起抛出"Oops!"
时,它都会导致以下响应:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Body>
<SOAP-ENV:Fault>
<faultcode>SOAP-ENV:Server</faultcode>
<faultstring>Oops!</faultstring>
</SOAP-ENV:Fault>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
5.6. 服务器端测试
在测试 Web 服务端点时,您有两种可能的方法:
-
编写单元测试,您可以在其中提供(模拟)参数供端点使用。
这种方法的优点是它很容易完成(尤其是对于用 注释的类
@Endpoint
)。缺点是您没有真正测试通过网络发送的 XML 消息的确切内容。 -
编写集成测试,它会测试消息的内容。
第一种方法可以通过 EasyMock、JMock 等模拟框架轻松实现。下一节重点介绍使用 Spring Web Services 2.0 中引入的测试功能编写集成测试。
5.6.1。编写服务器端集成测试
Spring Web Services 2.0 引入了对创建端点集成测试的支持。在此上下文中,端点是处理 (SOAP) 消息的类(请参阅端点)。
集成测试支持存在于org.springframework.ws.test.server
包中。该包中的核心类是MockWebServiceClient
. 基本思想是该客户端创建一个请求消息,然后将其发送到在标准MessageDispatcherServlet
应用程序上下文中配置的端点(请参阅 参考资料MessageDispatcherServlet
)。这些端点处理消息并创建响应。然后,客户收到此响应并根据注册的期望对其进行验证。
的典型用法MockWebServiceClient
是: .
-
MockWebServiceClient
通过调用MockWebServiceClient.createClient(ApplicationContext)
or创建一个实例MockWebServiceClient.createClient(WebServiceMessageReceiver, WebServiceMessageFactory)
。 -
通过调用发送请求消息
sendRequest(RequestCreator)
,可能使用中RequestCreator
提供的默认实现RequestCreators
(可以静态导入)。 -
通过调用设置响应期望
andExpect(ResponseMatcher)
,可能使用ResponseMatcher
提供的默认实现ResponseMatchers
(可以静态导入)。andExpect(ResponseMatcher)
可以通过链接调用来设置多个期望。
请注意,MockWebServiceClient (和相关的类)提供了“流利的”API,因此您通常可以使用 IDE 中的代码完成功能来指导您完成设置模拟服务器的过程。
|
另请注意,您可以在单元测试中依赖 Spring Web Services 中可用的标准日志记录功能。有时,检查请求或响应消息以找出特定测试失败的原因可能很有用。有关详细信息,请参阅消息记录和跟踪。 |
例如,考虑以下 Web 服务端点类:
import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.RequestPayload;
import org.springframework.ws.server.endpoint.annotation.ResponsePayload;
@Endpoint (1)
public class CustomerEndpoint {
@ResponsePayload (2)
public CustomerCountResponse getCustomerCount( (2)
@RequestPayload CustomerCountRequest request) { (2)
CustomerCountResponse response = new CustomerCountResponse();
response.setCustomerCount(10);
return response;
}
}
1 | CustomerEndpoint 用 注释的in @Endpoint 。请参阅端点。 |
2 | 该getCustomerCount() 方法将 aCustomerCountRequest 作为其参数并返回 a CustomerCountResponse 。这两个类都是编组器支持的对象。例如,它们可以具有@XmlRootElement JAXB2 支持的注释。 |
以下示例显示了 的典型测试CustomerEndpoint
:
import javax.xml.transform.Source;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.xml.transform.StringSource;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.ws.test.server.MockWebServiceClient; (1)
import static org.springframework.ws.test.server.RequestCreators.*; (1)
import static org.springframework.ws.test.server.ResponseMatchers.*; (1)
@RunWith(SpringJUnit4ClassRunner.class) (2)
@ContextConfiguration("spring-ws-servlet.xml") (2)
public class CustomerEndpointIntegrationTest {
@Autowired
private ApplicationContext applicationContext; (3)
private MockWebServiceClient mockClient;
@Before
public void createClient() {
mockClient = MockWebServiceClient.createClient(applicationContext); (4)
}
@Test
public void customerEndpoint() throws Exception {
Source requestPayload = new StringSource(
"<customerCountRequest xmlns='http://springframework.org/spring-ws'>" +
"<customerName>John Doe</customerName>" +
"</customerCountRequest>");
Source responsePayload = new StringSource(
"<customerCountResponse xmlns='http://springframework.org/spring-ws'>" +
"<customerCount>10</customerCount>" +
"</customerCountResponse>");
mockClient.sendRequest(withPayload(requestPayload)). (5)
andExpect(payload(responsePayload)); (5)
}
}
1 | CustomerEndpointIntegrationTest 导入和MockWebServiceClient 静态导入RequestCreators 和ResponseMatchers 。 |
2 | 此测试使用 Spring Framework 中提供的标准测试工具。这不是必需的,但通常是设置测试的最简单方法。 |
3 | 应用程序上下文是标准 Spring-WS 应用程序上下文(请参阅 参考资料MessageDispatcherServlet ),从spring-ws-servlet.xml . 在这种情况下,应用程序上下文包含一个 bean 定义CustomerEndpoint (或者可能<context:component-scan /> 使用了 a)。 |
4 | 在一个方法中,我们使用工厂方法@Before 创建一个。MockWebServiceClient createClient |
5 | 我们通过调用静态导入提供的sendRequest() a来发送请求(请参阅使用和)。
withPayload() RequestCreator RequestCreators RequestCreator RequestCreators 我们还通过调用静态导入提供的 这部分测试可能看起来有点混乱,但 IDE 的代码完成功能很有帮助。键入 后 |
5.6.2. 使用RequestCreator
和RequestCreators
最初,MockWebServiceClient
需要创建一个请求消息供端点消费。RequestCreator
客户端为此目的使用策略接口:
public interface RequestCreator {
WebServiceMessage createRequest(WebServiceMessageFactory messageFactory)
throws IOException;
}
您可以编写自己的此接口实现,使用消息工厂创建请求消息,但您当然不必这样做。该类RequestCreators
提供了一种RequestCreator
基于方法中给定有效负载创建的withPayload()
方法。您通常静态导入RequestCreators
.
5.6.3. 使用ResponseMatcher
和ResponseMatchers
当请求消息已经被端点处理并接收到响应时,MockWebServiceClient
可以验证该响应消息是否满足某些期望。ResponseMatcher
客户端为此目的使用策略接口:
public interface ResponseMatcher {
void match(WebServiceMessage request,
WebServiceMessage response)
throws IOException, AssertionError;
}
再一次,您可以编写自己的此接口实现,AssertionError
当消息不符合您的期望时抛出实例,但您当然不必这样做,因为ResponseMatchers
该类提供了标准ResponseMatcher
实现供您在测试中使用。您通常静态导入此类。
该类ResponseMatchers
提供以下响应匹配器:
ResponseMatchers 方法 |
描述 |
---|---|
|
期望给定的响应负载。 |
|
期望响应有效负载针对给定的 XSD 架构进行验证。 |
|
期望给定的 XPath 表达式存在、不存在或计算为给定值。 |
|
期望响应消息中存在给定的 SOAP 标头。 |
|
期望响应消息不包含 SOAP 错误。 |
|
期望响应消息包含特定的 SOAP 故障。 |
andExpect()
您可以通过链接调用来设置多个响应预期:
mockClient.sendRequest(...).
andExpect(payload(expectedResponsePayload)).
andExpect(validPayload(schemaResource));
有关由 提供的响应匹配器的更多信息ResponseMatchers
,请参阅Javadoc。
6. 在客户端使用 Spring Web Services
Spring-WS 提供了一个客户端 Web 服务 API,它允许对 Web 服务进行一致的、XML 驱动的访问。它还迎合编组器和解组器的使用,以便您的服务层代码可以专门处理 Java 对象。
该org.springframework.ws.client.core
包提供了使用客户端访问 API 的核心功能。它包含简化 Web 服务使用的模板类,就像 SpringJdbcTemplate
为 JDBC 所做的核心一样。Spring 模板类的共同设计原则是提供帮助方法来执行常见操作,并且对于更复杂的用法,委托给用户实现的回调接口。Web 服务模板遵循相同的设计。这些类提供了各种方便的方法
-
发送和接收 XML 消息
-
在发送之前将对象编组为 XML
-
允许多种运输方式
6.1。使用客户端 API
本节介绍如何使用客户端 API。有关如何使用服务器端 API,请参阅使用 Spring-WS 创建 Web 服务。
6.1.1.WebServiceTemplate
WebServiceTemplate
是 Spring-WS 中客户端 Web 服务访问的核心类。它包含用于发送Source
对象和接收响应消息的方法Source
或Result
。此外,它可以在通过传输发送对象之前将对象编组为 XML,并将任何响应 XML 再次解组为对象。
URI 和传输
该类WebServiceTemplate
使用 URI 作为消息目的地。您可以defaultUri
在模板本身上设置属性,也可以在调用模板上的方法时显式提供 URI。URI 被解析为WebServiceMessageSender
,它负责跨传输层发送 XML 消息。messageSender
您可以使用类的或messageSenders
属性设置一个或多个消息发送者WebServiceTemplate
。
HTTP 传输
WebServiceMessageSender
通过 HTTP 发送消息的接口有两种实现。默认实现是HttpUrlConnectionMessageSender
,它使用 Java 本身提供的工具。另一种方法是HttpComponentsMessageSender
,它使用Apache HttpComponents HttpClient。如果您需要更高级且易于使用的功能(例如身份验证、HTTP 连接池等),请使用后者。
要使用 HTTP 传输,请将 设置defaultUri
为类似http://example.com/services
或提供uri
其中一种方法的参数。
以下示例显示如何使用 HTTP 传输的默认配置:
<beans>
<bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory"/>
<bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
<constructor-arg ref="messageFactory"/>
<property name="defaultUri" value="http://example.com/WebService"/>
</bean>
</beans>
以下示例显示如何覆盖默认配置以及如何使用 Apache HttpClient 通过 HTTP 身份验证进行身份验证:
<bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
<constructor-arg ref="messageFactory"/>
<property name="messageSender">
<bean class="org.springframework.ws.transport.http.HttpComponentsMessageSender">
<property name="credentials">
<bean class="org.apache.http.auth.UsernamePasswordCredentials">
<constructor-arg value="john:secret"/>
</bean>
</property>
</bean>
</property>
<property name="defaultUri" value="http://example.com/WebService"/>
</bean>
JMS 传输
为了通过 JMS 发送消息,Spring Web Services 提供了JmsMessageSender
. 此类使用 Spring 框架的工具将 转换WebServiceMessage
为 JMS ,在orMessage
上发送它,并接收响应(如果有)。Queue
Topic
要使用JmsMessageSender
,您需要将defaultUri
oruri
参数设置为 JMS URI,它至少包含jms:
前缀和目标名称。JMS URI 的一些示例是:jms:SomeQueue
、jms:SomeTopic?priority=3&deliveryMode=NON_PERSISTENT
和jms:RequestQueue?replyToName=ResponseName
. 有关此 URI 语法的更多信息,请参阅JmsMessageSender
.
默认情况下,JmsMessageSender
发送 JMS ,但您可以使用 JMS URI 上的参数BytesMessage
覆盖它以使用- 例如,. 请注意,这是首选类型,因为不可靠地支持附件和字符编码。TextMessages
messageType
jms:Queue?messageType=TEXT_MESSAGE
BytesMessages
TextMessages
以下示例显示了如何将 JMS 传输与 ActiveMQ 连接工厂结合使用:
<beans>
<bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory"/>
<bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="vm://localhost?broker.persistent=false"/>
</bean>
<bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
<constructor-arg ref="messageFactory"/>
<property name="messageSender">
<bean class="org.springframework.ws.transport.jms.JmsMessageSender">
<property name="connectionFactory" ref="connectionFactory"/>
</bean>
</property>
<property name="defaultUri" value="jms:RequestQueue?deliveryMode=NON_PERSISTENT"/>
</bean>
</beans>
电子邮件传输
Spring Web Services 还提供了电子邮件传输,您可以使用它通过 SMTP 发送 Web 服务消息并通过 POP3 或 IMAP 检索它们。客户端电子邮件功能包含在MailMessageSender
该类中。此类根据请求创建电子邮件消息WebServiceMessage
并通过 SMTP 发送。然后它等待响应消息到达传入的 POP3 或 IMAP 服务器。
要使用MailMessageSender
,请将defaultUri
oruri
参数设置为mailto
URI - 例如,mailto:john@example.com
or mailto:server@localhost?subject=SOAP%20Test
。确保消息发送者正确配置了 a transportUri
,它指示用于发送请求的服务器(通常是 SMTP 服务器)和 a storeUri
,它指示轮询响应的服务器(通常是 POP3 或 IMAP 服务器)。
以下示例显示了如何使用电子邮件传输:
<beans>
<bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory"/>
<bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
<constructor-arg ref="messageFactory"/>
<property name="messageSender">
<bean class="org.springframework.ws.transport.mail.MailMessageSender">
<property name="from" value="Spring-WS SOAP Client <client@example.com>"/>
<property name="transportUri" value="smtp://client:s04p@smtp.example.com"/>
<property name="storeUri" value="imap://client:s04p@imap.example.com/INBOX"/>
</bean>
</property>
<property name="defaultUri" value="mailto:server@example.com?subject=SOAP%20Test"/>
</bean>
</beans>
XMPP 传输
Spring Web Services 2.0 引入了 XMPP (Jabber) 传输,您可以使用它通过 XMPP 发送和接收 Web 服务消息。客户端 XMPP 功能包含在XmppMessageSender
该类中。此类从请求中创建 XMPP 消息WebServiceMessage
并通过 XMPP 发送它。然后它侦听响应消息的到达。
要使用XmppMessageSender
,请将defaultUri
oruri
参数设置为xmpp
URI - 例如xmpp:johndoe@jabber.org
. 发件人还需要一个XMPPConnection
to work,可以使用org.springframework.ws.transport.xmpp.support.XmppConnectionFactoryBean
.
以下示例显示了如何使用 XMPP 传输:
<beans>
<bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory"/>
<bean id="connection" class="org.springframework.ws.transport.xmpp.support.XmppConnectionFactoryBean">
<property name="host" value="jabber.org"/>
<property name="username" value="username"/>
<property name="password" value="password"/>
</bean>
<bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
<constructor-arg ref="messageFactory"/>
<property name="messageSender">
<bean class="org.springframework.ws.transport.xmpp.XmppMessageSender">
<property name="connection" ref="connection"/>
</bean>
</property>
<property name="defaultUri" value="xmpp:user@jabber.org"/>
</bean>
</beans>
6.1.2. 发送和接收WebServiceMessage
包含许多方便的WebServiceTemplate
方法来发送和接收 Web 服务消息。有接受和返回 a 的方法和返回 a 的Source
方法Result
。此外,还有一些方法可以将对象编组和解组到 XML。以下示例将简单的 XML 消息发送到 Web 服务:
import java.io.StringReader;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.springframework.ws.WebServiceMessageFactory;
import org.springframework.ws.client.core.WebServiceTemplate;
import org.springframework.ws.transport.WebServiceMessageSender;
public class WebServiceClient {
private static final String MESSAGE =
"<message xmlns=\"http://tempuri.org\">Hello, Web Service World</message>";
private final WebServiceTemplate webServiceTemplate = new WebServiceTemplate();
public void setDefaultUri(String defaultUri) {
webServiceTemplate.setDefaultUri(defaultUri);
}
// send to the configured default URI
public void simpleSendAndReceive() {
StreamSource source = new StreamSource(new StringReader(MESSAGE));
StreamResult result = new StreamResult(System.out);
webServiceTemplate.sendSourceAndReceiveToResult(source, result);
}
// send to an explicit URI
public void customSendAndReceive() {
StreamSource source = new StreamSource(new StringReader(MESSAGE));
StreamResult result = new StreamResult(System.out);
webServiceTemplate.sendSourceAndReceiveToResult("http://localhost:8080/AnotherWebService",
source, result);
}
}
<beans xmlns="http://www.springframework.org/schema/beans">
<bean id="webServiceClient" class="WebServiceClient">
<property name="defaultUri" value="http://localhost:8080/WebService"/>
</bean>
</beans>
前面的示例使用WebServiceTemplate
向位于http://localhost:8080/WebService
(在simpleSendAndReceive()
方法的情况下)的 Web 服务发送“Hello, World”消息并将结果写入控制台。使用WebServiceTemplate
默认 URI 注入,因为在 Java 代码中没有显式提供 URI。
请注意,WebServiceTemplate
一旦配置该类是线程安全的(假设它的所有依赖项也是线程安全的,Spring-WS 附带的所有依赖项都是这种情况),因此多个对象可以使用同一个共享WebServiceTemplate
实例。公开了WebServiceTemplate
一个零参数构造函数messageFactory
和messageSender
bean 属性,您可以使用它们来构造实例(通过使用 Spring 容器或纯 Java 代码)。或者,考虑从 Spring-WS 的便利基类派生WebServiceGatewaySupport
,它公开了便利的 bean 属性以实现轻松配置。(您不必扩展此基类。它仅作为便利类提供。)
6.1.3. 发送和接收 POJO——编组和解组
为了便于发送普通的 Java 对象,WebServiceTemplate
有许多send(..)
方法可以将 anObject
作为消息数据内容的参数。marshalSendAndReceive(..)
类中的方法WebServiceTemplate
将请求对象到 XML 的转换委托给 aMarshaller
并将响应 XML 到对象的转换委托给Unmarshaller
. (有关 marshalling 和 unmarshaller 的更多信息,请参阅Spring Framework 参考文档。)通过使用 marshaller,您的应用程序代码可以专注于正在发送或接收的业务对象,而不必关心它如何表示为的细节XML。marshaller
要使用编组功能,您必须使用和unmarshaller
属性设置编组器和解组器WebServiceTemplate
班级。
6.1.4。使用WebServiceMessageCallback
为了适应对消息的 SOAP 标头和其他设置的设置,该WebServiceMessageCallback
界面允许您在创建消息之后但在发送之前访问该消息。以下示例演示如何在通过编组对象创建的消息上设置 SOAP 操作标头:
public void marshalWithSoapActionHeader(MyObject o) {
webServiceTemplate.marshalSendAndReceive(o, new WebServiceMessageCallback() {
public void doWithMessage(WebServiceMessage message) {
((SoapMessage)message).setSoapAction("http://tempuri.org/Action");
}
});
}
请注意,您还可以使用org.springframework.ws.soap.client.core.SoapActionCallback 设置 SOAP 操作标头。
|
WS-寻址
除了服务器端的 WS-Addressing支持之外,Spring Web Services 在客户端也支持这个规范。
要在客户端上设置 WS-Addressing 标头,您可以使用org.springframework.ws.soap.addressing.client.ActionCallback
. 此回调将所需的操作标头作为参数。它还具有用于指定 WS-Addressing 版本和To
标头的构造函数。如果未指定,则To
标头默认为正在建立的连接的 URL。
以下示例将Action
标头设置为http://samples/RequestOrder
:
webServiceTemplate.marshalSendAndReceive(o, new ActionCallback("http://samples/RequestOrder"));
6.1.5。使用WebServiceMessageExtractor
该WebServiceMessageExtractor
接口是一个低级回调接口,您可以完全控制Object
从接收到的WebServiceMessage
. 当与服务资源的底层连接仍然打开时,WebServiceTemplate
调用extractData(..)
提供的方法。WebServiceMessageExtractor
以下示例显示了WebServiceMessageExtractor
实际操作:
public void marshalWithSoapActionHeader(final Source s) {
final Transformer transformer = transformerFactory.newTransformer();
webServiceTemplate.sendAndReceive(new WebServiceMessageCallback() {
public void doWithMessage(WebServiceMessage message) {
transformer.transform(s, message.getPayloadResult());
},
new WebServiceMessageExtractor() {
public Object extractData(WebServiceMessage message) throws IOException {
// do your own transforms with message.getPayloadResult()
// or message.getPayloadSource()
}
}
});
}
6.2. 客户端测试
在测试您的 Web 服务客户端(即使用WebServiceTemplate
来访问 Web 服务的类)时,您有两种可能的方法:
-
编写单元测试,模拟
WebServiceTemplate
类、WebServiceOperations
接口或完整的客户端类。这种方法的优点是很容易实现。缺点是您并没有真正测试通过网络发送的 XML 消息的确切内容,尤其是在模拟整个客户端类时。
-
编写集成测试,它会测试消息的内容。
第一种方法可以通过模拟框架轻松完成,例如 EasyMock、JMock 等。下一节重点介绍使用 Spring Web Services 2.0 中引入的测试功能编写集成测试。
6.2.1. 编写客户端集成测试
Spring Web Services 2.0 引入了对创建 Web 服务客户端集成测试的支持。在这种情况下,客户端是一个使用WebServiceTemplate
来访问 Web 服务的类。
集成测试支持存在于org.springframework.ws.test.client
包中。该包中的核心类是MockWebServiceServer
. 基本思想是 Web 服务模板连接到该模拟服务器并向其发送请求消息,然后模拟服务器根据注册的期望验证该请求消息。如果满足预期,模拟服务器然后准备响应消息,将其发送回模板。
的典型用法MockWebServiceServer
是: .
-
MockWebServiceServer
通过调用MockWebServiceServer.createServer(WebServiceTemplate)
、MockWebServiceServer.createServer(WebServiceGatewaySupport)
或来创建实例MockWebServiceServer.createServer(ApplicationContext)
。 -
通过调用设置请求期望
expect(RequestMatcher)
,可能使用RequestMatcher
提供的默认实现RequestMatchers
(可以静态导入)。andExpect(RequestMatcher)
可以通过链接调用来设置多个期望。 -
通过调用创建适当的响应消息
andRespond(ResponseCreator)
,可能使用ResponseCreator
提供的默认实现ResponseCreators
(可以静态导入)。 -
WebServiceTemplate
照常使用,直接或通过客户端代码。 -
打电话
MockWebServiceServer.verify()
以确保所有期望都得到满足。
请注意,MockWebServiceServer (和相关的类)提供了一个“流利的”API,因此您通常可以使用 IDE 中的代码完成功能来指导您完成设置模拟服务器的过程。
|
另请注意,您可以在单元测试中依赖 Spring Web Services 中可用的标准日志记录功能。有时,检查请求或响应消息以找出特定测试失败的原因可能很有用。有关详细信息,请参阅消息记录和跟踪。 |
例如,考虑以下 Web 服务客户端类:
import org.springframework.ws.client.core.support.WebServiceGatewaySupport;
public class CustomerClient extends WebServiceGatewaySupport { (1)
public int getCustomerCount() {
CustomerCountRequest request = new CustomerCountRequest(); (2)
request.setCustomerName("John Doe");
CustomerCountResponse response =
(CustomerCountResponse) getWebServiceTemplate().marshalSendAndReceive(request); (3)
return response.getCustomerCount();
}
}
1 | CustomerClient extends WebServiceGatewaySupport ,它为它提供了一个属性webServiceTemplate 。 |
2 | CustomerCountRequest 是由编组器支持的对象。例如,它可以具有@XmlRootElement JAXB2 支持的注释。 |
3 | 使用提供的CustomerClient 将请求对象编组为 SOAP 消息并将其发送到 Web 服务。响应对象被解组为.WebServiceTemplate WebServiceGatewaySupport CustomerCountResponse |
以下示例显示了 的典型测试CustomerClient
:
import javax.xml.transform.Source;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.xml.transform.StringSource;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.assertEquals;
import org.springframework.ws.test.client.MockWebServiceServer; (1)
import static org.springframework.ws.test.client.RequestMatchers.*; (1)
import static org.springframework.ws.test.client.ResponseCreators.*; (1)
@RunWith(SpringJUnit4ClassRunner.class) (2)
@ContextConfiguration("integration-test.xml") (2)
public class CustomerClientIntegrationTest {
@Autowired
private CustomerClient client; (3)
private MockWebServiceServer mockServer; (4)
@Before
public void createServer() throws Exception {
mockServer = MockWebServiceServer.createServer(client);
}
@Test
public void customerClient() throws Exception {
Source requestPayload = new StringSource(
"<customerCountRequest xmlns='http://springframework.org/spring-ws'>" +
"<customerName>John Doe</customerName>" +
"</customerCountRequest>");
Source responsePayload = new StringSource(
"<customerCountResponse xmlns='http://springframework.org/spring-ws'>" +
"<customerCount>10</customerCount>" +
"</customerCountResponse>");
mockServer.expect(payload(requestPayload)).andRespond(withPayload(responsePayload));(5)
int result = client.getCustomerCount(); (6)
assertEquals(10, result); (6)
mockServer.verify(); (7)
}
}
1 | CustomerClientIntegrationTest 导入和MockWebServiceServer 静态导入RequestMatchers 和ResponseCreators 。 |
2 | 此测试使用 Spring Framework 中提供的标准测试工具。这不是必需的,但通常是设置测试的最简单方法。 |
3 | 使用. _ CustomerClient _integration-test.xml @Autowired |
4 | 在一个方法中,我们使用工厂方法@Before 创建一个。MockWebServiceServer createServer |
5 | 我们通过调用静态导入提供的expect() a来定义期望(请参阅使用and)。
payload() RequestMatcher RequestMatchers RequestMatcher RequestMatchers 我们还通过调用静态导入提供的 这部分测试可能看起来有点混乱,但 IDE 的代码完成功能很有帮助。键入 后 |
6 | 我们调用getCustomerCount() ,CustomerClient 因此使用WebServiceTemplate 。模板现在已经设置为“测试模式”,所以这个方法调用没有建立真正的(HTTP)连接。我们还根据方法调用的结果做出一些 JUnit 断言。 |
7 | 我们调用verify() ,MockWebServiceServer 验证是否确实收到了预期的消息。 |
6.2.2. 使用RequestMatcher
和RequestMatchers
为了验证请求消息是否满足某些期望,MockWebServiceServer
使用RequestMatcher
策略接口。该接口定义的合约如下:
public interface RequestMatcher {
void match(URI uri,
WebServiceMessage request)
throws IOException,
AssertionError;
}
您可以编写自己的此接口实现,AssertionError
当消息不符合您的期望时抛出异常,但您当然不必这样做。该类RequestMatchers
提供标准RequestMatcher
实现供您在测试中使用。您通常静态导入此类。
该类RequestMatchers
提供以下请求匹配器:
RequestMatchers 方法 |
描述 |
---|---|
|
期待任何类型的请求。 |
|
期望给定的请求有效负载。 |
|
期望请求有效负载针对给定的 XSD 架构进行验证。 |
|
期望给定的 XPath 表达式存在、不存在或计算为给定值。 |
|
期望给定的 SOAP 标头存在于请求消息中。 |
|
期望与给定 URL 的连接。 |
andExpect()
您可以通过链接调用来设置多个请求预期:
mockServer.expect(connectionTo("http://example.com")).
andExpect(payload(expectedRequestPayload)).
andExpect(validPayload(schemaResource)).
andRespond(...);
有关由 提供的请求匹配器的更多信息RequestMatchers
,请参阅Javadoc。
6.2.3. 使用ResponseCreator
和ResponseCreators
当请求消息已被验证并满足定义的期望时,MockWebServiceServer
创建响应消息以供WebServiceTemplate
使用。服务器ResponseCreator
为此使用策略接口:
public interface ResponseCreator {
WebServiceMessage createResponse(URI uri,
WebServiceMessage request,
WebServiceMessageFactory messageFactory)
throws IOException;
}
同样,您可以编写自己的此接口实现,使用消息工厂创建响应消息,但您当然不必这样做,因为ResponseCreators
该类提供了标准ResponseCreator
实现供您在测试中使用。您通常静态导入此类。
该类ResponseCreators
提供以下响应:
ResponseCreators 方法 |
描述 |
---|---|
|
创建具有给定有效负载的响应消息。 |
|
在响应连接中创建错误。此方法使您有机会测试您的错误处理。 |
|
从响应连接读取时引发异常。此方法使您有机会测试您的异常处理。 |
|
创建具有给定 SOAP 错误的响应消息。此方法使您有机会测试您的故障处理。 |
有关由 提供的请求匹配器的更多信息RequestMatchers
,请参阅Javadoc。
7. 使用 Spring-WS 保护您的 Web 服务
本章说明如何将 WS-Security 方面添加到您的 Web 服务中。我们专注于 WS-Security 的三个不同领域:
-
身份验证:这是确定委托人是否是他们声称的身份的过程。在这种情况下,“主体”通常是指可以在您的应用程序中执行操作的用户、设备或其他一些系统。
-
数字签名:消息的数字签名是基于文档和签名者私钥的一条信息。它是通过使用散列函数和私有签名函数(使用签名者的私钥加密)创建的。
-
加密和解密:加密是将数据转换为没有适当密钥就无法读取的形式的过程。它主要用于将信息隐藏起来,不让任何人知道。解密是加密的逆过程。这是将加密数据转换回可读形式的过程。
这三个区域是通过使用XwsSecurityInterceptor
or来实现的,我们分别Wss4jSecurityInterceptor
在XwsSecurityInterceptor
和Using中进行了描述Wss4jSecurityInterceptor
请注意,WS-Security(尤其是加密和签名)需要大量内存并且会降低性能。如果性能对您很重要,您可能要考虑不使用 WS-Security 或使用基于 HTTP 的安全性。 |
7.1。XwsSecurityInterceptor
这XwsSecurityInterceptor
是一个基于 SUN 的 XML 和 Web 服务安全包 (XWSS)的EndpointInterceptor
(请参阅拦截请求 -EndpointInterceptor
接口)。此 WS-Security 实现是 Java Web Services Developer Pack ( Java WSDP ) 的一部分。
与任何其他端点拦截器一样,它在端点映射中定义(请参阅端点映射)。这意味着您可以选择性地添加 WS-Security 支持。一些端点映射需要它,而另一些则不需要。
请注意,XWSS 需要 SUN 1.5 JDK 和 SUN SAAJ 参考实现。WSS4J 拦截器没有这些要求(请参阅使用Wss4jSecurityInterceptor )。
|
需要一个XwsSecurityInterceptor
安全策略文件才能运行。这个 XML 文件告诉拦截器需要从传入 SOAP 消息中获得哪些安全方面以及要向传出消息添加哪些方面。策略文件的基本格式将在以下部分进行说明,但您可以在此处找到更深入的教程。您可以使用policyConfiguration
需要 Spring 资源的属性设置策略。策略文件可以包含多个元素——例如,要求传入消息的用户名令牌并签署所有传出消息。它包含一个SecurityConfiguration
元素(不是JAXRPCSecurity
元素)作为其根。
此外,安全拦截器需要一个或多个CallbackHandler
实例才能运行。这些处理程序用于检索证书、私钥、验证用户凭据等。Spring-WS 为最常见的安全问题提供了处理程序——例如,针对 Spring Security 身份验证管理器进行身份验证和基于 X509 证书签署传出消息。以下部分说明了针对哪个安全问题使用哪个回调处理程序。您可以使用callbackHandler
orcallbackHandlers
属性设置回调处理程序。
以下示例显示了如何连接XwsSecurityInterceptor
:
<beans>
<bean id="wsSecurityInterceptor"
class="org.springframework.ws.soap.security.xwss.XwsSecurityInterceptor">
<property name="policyConfiguration" value="classpath:securityPolicy.xml"/>
<property name="callbackHandlers">
<list>
<ref bean="certificateHandler"/>
<ref bean="authenticationHandler"/>
</list>
</property>
</bean>
...
</beans>
这个拦截器是通过使用securityPolicy.xml
类路径上的文件来配置的。它使用两个稍后在文件中定义的回调处理程序。
7.1.1. 密钥库
对于大多数加密操作,您可以使用标准java.security.KeyStore
对象。这些操作包括证书验证、消息签名、签名验证和加密。它们不包括用户名和时间戳验证。本节旨在为您提供一些有关密钥库的背景知识以及可用于将密钥和证书存储在密钥库文件中的 Java 工具。这些信息大多与 Spring-WS 无关,但与 Java 的一般加密特性有关。
该类java.security.KeyStore
表示加密密钥和证书的存储设施。它可以包含三种不同类型的元素:
-
私钥:这些密钥用于自我认证。私钥伴随着对应公钥的证书链。在 WS-Security 领域内,这说明了消息签名和消息解密。
-
对称密钥:对称(或秘密)密钥也用于消息加密和解密——不同之处在于双方(发送方和接收方)共享相同的密钥。
-
受信任的证书:这些 X509 证书被称为“受信任的证书”,因为密钥库所有者相信证书中的公钥确实属于证书的所有者。在 WS-Security 中,这些证书用于证书验证、签名验证和加密。
使用keytool
该keytool
程序是一个密钥和证书管理实用程序,随您的 Java 虚拟机一起提供。您可以使用此工具创建新的密钥库、向其中添加新的私钥和证书,等等。提供该命令的完整参考超出了本文档的范围,但您可以在此处或通过在命令行上使用该命令keytool
找到参考。keytool -help
使用KeyStoreFactoryBean
要使用 Spring 配置轻松加载密钥库,您可以使用KeyStoreFactoryBean
. 它有一个资源位置属性,您可以将其设置为指向要加载的密钥库的路径。可以提供密码来检查密钥库数据的完整性。如果没有给出密码,则不执行完整性检查。以下清单配置了一个KeyStoreFactoryBean
:
<bean id="keyStore" class="org.springframework.ws.soap.security.support.KeyStoreFactoryBean">
<property name="password" value="password"/>
<property name="location" value="classpath:org/springframework/ws/soap/security/xwss/test-keystore.jks"/>
</bean>
如果您不指定 location 属性,则会创建一个新的空密钥库,这很可能不是您想要的。 |
KeyStoreCallbackHandler
要在 a 中使用密钥库XwsSecurityInterceptor
,您需要定义一个KeyStoreCallbackHandler
. 此回调具有三个类型为keystore
: ( keyStore
、trustStore
和symmetricStore
) 的属性。处理程序使用的确切存储取决于此处理程序要执行的加密操作。对于私钥操作,keyStore
使用 。对于对称密钥操作,symmetricStore
使用 。为了确定信任关系,trustStore
使用 。下表表明了这一点:
密码操作 | 使用的密钥库 |
---|---|
证书验证 |
首先 |
基于私钥解密 |
|
基于对称密钥的解密 |
|
基于公钥证书的加密 |
|
基于对称密钥的加密 |
|
签约 |
|
签名验证 |
|
此外,它KeyStoreCallbackHandler
还有一个privateKeyPassword
属性,应该设置它来解锁包含在“keyStore”中的私钥。
如果symmetricStore
未设置,则默认为keyStore
。如果未设置密钥或信任库,则回调处理程序使用标准 Java 机制来加载或创建它。请参阅 JavaDocKeyStoreCallbackHandler
以了解此机制的工作原理。
例如,如果您想使用KeyStoreCallbackHandler
来验证传入的证书或签名,您可以使用信任库:
<beans>
<bean id="keyStoreHandler" class="org.springframework.ws.soap.security.xwss.callback.KeyStoreCallbackHandler">
<property name="trustStore" ref="trustStore"/>
</bean>
<bean id="trustStore" class="org.springframework.ws.soap.security.support.KeyStoreFactoryBean">
<property name="location" value="classpath:truststore.jks"/>
<property name="password" value="changeit"/>
</bean>
</beans>
如果你想用它来解密传入的证书或签署传出的消息,你可以使用密钥库:
<beans>
<bean id="keyStoreHandler" class="org.springframework.ws.soap.security.xwss.callback.KeyStoreCallbackHandler">
<property name="keyStore" ref="keyStore"/>
<property name="privateKeyPassword" value="changeit"/>
</bean>
<bean id="keyStore" class="org.springframework.ws.soap.security.support.KeyStoreFactoryBean">
<property name="location" value="classpath:keystore.jks"/>
<property name="password" value="changeit"/>
</bean>
</beans>
以下部分说明了KeyStoreCallbackHandler
可以在何处使用以及为特定加密操作设置哪些属性。
7.1.2. 验证
正如本章介绍中所述,身份验证是确定委托人是否是他们声称的身份的任务。在 WS-Security 中,身份验证可以采用两种形式:使用用户名和密码令牌(使用纯文本密码或密码摘要)或使用 X509 证书。
纯文本用户名认证
最简单的用户名身份验证形式使用纯文本密码。在这种情况下,SOAP 消息包含一个UsernameToken
元素,该元素本身包含一个Username
元素和一个Password
包含纯文本密码的元素。可以将纯文本身份验证与 HTTP 服务器提供的基本身份验证进行比较。
请注意,纯文本密码不是很安全。因此,如果您使用它们(例如,使用 HTTPS 而不是纯 HTTP),则应始终向传输层添加额外的安全措施。 |
要要求每条传入消息都包含UsernameToken
带有纯文本密码的 a,安全策略文件应包含一个RequireUsernameToken
元素,其passwordDigestRequired
属性设置为false
. 您可以在此处找到可能的子元素的参考。以下清单显示了如何包含RequireUsernameToken
元素:
<xwss:SecurityConfiguration xmlns:xwss="http://java.sun.com/xml/ns/xwss/config">
...
<xwss:RequireUsernameToken passwordDigestRequired="false" nonceRequired="false"/>
...
</xwss:SecurityConfiguration>
如果用户名令牌不存在,则XwsSecurityInterceptor
向发送者返回一个 SOAP 错误。如果它存在,它会向已注册的处理程序触发PasswordValidationCallback
a 。PlainTextPasswordRequest
在 Spring-WS 中,有三个类处理这个特定的回调。
使用SimplePasswordValidationCallbackHandler
最简单的密码验证处理程序是SimplePasswordValidationCallbackHandler
. 此处理程序根据内存Properties
对象验证密码,您可以使用以下users
属性指定该对象:
<bean id="passwordValidationHandler"
class="org.springframework.ws.soap.security.xwss.callback.SimplePasswordValidationCallbackHandler">
<property name="users">
<props>
<prop key="Bert">Ernie</prop>
</props>
</property>
</bean>
在这种情况下,我们只允许用户“Bert”使用密码“Ernie”登录。
使用SpringPlainTextPasswordValidationCallbackHandler
使用SpringPlainTextPasswordValidationCallbackHandler
Spring Security对用户进行身份验证。描述 Spring Security 超出了本文档的范围,但它是一个成熟的安全框架。您可以在Spring Security 参考文档中阅读有关它的更多信息。
SpringPlainTextPasswordValidationCallbackHandler
需要一个AuthenticationManager
操作。它使用此管理器UsernamePasswordAuthenticationToken
对其创建的 a 进行身份验证。如果身份验证成功,则令牌存储在SecurityContextHolder
. 您可以使用以下authenticationManager
属性设置身份验证管理器:
<beans>
<bean id="springSecurityHandler"
class="org.springframework.ws.soap.security.xwss.callback.SpringPlainTextPasswordValidationCallbackHandler">
<property name="authenticationManager" ref="authenticationManager"/>
</bean>
<bean id="authenticationManager" class="org.springframework.security.providers.ProviderManager">
<property name="providers">
<bean class="org.springframework.security.providers.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="userDetailsService"/>
</bean>
</property>
</bean>
<bean id="userDetailsService" class="com.mycompany.app.dao.UserDetailService" />
...
</beans>
使用JaasPlainTextPasswordValidationCallbackHandler
它JaasPlainTextPasswordValidationCallbackHandler
基于标准的Java 身份验证和授权服务。提供对 JAAS 的完整介绍超出了本文档的范围,但提供了一个很好的教程。
JaasPlainTextPasswordValidationCallbackHandler
只需一个loginContextName
即可操作。LoginContext
它使用此名称创建一个新的 JAAS ,并处理标准 JAAS NameCallback
,并PasswordCallback
使用 SOAP 消息中提供的用户名和密码。这意味着此回调处理程序与LoginModule
在该login()
阶段触发这些回调的任何 JAAS 集成,这是标准行为。
您可以JaasPlainTextPasswordValidationCallbackHandler
按如下方式连接:
<bean id="jaasValidationHandler"
class="org.springframework.ws.soap.security.xwss.callback.jaas.JaasPlainTextPasswordValidationCallbackHandler">
<property name="loginContextName" value="MyLoginModule" />
</bean>
在这种情况下,回调处理程序使用LoginContext
命名的MyLoginModule
. 这个模块应该在您的jaas.config
文件中定义,如前面提到的教程中所述。
摘要用户名认证
使用密码摘要时,SOAP 消息还包含一个UsernameToken
元素,该元素本身包含一个Username
元素和一个Password
元素。不同之处在于密码不是作为纯文本发送,而是作为摘要发送。接收者将此摘要与他根据用户的已知密码计算的摘要进行比较,如果它们相同,则对用户进行身份验证。此方法与 HTTP 服务器提供的摘要式身份验证相当。
要要求每条传入消息都包含一个UsernameToken
带有密码摘要的元素,安全策略文件应该包含一个RequireUsernameToken
元素,其passwordDigestRequired
属性设置为true
. 此外,该nonceRequired
属性应设置为:您可以在此处true
找到可能的子元素的引用。以下清单显示了如何定义元素:RequireUsernameToken
<xwss:SecurityConfiguration xmlns:xwss="http://java.sun.com/xml/ns/xwss/config">
...
<xwss:RequireUsernameToken passwordDigestRequired="true" nonceRequired="true"/>
...
</xwss:SecurityConfiguration>
如果用户名令牌不存在,则XwsSecurityInterceptor
向发送者返回一个 SOAP 错误。如果它存在,它会向已注册的处理程序触发PasswordValidationCallback
a 。DigestPasswordRequest
在 Spring-WS 中,有两个类处理这个特定的回调:SimplePasswordValidationCallbackHandler
和SpringDigestPasswordValidationCallbackHandler
.
使用SimplePasswordValidationCallbackHandler
SimplePasswordValidationCallbackHandler
可以处理纯文本密码和密码摘要。它在使用SimplePasswordValidationCallbackHandler
中进行了描述。
使用SpringDigestPasswordValidationCallbackHandler
SpringDigestPasswordValidationCallbackHandler
需要 Spring SecurityUserDetailService
才能运行。它使用此服务来检索令牌中指定的用户的密码。然后将此详细信息对象中包含的密码摘要与消息中的摘要进行比较。如果它们相等,则用户已成功通过身份验证,并且 aUsernamePasswordAuthenticationToken
存储在SecurityContextHolder
. 您可以使用该userDetailsService
属性设置服务。此外,您可以设置一个userCache
属性,以缓存加载的用户详细信息。以下示例显示了如何执行此操作:
<beans>
<bean class="org.springframework.ws.soap.security.xwss.callback.SpringDigestPasswordValidationCallbackHandler">
<property name="userDetailsService" ref="userDetailsService"/>
</bean>
<bean id="userDetailsService" class="com.mycompany.app.dao.UserDetailService" />
...
</beans>
证书认证
更安全的身份验证方式使用 X509 证书。在这种情况下,SOAP 消息包含一个`BinarySecurityToken`,它包含一个 X509 证书的 Base 64 编码版本。接收方使用证书进行身份验证。存储在消息中的证书也用于对消息进行签名(请参阅验证签名)。
为了确保所有传入的 SOAP 消息都带有一个`BinarySecurityToken`,安全策略文件应该包含一个RequireSignature
元素。该元素可以进一步携带其他元素,这些元素在验证签名中有所介绍。您可以在此处找到可能的子元素的参考。以下清单显示了如何定义RequireSignature
元素:
<xwss:SecurityConfiguration xmlns:xwss="http://java.sun.com/xml/ns/xwss/config">
...
<xwss:RequireSignature requireTimestamp="false">
...
</xwss:SecurityConfiguration>
当没有携带证书的消息到达时,XwsSecurityInterceptor
向发送者返回一个 SOAP 错误。如果它存在,它会触发一个CertificateValidationCallback
. Spring-WS 中的三个处理程序处理此回调以进行身份验证:
在大多数情况下,证书身份验证应该先于证书验证,因为您只想针对有效证书进行身份验证。应忽略无效证书,例如过期日期已过或不在您的受信任证书存储中的证书。 在 Spring-WS 术语中,这意味着
使用此设置,拦截器首先使用密钥库确定消息中的证书是否有效,然后对其进行身份验证。 |
使用KeyStoreCallbackHandler
使用KeyStoreCallbackHandler
标准 Java 密钥库来验证证书。此证书验证过程包括以下步骤: .
-
处理程序检查证书是否在 private 中
keyStore
。如果是,它是有效的。 -
如果证书不在私钥库中,则处理程序检查当前日期和时间是否在证书中给定的有效期内。如果不是,则证书无效。如果是,则继续执行最后一步。
-
创建证书的证书路径。这基本上意味着处理程序确定证书是否已由
trustStore
. 如果可以成功构建证书路径,则证书有效。否则,证书无效。
要将KeyStoreCallbackHandler
用于证书验证目的,您很可能只需要设置trustStore
属性:
<beans>
<bean id="keyStoreHandler" class="org.springframework.ws.soap.security.xwss.callback.KeyStoreCallbackHandler">
<property name="trustStore" ref="trustStore"/>
</bean>
<bean id="trustStore" class="org.springframework.ws.soap.security.support.KeyStoreFactoryBean">
<property name="location" value="classpath:truststore.jks"/>
<property name="password" value="changeit"/>
</bean>
</beans>
使用前面示例中显示的设置,要验证的证书必须位于信任库本身中,或者信任库必须包含颁发证书的证书颁发机构。
使用SpringCertificateValidationCallbackHandler
这SpringCertificateValidationCallbackHandler
需要 Spring SecurityAuthenticationManager
才能运行。它使用此管理器X509AuthenticationToken
对其创建的 a 进行身份验证。配置的身份验证管理器应提供可以处理此令牌的提供程序(通常是 的实例X509AuthenticationProvider
)。如果身份验证成功,则令牌存储在SecurityContextHolder
. 您可以使用以下authenticationManager
属性设置身份验证管理器:
<beans>
<bean id="springSecurityCertificateHandler"
class="org.springframework.ws.soap.security.xwss.callback.SpringCertificateValidationCallbackHandler">
<property name="authenticationManager" ref="authenticationManager"/>
</bean>
<bean id="authenticationManager"
class="org.springframework.security.providers.ProviderManager">
<property name="providers">
<bean class="org.springframework.ws.soap.security.x509.X509AuthenticationProvider">
<property name="x509AuthoritiesPopulator">
<bean class="org.springframework.ws.soap.security.x509.populator.DaoX509AuthoritiesPopulator">
<property name="userDetailsService" ref="userDetailsService"/>
</bean>
</property>
</bean>
</property>
</bean>
<bean id="userDetailsService" class="com.mycompany.app.dao.UserDetailService" />
...
</beans>
在这种情况下,我们使用自定义用户详细信息服务来获取基于证书的身份验证详细信息。有关针对 X509 证书进行身份验证的更多信息,请参阅Spring Security 参考文档。
使用JaasCertificateValidationCallbackHandler
JaasCertificateValidationCallbackHandler
需要一个loginContextName
操作。LoginContext
它通过使用此名称和X500Principal
证书的 来创建一个新的 JAAS 。这意味着此回调处理程序与任何LoginModule
处理 X500 主体的 JAAS 集成。
您可以JaasCertificateValidationCallbackHandler
按如下方式连接:
<bean id="jaasValidationHandler"
class="org.springframework.ws.soap.security.xwss.callback.jaas.JaasCertificateValidationCallbackHandler">
<property name="loginContextName">MyLoginModule</property>
</bean>
在这种情况下,回调处理程序使用LoginContext
命名的MyLoginModule
. 该模块应该在您的jaas.config
文件中定义,并且应该能够针对 X500 主体进行身份验证。
7.1.3. 数字签名
消息的数字签名是基于文档和签名者私钥的一条信息。两个主要任务与 WS-Security 中的签名相关:验证签名和签署消息。
验证签名
与基于证书的身份验证一样,签名消息包含一个BinarySecurityToken
,其中包含用于对消息进行签名的证书。此外,它包含一个SignedInfo
块,指示消息的哪一部分已签名。
为了确保所有传入的 SOAP 消息都带有BinarySecurityToken
,安全策略文件应该包含一个RequireSignature
元素。它还可以包含一个SignatureTarget
元素,该元素指定预期要签名的目标消息部分和各种其他子元素。您还可以定义要使用的私钥别名,是否使用对称而不是私钥,以及许多其他属性。您可以在此处找到可能的子元素的参考。以下清单配置了一个RequireSignature
元素:
<xwss:SecurityConfiguration xmlns:xwss="http://java.sun.com/xml/ns/xwss/config">
<xwss:RequireSignature requireTimestamp="false"/>
</xwss:SecurityConfiguration>
如果签名不存在,则XwsSecurityInterceptor
向发送者返回一个 SOAP 错误。如果存在,它会SignatureVerificationKeyCallback
向已注册的处理程序触发 a。在 Spring-WS 中,一个类处理这个特定的回调:KeyStoreCallbackHandler
.
使用KeyStoreCallbackHandler
如KeyStoreCallbackHandler中所述,KeyStoreCallbackHandler
使用 ajava.security.KeyStore
处理各种加密回调,包括签名验证。对于签名验证,处理程序使用以下trustStore
属性:
<beans>
<bean id="keyStoreHandler" class="org.springframework.ws.soap.security.xwss.callback.KeyStoreCallbackHandler">
<property name="trustStore" ref="trustStore"/>
</bean>
<bean id="trustStore" class="org.springframework.ws.soap.security.support.KeyStoreFactoryBean">
<property name="location" value="classpath:org/springframework/ws/soap/security/xwss/test-truststore.jks"/>
<property name="password" value="changeit"/>
</bean>
</beans>
签署消息
签署消息时,XwsSecurityInterceptor
会在BinarySecurityToken
消息中添加 。它还添加了一个SignedInfo
块,指示消息的哪一部分已签名。
要签署所有传出的 SOAP 消息,安全策略文件应该包含一个Sign
元素。它还可以包含一个SignatureTarget
元素,该元素指定预期要签名的目标消息部分和各种其他子元素。您还可以定义要使用的私钥别名,是否使用对称而不是私钥,以及许多其他属性。您可以在此处找到可能的子元素的参考。以下示例包含一个Sign
元素:
<xwss:SecurityConfiguration xmlns:xwss="http://java.sun.com/xml/ns/xwss/config">
<xwss:Sign includeTimestamp="false" />
</xwss:SecurityConfiguration>
向已注册的处理程序触发XwsSecurityInterceptor
。SignatureKeyCallback
在 Spring-WS 中,KeyStoreCallbackHandler
该类处理这个特定的回调。
使用KeyStoreCallbackHandler
如KeyStoreCallbackHandler中所述,KeyStoreCallbackHandler
使用 ajava.security.KeyStore
来处理各种加密回调,包括签名消息。为了添加签名,处理程序使用该keyStore
属性。此外,您必须设置privateKeyPassword
属性以解锁用于签名的私钥。以下示例使用KeyStoreCallbackHandler
:
<beans>
<bean id="keyStoreHandler" class="org.springframework.ws.soap.security.xwss.callback.KeyStoreCallbackHandler">
<property name="keyStore" ref="keyStore"/>
<property name="privateKeyPassword" value="changeit"/>
</bean>
<bean id="keyStore" class="org.springframework.ws.soap.security.support.KeyStoreFactoryBean">
<property name="location" value="classpath:keystore.jks"/>
<property name="password" value="changeit"/>
</bean>
</beans>
7.1.4。解密和加密
加密时,消息被转换为只能用适当的密钥读取的形式。可以解密消息以显示原始的可读消息。
解密
要解密传入的 SOAP 消息,安全策略文件应该包含一个RequireEncryption
元素。该元素还可以携带一个EncryptionTarget
元素,该元素指示应该对消息的哪一部分进行加密,以及一个SymmetricKey
指示应该使用共享密钥而不是常规私钥来解密消息的元素。您可以在此处阅读其他元素的说明。以下示例使用一个RequireEncryption
元素:
<xwss:SecurityConfiguration xmlns:xwss="http://java.sun.com/xml/ns/xwss/config">
<xwss:RequireEncryption />
</xwss:SecurityConfiguration>
如果传入消息未加密,则XwsSecurityInterceptor
向发送者返回一个 SOAP 错误。如果存在,它会DecryptionKeyCallback
向已注册的处理程序触发 a。在 Spring-WS 中,KeyStoreCallbackHandler
该类处理这个特定的回调。
使用KeyStoreCallbackHandler
如KeyStoreCallbackHandler中所述,KeyStoreCallbackHandler
使用 ajava.security.KeyStore
来处理各种加密回调,包括解密。对于解密,处理程序使用该keyStore
属性。此外,您必须设置该privateKeyPassword
属性以解锁用于解密的私钥。对于基于对称密钥的解密,它使用symmetricStore
. 以下示例使用KeyStoreCallbackHandler
:
<beans>
<bean id="keyStoreHandler" class="org.springframework.ws.soap.security.xwss.callback.KeyStoreCallbackHandler">
<property name="keyStore" ref="keyStore"/>
<property name="privateKeyPassword" value="changeit"/>
</bean>
<bean id="keyStore" class="org.springframework.ws.soap.security.support.KeyStoreFactoryBean">
<property name="location" value="classpath:keystore.jks"/>
<property name="password" value="changeit"/>
</bean>
</beans>
加密
要加密传出的 SOAP 消息,安全策略文件应该包含一个Encrypt
元素。该元素还可以携带一个EncryptionTarget
元素,该元素指示应该对消息的哪一部分进行加密,以及一个SymmetricKey
指示应该使用共享密钥而不是常规公钥来加密消息的元素。您可以在此处阅读其他元素的说明。以下示例使用一个Encrypt
元素:
<xwss:SecurityConfiguration xmlns:xwss="http://java.sun.com/xml/ns/xwss/config">
<xwss:Encrypt />
</xwss:SecurityConfiguration>
向已注册的处理程序XwsSecurityInterceptor
触发EncryptionKeyCallback
以检索加密信息。在 Spring-WS 中,KeyStoreCallbackHandler
该类处理这个特定的回调。
使用KeyStoreCallbackHandler
如KeyStoreCallbackHandler中所述,KeyStoreCallbackHandler
使用 ajava.security.KeyStore
来处理各种加密回调,包括加密。对于基于公钥的加密,处理程序使用该trustStore
属性。对于基于对称密钥的加密,它使用symmetricStore
. 以下示例使用KeyStoreCallbackHandler
:
<beans>
<bean id="keyStoreHandler" class="org.springframework.ws.soap.security.xwss.callback.KeyStoreCallbackHandler">
<property name="trustStore" ref="trustStore"/>
</bean>
<bean id="trustStore" class="org.springframework.ws.soap.security.support.KeyStoreFactoryBean">
<property name="location" value="classpath:truststore.jks"/>
<property name="password" value="changeit"/>
</bean>
</beans>
7.1.5。安全异常处理
当安全或验证操作失败时,分别XwsSecurityInterceptor
抛出 aWsSecuritySecurementException
或WsSecurityValidationException
。这些异常绕过标准异常处理机制,但由拦截器本身处理。
WsSecuritySecurementException
异常handleSecurementException
由XwsSecurityInterceptor
. 默认情况下,此方法会记录错误并停止对消息的进一步处理。
同样,WsSecurityValidationException
异常handleValidationException
由XwsSecurityInterceptor
. 默认情况下,此方法会创建 SOAP 1.1 客户端或 SOAP 1.2 发送方故障并将其作为响应发回。
handleSecurementException 和
都是handleValidationException 受保护的方法,您可以覆盖它们以更改其默认行为。
|
7.2. 使用Wss4jSecurityInterceptor
这Wss4jSecurityInterceptor
是一个基于Apache 的 WSS4JEndpointInterceptor
的(请参阅拦截请求 -EndpointInterceptor
接口) 。
WSS4J 实现以下标准:
-
OASIS Web 服务安全:SOAP 消息安全 1.0 标准 200401,2004 年 3 月
-
用户名令牌配置文件V1.0
-
X.509 令牌配置文件 V1.0
此拦截器支持由AxiomSoapMessageFactory
和创建的消息SaajSoapMessageFactory
。
7.2.1. 配置Wss4jSecurityInterceptor
WSS4J 不使用外部配置文件。拦截器完全由属性配置。此拦截器调用的验证和安全操作分别通过validationActions
和securementActions
属性指定。操作作为空格分隔的字符串传递。以下清单显示了一个示例配置:
<bean class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
<property name="validationActions" value="UsernameToken Encrypt"/>
...
<property name="securementActions" value="Encrypt"/>
...
</bean>
下表显示了可用的验证操作:
验证操作 | 描述 |
---|---|
|
验证用户名令牌 |
|
验证时间戳 |
|
解密消息 |
|
验证签名 |
|
未执行任何操作 |
下表显示了可用的安全措施:
担保行动 | 描述 |
---|---|
|
添加用户名令牌 |
|
添加用户名令牌和签名用户名令牌密钥 |
|
添加时间戳 |
|
加密响应 |
|
签署回应 |
|
未执行任何操作 |
动作的顺序很重要,由拦截器强制执行。如果其安全操作的执行顺序与“validationActions”指定的顺序不同,则拦截器会拒绝传入的 SOAP 消息。
7.2.2. 处理数字证书
对于需要与密钥库交互或证书处理(签名、加密和解密操作)的加密操作,WSS4J 需要`org.apache.ws.security.components.crypto.Crypto` 的实例。
Crypto
实例可以从 WSS4J 获得,CryptoFactory
或者更方便地使用 Spring-WS`CryptoFactoryBean` 获得。
CryptoFactoryBean
Spring-WS 提供了一个方便的工厂 bean,它通过强类型属性(首选)或对象CryptoFactoryBean
来构造和配置实例。Crypto
Properties
默认情况下,CryptoFactoryBean
返回org.apache.ws.security.components.crypto.Merlin
. 您可以通过设置cryptoProvider
属性(或其等效的org.apache.ws.security.crypto.provider
字符串属性)来更改它。
以下示例配置使用CryptoFactoryBean
:
<bean class="org.springframework.ws.soap.security.wss4j.support.CryptoFactoryBean">
<property name="keyStorePassword" value="mypassword"/>
<property name="keyStoreLocation" value="file:/path_to_keystore/keystore.jks"/>
</bean>
7.2.3. 验证
本节介绍如何使用Wss4jSecurityInterceptor
.
验证用户名令牌
Spring-WS 提供了一组回调处理程序来与 Spring Security 集成。此外,还提供了一个简单的回调处理程序,用于使用内存对象SimplePasswordValidationCallbackHandler
配置用户和密码。Properties
回调处理程序是通过validationCallbackHandler
该Wss4jSecurityInterceptor
属性配置的。
使用SimplePasswordValidationCallbackHandler
SimplePasswordValidationCallbackHandler
针对内存中的Properties
对象验证纯文本和摘要用户名令牌。您可以按如下方式配置它:
<bean id="callbackHandler"
class="org.springframework.ws.soap.security.wss4j.callback.SimplePasswordValidationCallbackHandler">
<property name="users">
<props>
<prop key="Bert">Ernie</prop>
</props>
</property>
</bean>
使用SpringSecurityPasswordValidationCallbackHandler
通过SpringSecurityPasswordValidationCallbackHandler
使用 Spring SecurityUserDetailService
来操作验证纯文本和摘要密码。它使用此服务来检索令牌中指定的用户的密码(或密码摘要)。然后将此详细信息对象中包含的密码(或密码摘要)与消息中的摘要进行比较。如果它们相等,则用户已成功认证,并且 aUsernamePasswordAuthenticationToken
存储在 SecurityContextHolder 中。您可以使用userDetailsService
. 此外,您可以设置一个userCache
属性来缓存加载的用户详细信息,如下所示:
<beans>
<bean class="org.springframework.ws.soap.security.wss4j.callback.SpringDigestPasswordValidationCallbackHandler">
<property name="userDetailsService" ref="userDetailsService"/>
</bean>
<bean id="userDetailsService" class="com.mycompany.app.dao.UserDetailService" />
...
</beans>
添加用户名令牌
将用户名令牌添加到传出消息就像添加UsernameToken
到securementActions
属性Wss4jSecurityInterceptor
并指定和securementUsername
`securementPassword`一样简单。
可以通过设置securementPasswordType
属性来设置密码类型。可能的值是PasswordText
纯文本密码或PasswordDigest
摘要密码,这是默认值。
以下示例生成带有摘要密码的用户名令牌:
<bean class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
<property name="securementActions" value="UsernameToken"/>
<property name="securementUsername" value="Ernie"/>
<property name="securementPassword" value="Bert"/>
</bean>
如果选择明文密码类型,可以通过设置属性来指示拦截器添加Nonce
和Created
元素。securementUsernameTokenElements
该值必须是一个列表,其中包含由空格分隔的所需元素的名称(区分大小写)。
以下示例使用纯文本密码、aNonce
和 aCreated
元素生成用户名令牌:
<bean class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
<property name="securementActions" value="UsernameToken"/>
<property name="securementUsername" value="Ernie"/>
<property name="securementPassword" value="Bert"/>
<property name="securementPasswordType" value="PasswordText"/>
<property name="securementUsernameTokenElements" value="Nonce Created"/>
</bean>
证书认证
由于证书身份验证类似于数字签名,WSS4J 将其作为签名验证和保护的一部分进行处理。具体来说,该securementSignatureKeyIdentifier
属性必须设置为DirectReference
以指示 WSS4J 生成BinarySecurityToken
包含 X509 证书的元素并将其包含在传出消息中。证书的名称和密码分别通过securementUsername
和securementPassword
属性传递,如以下示例所示:
<bean class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
<property name="securementActions" value="Signature"/>
<property name="securementSignatureKeyIdentifier" value="DirectReference"/>
<property name="securementUsername" value="mycert"/>
<property name="securementPassword" value="certpass"/>
<property name="securementSignatureCrypto">
<bean class="org.springframework.ws.soap.security.wss4j.support.CryptoFactoryBean">
<property name="keyStorePassword" value="123456"/>
<property name="keyStoreLocation" value="classpath:/keystore.jks"/>
</bean>
</property>
</bean>
对于证书验证,常规签名验证适用:
<bean class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
<property name="validationActions" value="Signature"/>
<property name="validationSignatureCrypto">
<bean class="org.springframework.ws.soap.security.wss4j.support.CryptoFactoryBean">
<property name="keyStorePassword" value="123456"/>
<property name="keyStoreLocation" value="classpath:/keystore.jks"/>
</bean>
</property>
</bean>
在验证结束时,拦截器通过委托给默认的 WSS4J 实现来自动验证证书的有效性。verifyCertificateTrust
如果需要,您可以通过重新定义方法来更改此行为。
有关更多详细信息,请参阅数字签名。
7.2.4. 安全时间戳
本节介绍Wss4jSecurityInterceptor
.
验证时间戳
要验证时间戳,请添加Timestamp
到validationActions
属性。您可以通过设置属性设置并指定timestampStrict
以true
秒为单位的服务器端生存时间(默认值:300)来覆盖 SOAP 消息的发起者指定的时间戳语义。timeToLive
拦截器总是拒绝已经过期的时间戳,不管是什么值timeToLive
。
在以下示例中,拦截器将时间戳有效性窗口限制为 10 秒,拒绝该窗口之外的任何有效时间戳令牌:
<bean class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
<property name="validationActions" value="Timestamp"/>
<property name="timestampStrict" value="true"/>
<property name="timeToLive" value="10"/>
</bean>
添加时间戳
添加Timestamp
到该securementActions
属性会在传出消息中生成时间戳标头。该timestampPrecisionInMilliseconds
属性指定生成的时间戳的精度是否以毫秒为单位。默认值为true
。以下清单添加了一个时间戳:
<bean class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
<property name="securementActions" value="Timestamp"/>
<property name="timestampPrecisionInMilliseconds" value="true"/>
</bean>
7.2.5。数字签名
本节介绍Wss4jSecurityInterceptor
.
验证签名
要指示Wss4jSecurityInterceptor
,validationActions
必须包含Signature
操作。此外,该validationSignatureCrypto
属性必须指向包含发起者公共证书的密钥库:
<bean id="wsSecurityInterceptor" class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
<property name="validationActions" value="Signature"/>
<property name="validationSignatureCrypto">
<bean class="org.springframework.ws.soap.security.wss4j.support.CryptoFactoryBean">
<property name="keyStorePassword" value="123456"/>
<property name="keyStoreLocation" value="classpath:/keystore.jks"/>
</bean>
</property>
</bean>
签署消息
通过将Signature
操作添加到securementActions
. 要使用的私钥的别名和密码分别由securementUsername
和securementPassword
属性指定。securementSignatureCrypto
必须指向包含私钥的密钥库:
<bean class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
<property name="securementActions" value="Signature"/>
<property name="securementUsername" value="mykey"/>
<property name="securementPassword" value="123456"/>
<property name="securementSignatureCrypto">
<bean class="org.springframework.ws.soap.security.wss4j.support.CryptoFactoryBean">
<property name="keyStorePassword" value="123456"/>
<property name="keyStoreLocation" value="classpath:/keystore.jks"/>
</bean>
</property>
</bean>
securementSignatureAlgorithm
此外,您可以通过设置属性来定义签名算法。
您可以通过设置securementSignatureKeyIdentifier
属性来自定义要使用的密钥标识符类型。仅对签名有效IssuerSerial
。DirectReference
该securementSignatureParts
属性控制对消息的哪一部分进行签名。此属性的值是以分号分隔的元素名称列表,用于标识要签名的元素。签名部分的一般形式是{}{namespace}Element
. 请注意,第一个空括号仅用于加密部分。默认行为是对 SOAP 主体进行签名。
以下示例显示了如何echoResponse
在 Spring Web Services 回显示例中对元素进行签名:
<property name="securementSignatureParts"
value="{}{http://www.springframework.org/spring-ws/samples/echo}echoResponse"/>
要指定没有命名空间的元素,请使用字符串Null
(区分大小写)作为命名空间名称。
如果请求中没有其他元素的本地名称为Body
,则 SOAP 名称空间标识符可以为空 ( {}
)。
签名确认
enableSignatureConfirmation
通过设置启用签名确认true
。请注意,签名确认操作跨越请求和响应。这意味着即使没有相应的安全操作,也必须将 and 设置为secureResponse
(这是默认值)。以下示例将属性设置为:validateRequest
true
enableSignatureConfirmation
true
<bean class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
<property name="validationActions" value="Signature"/>
<property name="enableSignatureConfirmation" value="true"/>
<property name="validationSignatureCrypto">
<bean class="org.springframework.ws.soap.security.wss4j.support.CryptoFactoryBean">
<property name="keyStorePassword" value="123456"/>
<property name="keyStoreLocation" value="file:/keystore.jks"/>
</bean>
</property>
</bean>
7.2.6。解密和加密
本节介绍Wss4jSecurityInterceptor
.
解密
传入 SOAP 消息的解密需要将Encrypt
操作添加到validationActions
属性中。其余配置取决于消息中出现的关键信息。(这是因为 WSS4J 只需要加密密钥的 Crypto,而嵌入式密钥名称验证被委托给回调处理程序。)
要使用嵌入的加密对称密钥(xenc:EncryptedKey
元素)解密消息,validationDecryptionCrypto
需要指向包含解密私钥的密钥库。此外,validationCallbackHandler
必须注入一个org.springframework.ws.soap.security.wss4j.callback.KeyStoreCallbackHandler
指定密钥密码的:
<bean class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
<property name="validationActions" value="Encrypt"/>
<property name="validationDecryptionCrypto">
<bean class="org.springframework.ws.soap.security.wss4j.support.CryptoFactoryBean">
<property name="keyStorePassword" value="123456"/>
<property name="keyStoreLocation" value="classpath:/keystore.jks"/>
</bean>
</property>
<property name="validationCallbackHandler">
<bean class="org.springframework.ws.soap.security.wss4j.callback.KeyStoreCallbackHandler">
<property name="privateKeyPassword" value="mykeypass"/>
</bean>
</property>
</bean>
要支持使用嵌入式密钥名称(ds:KeyName
元素)解密消息,您可以配置一个KeyStoreCallbackHandler
指向具有对称密钥的密钥库的密钥。该symmetricKeyPassword
属性表示密钥的密码,密钥名称由ds:KeyName
元素指定:
<bean class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
<property name="validationActions" value="Encrypt"/>
<property name="validationCallbackHandler">
<bean class="org.springframework.ws.soap.security.wss4j.callback.KeyStoreCallbackHandler">
<property name="keyStore">
<bean class="org.springframework.ws.soap.security.support.KeyStoreFactoryBean">
<property name="location" value="classpath:keystore.jks"/>
<property name="type" value="JCEKS"/>
<property name="password" value="123456"/>
</bean>
</property>
<property name="symmetricKeyPassword" value="mykeypass"/>
</bean>
</property>
</bean>
加密
添加Encrypt
到securementActions
启用传出消息的加密。securementEncryptionUser
您可以通过设置属性来设置证书的别名以用于加密。securementEncryptionCrypto
通过属性访问证书所在的密钥库。由于加密依赖于公共证书,因此无需传递密码。以下示例使用该securementEncryptionCrypto
属性:
<bean class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
<property name="securementActions" value="Encrypt"/>
<property name="securementEncryptionUser" value="mycert"/>
<property name="securementEncryptionCrypto">
<bean class="org.springframework.ws.soap.security.wss4j.support.CryptoFactoryBean">
<property name="keyStorePassword" value="123456"/>
<property name="keyStoreLocation" value="file:/keystore.jks"/>
</bean>
</property>
</bean>
您可以通过多种方式自定义加密: 要使用的密钥标识符类型由securementEncryptionKeyIdentifier
属性定义。可能的值为IssuerSerial
、X509KeyIdentifier
、DirectReference
、Thumbprint
、SKIKeyIdentifier
和EmbeddedKeyName
。
如果选择EmbeddedKeyName
类型,则需要指定用于加密的密钥。键的别名在securementEncryptionUser
属性中设置,与其他键标识符类型一样。但是,WSS4J 需要回调处理程序来获取密钥。因此,您必须提供指向适当密钥库securementCallbackHandler
的那个。KeyStoreCallbackHandler
默认情况下,ds:KeyName
生成的 WS-Security 标头中的元素采用securementEncryptionUser
属性的值。要指示不同的名称,您可以将 设置securementEncryptionEmbeddedKeyName
为所需的值。在下一个示例中,传出消息使用密钥 aliased 加密,secretKey
而出myKey
现在ds:KeyName
元素中:
<bean class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
<property name="securementActions" value="Encrypt"/>
<property name="securementEncryptionKeyIdentifier" value="EmbeddedKeyName"/>
<property name="securementEncryptionUser" value="secretKey"/>
<property name="securementEncryptionEmbeddedKeyName" value="myKey"/>
<property name="securementCallbackHandler">
<bean class="org.springframework.ws.soap.security.wss4j.callback.KeyStoreCallbackHandler">
<property name="symmetricKeyPassword" value="keypass"/>
<property name="keyStore">
<bean class="org.springframework.ws.soap.security.support.KeyStoreFactoryBean">
<property name="location" value="file:/keystore.jks"/>
<property name="type" value="jceks"/>
<property name="password" value="123456"/>
</bean>
</property>
</bean>
</property>
</bean>
该securementEncryptionKeyTransportAlgorithm
属性定义用于加密生成的对称密钥的算法。支持的值为http://www.w3.org/2001/04/xmlenc#rsa-1_5
(默认值)和http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p
。
您可以通过设置securementEncryptionSymAlgorithm
属性来设置要使用的对称加密算法。支持的值为http://www.w3.org/2001/04/xmlenc#aes128-cbc
(默认)http://www.w3.org/2001/04/xmlenc#tripledes-cbc
、、、http://www.w3.org/2001/04/xmlenc#aes256-cbc
和http://www.w3.org/2001/04/xmlenc#aes192-cbc
。
最后,该securementEncryptionParts
属性定义了消息的哪些部分被加密。此属性的值是以分号分隔的元素名称列表,用于标识要加密的元素。一个加密模式说明符和一个命名空间标识,每个都在一对大括号内,可以在每个元素名称之前。加密模式说明符是{Content}
或{Element}
请参阅 W3C XML 加密规范,了解元素加密和内容加密之间的区别。以下示例echoResponse
从 echo 样本中识别出:
<property name="securementEncryptionParts"
value="{Content}{http://www.springframework.org/spring-ws/samples/echo}echoResponse"/>
请注意,元素名称、命名空间标识符和加密修饰符是区分大小写的。您可以省略加密修饰符和命名空间标识符。如果这样做,则加密模式默认为Content
,并且名称空间设置为 SOAP 名称空间。
要指定没有命名空间的元素,请使用值Null
(区分大小写)作为命名空间名称。如果未指定列表,则处理程序Content
默认以模式加密 SOAP 主体。
7.2.7. 安全异常处理
的异常处理与 的Wss4jSecurityInterceptor
相同XwsSecurityInterceptor
。有关详细信息,请参阅安全异常处理。