二、参考资料
4. 共享组件
本章探讨了客户端和服务器端 Spring-WS 开发之间共享的组件。 这些接口和类代表了 Spring-WS 的构建块,因此即使您不直接使用它们,您也需要了解它们的作用。
4.1. 空安全
声明 API 的可空性的主要目标是防止NullPointerException避免在运行时抛出。
这是通过 Java 和 Kotlin 提供的构建时检查来实现的。
使用 Java 执行检查需要一些工具,例如 NullAway 或支持 JSpecify 注释的 IDE,例如 IntelliJ IDEA。
Kotlin 会自动进行检查,Kotlin 将 JSpecify 注释转换为 Kotlin 的 null 安全性。
要了解有关 Spring 的 null 安全性的更多信息,请参阅 Spring 框架参考文档。
4.2. Web 服务消息
本节介绍 Spring-WS 使用的消息和消息工厂。
4.2.1.WebServiceMessage
Spring-WS 的核心接口之一是WebServiceMessage.
此接口表示与协议无关的 XML 消息。
该接口包含提供对消息有效负载的访问的方法,其形式为javax.xml.transform.Source或javax.xml.transform.Result.Source和Result是表示 XML 输入和输出抽象的标记接口。
具体实现包装各种 XML 表示形式,如下表所示:
| 源或结果实现 | 包装的 XML 表示 |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
除了读取和写入有效负载之外,Web 服务消息还可以将自身写入输出流。
4.2.2.SoapMessage
SoapMessage是WebServiceMessage.
它包含特定于 SOAP 的方法,例如获取 SOAP 标头、SOAP 错误等。
通常,您的代码不应依赖于SoapMessage,因为 SOAP 正文的内容(消息的有效负载)可以通过使用getPayloadSource()和getPayloadResult()在WebServiceMessage.
只有当需要执行特定于 SOAP 的作(例如添加标头、获取附件等)时,才需要强制转换WebServiceMessage自SoapMessage.
4.2.3. 消息工厂
具体消息实现由WebServiceMessageFactory.
此工厂可以创建空消息或从输入流读取消息。
有两种具体实现WebServiceMessageFactory.
一个基于 SAAJ,即 Java 的带有附件的 SOAP API。
另一个基于 Axis 2 的 AXIOM(AXis 对象模型)。
SaajSoapMessageFactory
这SaajSoapMessageFactory使用 SOAP with Attachments API for Java (SAAJ) 创建SoapMessage实现。
SAAJ 是 J2EE 1.4 的一部分,因此大多数现代应用程序服务器都应该支持它。
以下是常见应用程序服务器提供的 SAAJ 版本的概述:
| 应用服务器 | SAAJ 版本 |
|---|---|
东亚 WebLogic 8 |
1.1 |
东亚 WebLogic 9 |
1.1/1.21 |
IBM WebSphere 6 系统 |
1.2 |
太阳玻璃鱼 1 |
1.3 |
1Weblogic 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使用 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和StreamingPayload.
SOAP 1.1 或 1.2
两个SaajSoapMessageFactory和AxiomSoapMessageFactory有一个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 消息。
|
尽管两个版本的 SOAP 在格式上非常相似,但 1.2 版本与 1.1 不向后兼容,因为它使用不同的 XML 命名空间。
SOAP 1.1 和 1.2 之间的其他主要区别包括不同的故障结构以及 关于 SOAP 版本号(或一般的 WS-* 规范版本号)需要注意的一件重要事情是,规范的最新版本通常不是最流行的版本。 对于 SOAP,这意味着(当前)最好使用的版本是 1.1。 1.2 版本将来可能会变得更加流行,但 1.1 是目前最安全的选择。 |
4.2.4.MessageContext
通常,消息成对出现:请求和响应。 在客户端创建请求,该请求通过某种传输发送到服务器端,在服务器端生成响应。 此响应被发送回客户端,并在客户端读取。
在 Spring-WS 中,这样的对话包含在MessageContext,它具有用于获取请求和响应消息的属性。
在客户端,消息上下文由WebServiceTemplate.
在服务器端,从特定于传输的输入流中读取消息上下文。
例如,在 HTTP 中,它是从HttpServletRequest,并将响应写回HttpServletResponse.
4.3.TransportContext
SOAP 协议的关键属性之一是它试图与传输无关。 这就是为什么,例如,Spring-WS 不支持通过 HTTP 请求 URL 而是通过消息内容将消息映射到端点。
但是,有时需要在客户端或服务器端访问底层传输。
为此,Spring-WS 具有TransportContext.
传输上下文允许访问底层WebServiceConnection,通常是HttpServletConnection在服务器端或HttpUrlConnection或CommonsHttpConnection在客户端。
例如,您可以在服务器端端点或拦截器中获取当前请求的 IP 地址:
TransportContext context = TransportContextHolder.getTransportContext();
HttpServletConnection connection = (HttpServletConnection )context.getConnection();
HttpServletRequest request = connection.getHttpServletRequest();
String ipAddress = request.getRemoteAddr();
4.4. 使用 XPath 处理 XML
处理 XML 的最佳方法之一是使用 XPath。 引用 [effective-xml],第 35 项:
XPath 是第四代声明性语言,它允许您指定要处理的节点,而无需准确指定处理器应该如何导航到这些节点。 XPath 的数据模型设计得非常好,可以完全支持几乎所有开发人员都希望从 XML 中得到的东西。 例如,它合并所有相邻文本,包括 CDATA 部分中的文本,允许计算跳过注释和处理指令的值,并包含来自子元素和后代元素的文本,并要求解析所有外部实体引用。 在实践中,XPath 表达式对于输入文档中意外但可能微不足道的更改往往更加稳健。
Spring-WS 有两种在应用程序中使用 XPath 的方法:速度越快XPathExpression或更灵活XPathOperations.
4.4.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工厂豆的属性。
该表达式可以在代码中使用,如下所示:
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.4.2.XPathOperations
这XPathExpression仅允许您计算单个预编译表达式。
一个更灵活但速度较慢的替代方案是XPathOperations.
此类遵循整个 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.5. 消息日志记录和跟踪
在开发或调试 Web 服务时,在 (SOAP) 消息到达时或发送之前查看其内容可能非常有用。 Spring-WS 通过标准的 Commons Logging 接口提供此功能。
要记录所有服务器端消息,请将org.springframework.ws.server.MessageTracinglogger 级别设置为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.
以下示例log4j2.properties配置文件在客户端记录已发送消息的完整内容,并且仅记录客户端接收消息的有效负载根元素。在服务器端,记录已发送和接收消息的有效负载根:
appender.console.name=STDOUT
appender.console.type=Console
appender.console.layout.type=PatternLayout
appender.console.layout.pattern=%-5p [%c{3}] %m%n
rootLogger=DEBUG,STDOUT
logger.org.springframework.ws.client.MessageTracing.sent=TRACE
logger.org.springframework.ws.client.MessageTracing.received=DEBUG
logger.org.springframework.ws.server.MessageTracing=DEBUG
使用此配置,典型输出为:
TRACE [client.MessageTracing.sent] Sent request [<SOAP-ENV:Envelope xmlns:SOAP-ENV="...
DEBUG [server.MessageTracing.received] Received request [SaajSoapMessage {http://example.com}request] ...
DEBUG [server.MessageTracing.sent] Sent response [SaajSoapMessage {http://example.com}response] ...
DEBUG [client.MessageTracing.received] Received response [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 消息分派到端点。
弹簧-WSMessageDispatcher非常灵活,允许您使用任何类型的类作为端点,只要它可以在 Spring IoC 容器中配置即可。
在某种程度上,消息调度器类似于 Spring 的DispatcherServlet,Spring Web MVC 中使用的“前端控制器”。
以下序列图显示了MessageDispatcher:
当MessageDispatcher设置为使用,并且针对该特定调度程序发出请求,则MessageDispatcher开始处理请求。
以下过程介绍了如何MessageDispatcher处理请求:
-
配置的
EndpointMapping(s)搜索适当的端点。 如果找到端点,则调用与该端点关联的调用链(预处理器、后处理器和端点)以创建响应。 -
为终结点找到适当的适配器。 这
MessageDispatcher委托给此适配器以调用终结点。 -
如果返回响应,则会在途中发送。 如果未返回响应(这可能是由于预处理器或后处理器拦截请求,例如出于安全原因),则不会发送任何响应。
在处理请求期间引发的异常将由应用程序上下文中声明的任何终结点异常解析程序选取。 使用这些异常解析器可以定义自定义行为(例如返回 SOAP 错误),以防引发此类异常。
这MessageDispatcher具有多个用于设置端点适配器、映射、异常解析器的属性。
但是,不需要设置这些属性,因为调度程序会自动检测在应用程序上下文中注册的所有类型。
仅当需要覆盖检测时,才应设置这些属性。
消息调度程序在消息上下文上运行,而不是在特定于传输的输入流和输出流上运行。
因此,特定于传输的请求需要读入MessageContext.
对于 HTTP,这是通过WebServiceMessageReceiverHandlerAdapter(这是一个 Spring WebHandlerInterceptor),以便MessageDispatcher可以接线在标准中DispatcherServlet.
但是,有一种更方便的方法可以做到这一点,如MessageDispatcherServlet.
5.2. 运输
Spring-WS 支持多种传输协议。 最常见的是 HTTP 传输,为此提供了自定义 servlet,但您也可以通过 JMS 甚至电子邮件发送消息。
5.2.1.MessageDispatcherServlet
这MessageDispatcherServlet是一个标准Servlet方便地从标准 Spring Web 扩展而来DispatcherServlet并包装一个MessageDispatcher.
因此,它将这些属性合二为一。
作为MessageDispatcher,它遵循与上一节中所述的相同请求处理流程。
作为 servlet,MessageDispatcherServlet在web.xml的 Web 应用程序。
您希望MessageDispatcherServlet处理必须由同一 URL 映射映射web.xml文件。
这是标准的 JavaEE 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-WS 的第一步,因为 Spring-WS 框架使用的各种组件 bean 也需要配置。
此配置由标准 Spring XML 组成<bean/>定义。
因为MessageDispatcherServlet是标准弹簧DispatcherServlet,它会查找一个名为[servlet-name]-servlet.xml在WEB-INF目录,并在 Spring 容器中创建定义的 bean。
在前面的示例中,它查找/WEB-INF/spring-ws-servlet.xml.
此文件包含所有 Spring-WS bean,例如端点、封送程序等。
作为替代方案web.xml,您可以以编程方式配置 Spring-WS。
为此,Spring-WS 提供了许多抽象基类,这些基类扩展了WebApplicationInitializer接口。
如果您还使用@Configuration类,您应该将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类)。
其他 Bean 定义(通常是服务、存储库等)可以在MyRootConfig类。
默认情况下,AbstractAnnotationConfigMessageDispatcherServletInitializer将 Servlet 映射到两个模式:/services和*.wsdl,但您可以通过覆盖getServletMappings()方法。
有关MessageDispatcherServlet,请参阅AbstractMessageDispatcherServletInitializer和AbstractAnnotationConfigMessageDispatcherServletInitializer.
自动 WSDL 公开
这MessageDispatcherServlet自动检测任何WsdlDefinitionbean 在其 Spring 容器中定义。
所有WsdlDefinition检测到的 bean 也通过WsdlDefinitionHandlerAdapter.
这是通过定义一些 bean 向客户端公开 WSDL 的便捷方法。
举个例子,考虑以下内容<static-wsdl>定义,在 Spring-WS 配置文件 (/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"));
}
您可以访问orders.wsdl通过GET请求到以下形式的 URL(根据需要替换主机、端口和 servlet 上下文路径):
http://localhost:8080/spring-ws/orders.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-WS 还可以从 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("Orders.xsd")));
return definition;
}
这<dynamic-wsdl>元素取决于DefaultWsdl11Definition类。
此定义类在org.springframework.ws.wsdl.wsdl11.providerpackage 和ProviderBasedWsdl4jDefinition类在第一次请求时生成 WSDL。
请参阅这些类的类级 Javadoc,了解如何在必要时扩展此机制。
这DefaultWsdl11Definition(因此,<dynamic-wsdl>标签)使用约定从 XSD 模式构建 WSDL。
它迭代所有element元素,并创建message对于所有元素。
接下来,它创建一个 WSDLoperation对于以定义的请求或响应后缀结尾的所有消息。
默认请求后缀为Request.
默认响应后缀为Response,尽管可以通过设置requestSuffix和responseSuffix属性<dynamic-wsdl />分别。
它还构建了一个portType,binding和service基于作。
例如,如果我们的Orders.xsdschema 定义了GetOrdersRequest和GetOrdersResponse元素<dynamic-wsdl>创建一个GetOrdersRequest和GetOrdersResponsemessage 和GetOrders作,将其放在Orders端口类型。
要通过包含或导入使用多个模式,您可以将 Commons XMLSchema 放在类路径上。
如果 Commons XMLSchema 在类路径上,则<dynamic-wsdl>元素遵循所有 XSD 导入,并将它们作为单个 XSD 包含在 WSDL 中并内联。
使用 Java 配置,您可以使用CommonsXsdSchemaCollection如以下示例所示:
@Bean
public DefaultWsdl11Definition ordersWsdlDefinition() throws Exception {
DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition();
// ... configuration
CommonsXsdSchemaCollection schemas = new CommonsXsdSchemaCollection(
new ClassPathResource("xsd/order/main.xsd"));
schemas.setInline(true);
wsdl11Definition.setSchemaCollection(schemas);
return wsdl11Definition;
}
这极大地简化了架构的部署,同时仍然可以单独编辑它们。
|
尽管在运行时从 XSD 创建 WSDL 很方便,但这种方法也有一些缺点。 首先,尽管我们试图在版本之间保持 WSDL 生成过程的一致性,但它仍然有可能(轻微地)发生变化。 其次,生成速度有点慢,不过,一旦生成,WSDL 就会被缓存以供以后参考。 |
因此,您应该使用<dynamic-wsdl>仅在项目的开发阶段。
我们建议使用浏览器下载生成的 WSDL,将其存储在项目中,并使用<static-wsdl>.
这是真正确保 WSDL 不会随时间变化的唯一方法。
5.2.2. 在DispatcherServlet
作为MessageDispatcherServlet,您可以连接一个MessageDispatcher在标准中,Spring-Web MVCDispatcherServlet.
默认情况下,DispatcherServlet只能委托给Controllers,但我们可以指示它委托给MessageDispatcher通过添加一个WebServiceMessageReceiverHandlerAdapter到 Servlet 的 Web 应用程序上下文:
<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在最后。
以类似的方式,您可以连接一个WsdlDefinitionHandlerAdapter以确保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/myServiceDefinition.wsdl"/>
</bean>
...
</beans>
5.2.3. JMS 传输
Spring-WS 通过 Spring 框架中提供的 JMS 功能支持服务器端 JMS 处理。
Spring-WS 提供了WebServiceMessageListener插入MessageListenerContainer.
此消息侦听器需要一个WebServiceMessageFactory和MessageDispatcher作。
以下配置示例显示了这一点:
<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-WS 还提供服务器端电子邮件处理。
此功能通过MailMessageReceiver类。
此类监控 POP3 或 IMAP 文件夹,将电子邮件转换为WebServiceMessage,并使用 SMTP 发送任何响应。
您可以通过storeUri,指示要监视请求的邮件文件夹(通常是 POP3 或 IMAP 文件夹),以及transportUri,指示用于发送响应的服务器(通常是 SMTP 服务器)。
您可以配置如何MailMessageReceiver使用可插拔策略监控传入消息:MonitoringStrategy.
默认情况下,使用轮询策略,其中每五分钟轮询一次传入文件夹以查找新邮件。
您可以通过设置pollingInterval策略上的属性。
默认情况下,所有MonitoringStrategy实现删除已处理的消息。
您可以通过将deleteMessages财产。
作为效率低下的轮询方法的替代方法,有一种使用 IMAP IDLE 的监控策略。
IDLE 命令是 IMAP 电子邮件协议的可选扩展,它允许邮件服务器将新邮件更新发送到MailMessageReceiver异步。 如果您使用支持 IDLE 命令的 IMAP 服务器,则可以将ImapIdleMonitoringStrategy进入monitoringStrategy财产。
以下配置显示如何使用服务器端电子邮件支持,覆盖默认轮询间隔以每 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 <[email protected]>"/>
<property name="storeUri" value="imap://server:[email protected]/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-WS 提供了一种基于 Sun 的 JRE HTTP 服务器的传输。 嵌入式 HTTP 服务器是一个易于配置的独立服务器。 它为传统servlet容器提供了一种更轻的替代品。
使用嵌入式 HTTP 服务器时,不需要外部部署描述符 (web.xml).
您只需定义服务器的实例并将其配置为处理传入请求。SimpleHttpServerFactoryBean连接起来,最重要的属性是contexts,它将上下文路径映射到相应的HttpHandler实例。
Spring-WS 提供了两个HttpHandler接口:WsdlDefinitionHttpHandler和WebServiceMessageReceiverHttpHandler.
前者将传入的 GET 请求映射到WsdlDefinition.
后者负责处理 Web 服务消息的 POST 请求,因此需要一个WebServiceMessageFactory(通常SaajSoapMessageFactory) 和WebServiceMessageReceiver(通常SoapMessageDispatcher)完成其任务。
为了与 servlet 世界进行类比,请执行以下作的contexts属性在web.xml和WebServiceMessageReceiverHttpHandler相当于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.ws.transport.http.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>
5.2.6. XMPP 传输
Spring-WS 支持 XMPP,也称为 Jabber。 该支持基于 Smack 库。
Spring-WS 对 XMPP 的支持与其他传输非常相似:有一个XmppMessageSender对于WebServiceTemplate和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.3. 端点
端点是 Spring-WS 服务器端支持的核心概念。 端点提供对应用程序行为的访问,该行为通常由业务服务接口定义。 端点解释 XML 请求消息,并使用该输入(通常)调用业务服务上的方法。 该服务调用的结果表示为响应消息。 Spring-WS 具有多种端点,并使用各种方式来处理 XML 消息和创建响应。
您可以通过使用@Endpoint注解。
在类中,您可以使用各种参数类型(例如 DOM 元素、JAXB2 对象等)来定义一个或多个处理传入 XML 请求的方法。
您可以使用另一个注释(通常@PayloadRoot).
请考虑以下示例终结点:
package samples;
@Endpoint (1)
public class AnnotationOrderEndpoint {
private final OrderService orderService;
public AnnotationOrderEndpoint(OrderService orderService) {
this.orderService = orderService;
}
@PayloadRoot(localPart = "order", namespace = "http://samples") (4)
public void order(@RequestPayload Element orderElement) { (2)
Order order = createOrder(orderElement);
orderService.createOrder(order);
}
@PayloadRoot(localPart = "orderRequest", namespace = "http://samples") (4)
@ResponsePayload
public Order getOrder(@RequestPayload OrderRequest orderRequest, SoapHeader header) { (3)
checkSoapHeaderForSomething(header);
return orderService.getOrder(orderRequest.getId());
}
}
| 1 | 该类的注释是@Endpoint,将其标记为 Spring-WS 端点。 |
| 2 | 这order方法采用Element(注释为@RequestPayload) 作为参数。
这意味着消息的有效负载作为 DOM 元素在此方法上传递。
该方法有一个void返回类型,表示未发送任何响应消息。
有关终结点方法的详细信息,请参阅@Endpoint处理方法. |
| 3 | 这getOrder方法采用OrderRequest(也用@RequestPayload) 作为参数。
此参数是 JAXB2 支持的对象(它用@XmlRootElement).
这意味着消息的有效负载将作为未封送的对象传递到此方法。
这SoapHeadertype 也作为参数给出。
在调用时,此参数包含请求消息的 SOAP 标头。
该方法还用@ResponsePayload,表示返回值(Order) 用作响应消息的有效负载。
有关终结点方法的详细信息,请参阅@Endpoint处理方法. |
| 4 | 该端点的两种处理方式用@PayloadRoot,指示该方法可以处理哪种类型的请求消息:该方法:getOrder方法对于具有orderRequest本地名称和http://samples命名空间 URI。
对于具有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并覆盖各个方法:
@Configuration
@EnableWs
public class EchoConfig implements WsConfigurer {
@Override
public void addInterceptors(List<EndpointInterceptor> interceptors) {
interceptors.add(new MyInterceptor());
}
@Override
public void addArgumentResolvers(List<MethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new MyArgumentResolver());
}
}
如果WsConfigurer不暴露一些需要配置的更高级的设置,考虑删除@EnableWs并直接从WsConfigurationSupport或DelegatingWsConfiguration.
@Configuration
public class EchoConfig extends WsConfigurationSupport {
@Override
public void addInterceptors(List<EndpointInterceptor> interceptors) {
interceptors.add(new MyInterceptor());
}
@Bean
@Override
public PayloadRootAnnotationMethodEndpointMapping payloadRootAnnotationMethodEndpointMapping() {
// Create or delegate to "super" to create and
// customize properties of PayloadRootAnnotationMethodEndpointMapping
}
}
在接下来的几节中,将对@Endpoint给出了编程模型。
|
与任何其他 Spring Bean 一样,端点默认范围为单例。 也就是说,每个容器创建一个 bean 定义的实例。 作为单例意味着多个线程可以同时使用它,因此端点必须是线程安全的。 如果您想使用不同的作用域,例如 prototype,请参阅 Spring 参考文档。 |
请注意,Spring-WS 中提供的所有抽象基类都是线程安全的,除非类级 Javadoc 中另有说明。
5.4.@Endpoint处理方法
要使终结点实际处理传入的 XML 消息,它需要具有一个或多个处理方法。 处理方法可以采用各种参数和返回类型。 但是,它们通常有一个包含消息有效负载的参数,并且它们返回响应消息的有效负载(如果有)。 本节介绍支持哪些参数和返回类型。
为了指示方法可以处理的消息类型,该方法通常使用@PayloadRoot或@SoapAction注解。
您可以在端点映射中了解有关这些注释的更多信息。
以下示例显示了一种处理方法:
@PayloadRoot(localPart = "order", namespace = "http://samples")
public void order(@RequestPayload Element orderElement) {
Order order = createOrder(orderElement);
orderService.createOrder(order);
}
这order方法采用Element(注释为@RequestPayload) 作为参数。
这意味着消息的有效负载作为 DOM 元素在此方法上传递。
该方法有一个void返回类型,表示未发送任何响应消息。
5.4.1. 处理方法参数
处理方法通常具有一个或多个参数,这些参数引用传入 XML 消息的各个部分。 最常见的是,处理方法具有映射到消息有效负载的单个参数,但它也可以映射到请求消息的其他部分,例如 SOAP 标头。 本节介绍可在处理方法签名中使用的参数。
要将参数映射到请求消息的有效负载,您需要使用@RequestPayload注解。
此注解告诉 Spring-WS 需要将参数绑定到请求有效负载。
支持的参数类型如下表所示。
它显示了支持的类型,是否应该用@RequestPayload,以及任何其他注释。
| 名称 | 支持的参数类型 | @RequestPayload必填? |
附加说明 |
|---|---|---|---|
特拉克斯 |
|
是的 |
默认启用。 |
W3C DOM |
|
是的 |
默认启用 |
多姆4J |
|
是的 |
当 dom4j 在类路径上时启用。 |
JDOM的 |
|
是的 |
当 JDOM 在类路径上时启用。 |
XOM的 |
|
是的 |
当 XOM 在类路径上时启用。 |
StAX |
|
是的 |
当 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)
调用以下方法,请求消息的有效负载被解组到MyJaxb2Object(注释为@XmlRootElement).
消息的有效负载也以 DOM 的形式给出Element.
整个消息上下文作为第三个参数传递。
public void handle(@RequestPayload MyJaxb2Object requestObject, @RequestPayload Element element, Message messageContext)
如您所见,在定义如何处理方法签名时,有很多可能性。
您甚至可以扩展此机制以支持您自己的参数类型。
请参阅 Java 文档DefaultMethodEndpointAdapter和MethodArgumentResolver看看如何。
@XPathParam
一种参数类型需要一些额外的解释:@XPathParam.
这里的想法是,使用 XPath 表达式注释一个或多个方法参数,并且每个此类注释参数都绑定到表达式的计算。
以下示例显示了如何执行此作:
package samples;
@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前缀,我们必须将其绑定到http://samplesNamespace。
这是通过@Namespace注解。
或者,我们可以将此注释放在类型级别,以便对所有处理程序方法甚至包级别(在package-info.java) 将其用于多个端点。
通过使用@XPathParam,您可以绑定到 XPath 支持的所有数据类型:
-
boolean或Boolean. -
double或Double. -
String. -
Node. -
NodeList.
除了此列表之外,您还可以使用任何可以从String通过 Spring 转换服务。
5.4.2. 处理方法返回类型
若要发送响应消息,处理需要指定返回类型。
如果不需要响应消息,该方法可以声明void返回类型。
最常见的是,返回类型用于创建响应消息的有效负载。
但是,您也可以映射到响应消息的其他部分。
本节介绍可在处理方法签名中使用的返回类型。
要将返回值映射到响应消息的有效负载,您需要使用@ResponsePayload注解。
此注释告诉 Spring-WS 返回值需要绑定到响应有效负载。
下表描述了支持的返回类型。
它显示了支持的类型,是否应该用@ResponsePayload,以及任何其他注释。
| 名称 | 支持的返回类型 | @ResponsePayload必填? |
附加说明 |
|---|---|---|---|
无响应 |
|
不 |
默认启用。 |
特拉克斯 |
|
是的 |
默认启用。 |
W3C DOM |
|
是的 |
默认启用 |
多姆4J |
|
是的 |
当 dom4j 在类路径上时启用。 |
JDOM的 |
|
是的 |
当 JDOM 在类路径上时启用。 |
XOM的 |
|
是的 |
当 XOM 在类路径上时启用。 |
JAXB2 |
任何用 |
是的 |
当 JAXB2 在类路径上时启用。 |
OXM的 |
Spring OXM 支持的任何类型 |
是的 |
当 |
在定义处理方法签名时,有很多可能性。甚至可以扩展此机制以支持您自己的参数类型。请参阅DefaultMethodEndpointAdapter和MethodReturnValueHandler看看如何。
5.5. 端点映射
端点映射负责将传入消息映射到适当的端点。默认情况下,某些端点映射处于启用状态,例如,PayloadRootAnnotationMethodEndpointMapping或SoapActionAnnotationMethodEndpointMapping. 但是,我们首先需要检查EndpointMapping.
一EndpointMapping提供EndpointInvocationChain,其中包含与传入请求匹配的端点,并且还可能包含应用于请求和响应的端点拦截器列表。当请求传入时,MessageDispatcher将其交给端点映射,让它检查请求并提出适当的EndpointInvocationChain. 然后,MessageDispatcher调用端点和链中的任何拦截器。
可配置的端点映射的概念非常强大,可以选择包含拦截器(反过来可以纵请求、响应或两者兼而有之)。许多支持功能可以内置到自定义中EndpointMapping实现。 例如,自定义端点映射不仅可以根据消息的内容,还可以根据特定的 SOAP 标头(或者实际上是多个 SOAP 标头)来选择端点。
大多数端点映射继承自AbstractEndpointMapping,它提供了一个 'interceptors' 属性,这是要使用的拦截器的列表。EndpointInterceptors在拦截请求 —EndpointInterceptor接口.
如端点中所述,该@Endpointstyle 允许您在一个端点类中处理多个请求。这是MethodEndpointMapping. 此映射确定要为传入请求消息调用哪个方法。
有两个端点映射可以将请求定向到方法:PayloadRootAnnotationMethodEndpointMapping和SoapActionAnnotationMethodEndpointMapping您可以使用以下命令启用这两种方法<sws:annotation-driven/>在您的应用程序上下文中。
这PayloadRootAnnotationMethodEndpointMapping使用@PayloadRoot注释,使用localPart和namespace元素,以使用特定的限定名称标记方法。
每当消息传入带有有效负载根元素的此限定名称时,都会调用该方法。
或者,SoapActionAnnotationMethodEndpointMapping使用@SoapAction注释以使用特定 SOAP作标记方法。
每当收到带有此消息时SOAPActionheader,则调用该方法。
AbstractEndpointMappingimplementations 提供了一个defaultEndpoint属性,用于配置在配置的映射不会导致匹配的终结点时使用的终结点。
5.5.1. WS 寻址
WS-Addressing 指定传输中立路由机制。
它基于To和ActionSOAP 标头,分别指示 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-WS 中,WS-Addressing 作为端点映射实现。
通过使用此映射,您可以将 WS-Addressing作与端点相关联,类似于SoapActionAnnotationMethodEndpointMapping前面描述过。
用AnnotationActionEndpointMapping
这AnnotationActionEndpointMapping类似于SoapActionAnnotationMethodEndpointMapping但使用 WS-Addressing 标头而不是 SOAP Action 传输标头。
要使用AnnotationActionEndpointMapping,则使用@Action注释,类似于@PayloadRoot和@SoapAction中描述的注释@Endpoint处理方法和终结点映射。
以下示例显示了如何执行此作:
package samples;
@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-Addressing 的请求Action之http://samples/RequestOrder到getOrder方法。
请求http://samples/CreateOrder被路由到order方法。
默认情况下,AnnotationActionEndpointMapping支持 WS-Addressing 的 1.0(2006 年 5 月)和 2004 年 8 月版本。
这两个版本最受欢迎,可与 Axis 1 和 2、JAX-WS、XFire、Windows Communication Foundation (WCF) 和 Windows Services Enhancements (WSE) 3.0 互作。
如有必要,可以将特定版本的规范注入到versions财产。
除了@Action注释,您可以使用@Address注解。
如果设置,则该值将与Toheader 传入消息的属性。
最后,还有messageSenders属性,这是将响应消息发送到非匿名、越界地址所必需的。
您可以设置MessageSender实现,与您在WebServiceTemplate.
请参阅 URI 和传输。
5.5.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作为有效负载根命名空间。
我们可以定义一个localPart属性,以及namespaceUri以进一步限制拦截器应用的消息。
最后,我们定义了两个拦截器,当消息具有http://www.example.com/SoapActionSOAP作。
请注意,第二个拦截器实际上是对<interceptors>元素。
您可以在<interceptors>元素。
当您使用@Configuration类,您可以实现WsConfigurer要添加拦截器:
@Configuration
@EnableWs
public class MyWsConfiguration implements WsConfigurer {
@Override
public void addInterceptors(List<EndpointInterceptor> interceptors) {
interceptors.add(new MyPayloadRootInterceptor());
}
}
拦截器必须实现EndpointInterceptor.
该接口定义了三种方法,一种可用于在处理实际端点之前处理请求消息,一种可用于处理正常响应消息,另一种可用于处理故障消息。
后两个在处理终结点后调用。
这三种方法应该提供足够的灵活性来进行各种预处理和后处理。
这handleRequest(..)方法返回一个布尔值。
您可以使用此方法中断或继续调用链的处理。
当此方法返回true,端点处理链将继续。
当它返回时false这MessageDispatcher将此解释为拦截器本身已经处理了事情,并且不继续处理调用链中的其他拦截器和实际端点。
这handleResponse(..)和handleFault(..)方法也有一个布尔返回值。
当这些方法返回false,则响应不会发送回客户端。
有多个标准EndpointInterceptor可在 Web 服务中使用的实现。
此外,还有XwsSecurityInterceptor,其中描述在XwsSecurityInterceptor.
PayloadLoggingInterceptor和SoapEnvelopeLoggingInterceptor
在开发 Web 服务时,记录传入和传出的 XML 消息可能很有用。
Spring WS 通过PayloadLoggingInterceptor和SoapEnvelopeLoggingInterceptor类。
前者仅记录消息的有效负载。
后者记录整个 SOAP 信封,包括 SOAP 标头。
以下示例显示了如何定义PayloadLoggingInterceptor在端点映射中:
<sws:interceptors>
<bean class="org.springframework.ws.server.endpoint.interceptor.PayloadLoggingInterceptor"/>
</sws:interceptors>
这两种拦截器都有两个属性,logRequest和logResponse,可以设置为false禁用请求或响应消息的日志记录。
您可以使用WsConfigurer如前所述,对于PayloadLoggingInterceptor也。
PayloadValidatingInterceptor
使用契约优先开发风格的好处之一是,我们可以使用模式来验证传入和传出的 XML 消息。
Spring-WS 通过PayloadValidatingInterceptor.
此拦截器需要引用一个或多个 W3C XML 或 RELAX NG 模式,并且可以设置为验证请求、响应或两者。
|
虽然请求验证听起来是个好主意,但它使生成的 Web 服务变得非常严格。 通常,请求是否验证并不重要,只有当端点能够获得足够的信息来满足请求时。 验证响应是一个好主意,因为终结点应遵守其架构。 记住波斯特尔定律:“做事要保守;在接受别人的东西上要自由。 |
以下示例使用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>
当然,您可以使用WsConfigurer如前所述,对于PayloadValidatingInterceptor也。
用PayloadTransformingInterceptor
为了将有效负载转换为另一种 XML 格式,Spring-WS 提供了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.
请注意,由于端点拦截器是在端点映射级别注册的,因此您可以创建应用于“旧样式”消息的端点映射,并将拦截器添加到该映射中。
因此,转换仅适用于这些“旧式”信息。
您可以使用WsConfigurer如前所述,对于PayloadTransformingInterceptor也。
5.6. 处理异常
Spring-WS 提供EndpointExceptionResolver实现,以减轻在与请求匹配的端点处理消息时发生意外异常的痛苦。
端点异常解析器有点类似于可以在 Web 应用程序描述符中定义的异常映射web.xml.
但是,它们提供了一种更灵活的方式来处理异常。
它们提供有关引发异常时调用的终结点的信息。
此外,处理异常的编程方式为您提供了更多有关如何适当响应的选项。
您可以以任何您想要的方式处理异常,而不是通过提供异常和堆栈跟踪来公开应用程序的内部结构,例如,通过返回具有特定故障代码和字符串的 SOAP 错误。
端点异常解析器由MessageDispatcher,因此不需要显式配置。
除了实现EndpointExceptionResolver接口,这只是实现resolveException(MessageContext, endpoint, Exception)方法,您也可以使用提供的实现之一。
最简单的实现是SimpleSoapExceptionResolver,它会创建 SOAP 1.1 服务器或 SOAP 1.2 接收器故障,并使用异常消息作为故障字符串。
您可以对其进行子类化以自定义故障,如以下示例所示:
public class CustomSoapExceptionResolver extends SimpleSoapExceptionResolver {
private final Transformer transformer;
public CustomSoapExceptionResolver(Transformer transformer) {
this.transformer = transformer;
}
@Override
protected void customizeFault(MessageContext messageContext, Object endpoint, Exception exception,
SoapFault fault) {
SoapFaultDetail faultDetail = fault.addFaultDetail();
try {
this.transformer.transform(new StringSource("""
<ns2:YourCustomException xmlns:ns2="http://serviceendpoint/">
<errorCode>Your custom error code</exName>
<systemMessage>A system message</exMessage>
</ns2:YourCustomException >
"""), faultDetail.getResult());
}
catch (TransformerException ex) {
throw new IllegalArgumentException("Failed to write detail", ex);
}
}
}
这SimpleSoapExceptionResolver是默认值,但可以通过显式添加另一个解析器来覆盖它。
5.6.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到错误字符串为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.6.2. 使用SoapFaultAnnotationExceptionResolver
您还可以使用@SoapFault注释,以指示每当抛出异常时应返回的 SOAP 错误。
要选择这些注释,您需要添加SoapFaultAnnotationExceptionResolver到您的应用程序上下文。注释的元素包括故障代码枚举、故障字符串或原因以及语言。
以下示例显示了此类异常:
package samples;
@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.7. 服务器端测试
在测试 Web 服务端点时,有两种可能的方法:
-
编写单元测试,您可以在其中提供(模拟)参数供终结点使用。 这种方法的优点是它很容易完成(特别是对于用
@Endpoint). 缺点是,您没有真正测试通过网络发送的 XML 消息的确切内容。 -
编写集成测试,它会测试消息的内容。
第一种方法可以通过 Mockito、EasyMock 等模拟框架轻松完成。 下一节重点介绍编写集成测试。
5.7.1. 编写服务器端集成测试
Spring-WS 支持创建端点集成测试。 在此上下文中,端点是处理 (SOAP) 消息的类(请参阅端点)。
集成测试支持存在于org.springframework.ws.test.server包。
该包中的核心类是MockWebServiceClient.
基本思想是,此客户端创建请求消息,然后将其发送到标准中配置的端点MessageDispatcherServlet应用程序上下文(请参阅MessageDispatcherServlet).
这些端点处理消息并创建响应。
然后,客户端收到此响应,并根据注册的预期对其进行验证。
的典型用法MockWebServiceClient是:。
-
创建一个
MockWebServiceClient实例调用MockWebServiceClient.createClient(ApplicationContext)或MockWebServiceClient.createClient(WebServiceMessageReceiver, WebServiceMessageFactory). -
通过调用发送请求消息
sendRequest(RequestCreator),可能通过使用默认的RequestCreator中提供的实现RequestCreators(可以静态导入)。 -
通过调用
andExpect(ResponseMatcher),可能通过使用默认的ResponseMatcher中提供的实现ResponseMatchers(可以静态导入)。 通过链接可以设置多个期望andExpect(ResponseMatcher)调用。
|
|
|
您可以在单元测试中依赖 Spring-WS 中提供的标准日志记录功能。 有时,检查请求或响应消息以找出特定测试失败的原因可能会很有用。 有关详细信息,请参阅消息日志记录和跟踪。 |
例如,考虑以下 Web 服务端点类:
@Endpoint (1)
public class CustomerEndpoint {
@ResponsePayload (2)
public CustomerCountResponse getCustomerCount(
@RequestPayload CustomerCountRequest request) {
CustomerCountResponse response = new CustomerCountResponse();
response.setCustomerCount(10);
return response;
}
}
| 1 | 这CustomerEndpoint在注释为@Endpoint.
请参阅端点。 |
| 2 | 这getCustomerCount()方法采用CustomerCountRequest作为其参数,并返回一个CustomerCountResponse.
这两个类都是由封送处理程序支持的对象。
例如,他们可以有一个@XmlRootElementJAXB2 支持的注释。 |
以下示例显示了CustomerEndpoint:
@RunWith(SpringJUnit4ClassRunner.class) (1)
@ContextConfiguration("spring-ws-servlet.xml")
public class CustomerEndpointIntegrationTest {
@Autowired
private ApplicationContext applicationContext; (2)
private MockWebServiceClient mockClient;
@Before
public void createClient() {
mockClient = MockWebServiceClient.createClient(applicationContext); (3)
}
@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)). (4)
andExpect(payload(responsePayload));
}
}
| 1 | 此测试使用 Spring Framework 中提供的标准测试工具。 这不是必需的,但通常是设置测试的最简单方法。 |
| 2 | 应用程序上下文是标准的 Spring-WS 应用程序上下文(请参阅MessageDispatcherServlet),读取自spring-ws-servlet.xml.
在这种情况下,应用程序上下文包含CustomerEndpoint(或者也许是<context:component-scan />被使用)。 |
| 3 | 在@Before方法,我们创建一个MockWebServiceClient通过使用createClient工厂方法。 |
| 4 | 我们通过调用sendRequest()使用withPayload() RequestCreator由静态导入的RequestCreators(参见用RequestCreator和RequestCreators).
我们还通过调用andExpect()使用payload() ResponseMatcher由静态导入的ResponseMatchers(参见用ResponseMatcher和ResponseMatchers). |
这部分测试可能看起来有点令人困惑,但 IDE 的代码完成功能会有很大帮助。
打字后sendRequest(,您的 IDE 可以为您提供可能的请求创建策略列表,前提是您静态导入RequestCreators.
这同样适用于andExpect(),前提是您静态导入ResponseMatchers.
5.7.2. 使用RequestCreator和RequestCreators
最初,MockWebServiceClient需要为端点创建要使用的请求消息。
客户端使用RequestCreator为此目的的策略接口:
public interface RequestCreator {
WebServiceMessage createRequest(WebServiceMessageFactory messageFactory)
throws IOException;
}
您可以编写此接口的自己的实现,使用消息工厂创建请求消息,但您当然不必这样做。
这RequestCreatorsclass 提供了一种创建RequestCreator基于withPayload()方法。
通常以静态方式导入RequestCreators.
5.7.3. 使用ResponseMatcher和ResponseMatchers
当端点处理了请求消息并收到响应时,MockWebServiceClient可以验证此响应消息是否满足某些期望。
客户端使用ResponseMatcher为此目的的策略接口:
public interface ResponseMatcher {
void match(WebServiceMessage request, WebServiceMessage response)
throws IOException, AssertionError;
}
再一次,你可以编写自己的这个接口的实现,抛出AssertionError如果消息不符合您的期望,但您当然不必这样做,因为ResponseMatchers类提供标准ResponseMatcher供您在测试中使用的实现。
通常以静态方式导入此类。
这ResponseMatchersclass 提供以下响应匹配器:
ResponseMatchers方法 |
描述 |
|---|---|
|
期望给定的响应有效负载。 可能包括 XMLUnit 占位符。 |
|
期望响应有效负载根据给定的 XSD 架构进行验证。 |
|
期望给定的 XPath 表达式存在、不存在或计算为给定值。 |
|
期望给定的 SOAP 标头存在于响应消息中。 |
|
需要给定的 SOAP 有效负载。 可能包括 XMLUnit 占位符。 |
|
期望响应消息不包含 SOAP 错误。 |
|
期望响应消息包含特定的 SOAP 错误。 |
您可以通过链接来设置多个响应期望andExpect()调用:
mockClient.sendRequest(...).
andExpect(payload(expectedResponsePayload)).
andExpect(validPayload(schemaResource));
有关ResponseMatchers,请参阅 Javadoc。
6. 在客户端上使用 Spring-WS
Spring-WS 提供了一个客户端 Web 服务 API,允许对 Web 服务进行一致的、XML 驱动的访问。 它还满足了封送程序和取消封送程序的使用,以便您的服务层代码可以专门处理 Java 对象。
这org.springframework.ws.client.corepackage 提供了使用客户端访问 API 的核心功能。
它包含简化 Web 服务使用的模板类,就像核心 Spring 一样JdbcTemplate为 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 本身提供的功能。
替代方案是JdkHttpClientMessageSender使用 JDK 的HttpClient或HttpComponents5MessageSender/SimpleHttpComponents5MessageSender,它们使用 Apache 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.HttpComponents5MessageSender">
<property name="credentials">
<bean class="org.apache.hc.client5.http.auth.UsernamePasswordCredentials">
<constructor-arg value="john"/>
<constructor-arg value="secret"/>
</bean>
</property>
</bean>
</property>
<property name="defaultUri" value="http://example.com/WebService"/>
</bean>
JMS 传输
为了通过 JMS 发送消息,Spring-WS 提供了JmsMessageSender.
此类使用 Spring 框架的设施来转换WebServiceMessage进入 JMSMessage,将其发送到Queue或Topic,并接收响应(如果有)。
使用JmsMessageSender,您需要将defaultUri或uri参数添加到 JMS URI,该 URI 至少由jms:前缀和目标名称。
JMS URI 的一些示例包括:jms:SomeQueue,jms:SomeTopic?priority=3&deliveryMode=NON_PERSISTENT和jms:RequestQueue?replyToName=ResponseName.
有关此 URI 语法的更多信息,请参阅Javadoc 的JmsMessageSender.
默认情况下,JmsMessageSender发送 JMSBytesMessage,但您可以覆盖它以使用TextMessages通过使用messageType参数 — 例如jms:Queue?messageType=TEXT_MESSAGE.
请注意BytesMessages是首选类型,因为TextMessages不可靠地支持附件和字符编码。
以下示例显示了如何将 JMS 传输与 Artemis 连接工厂结合使用:
<beans>
<bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory"/>
<bean id="connectionFactory" class="org.apache.activemq.artemis.jms.client.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-WS 还提供了一个电子邮件传输,您可以使用它通过 SMTP 发送 Web 服务消息并通过 POP3 或 IMAP 检索它们。
客户端电子邮件功能包含在MailMessageSender.
此类从请求创建电子邮件WebServiceMessage并通过 SMTP 发送。
然后,它等待响应消息到达传入的 POP3 或 IMAP 服务器。
要使用MailMessageSender,将defaultUri或uri参数设置为mailtoURI — 例如,mailto:[email protected]或mailto:server@localhost?subject=SOAP%20Test.
确保邮件发件人已正确配置transportUri,指示用于发送请求的服务器(通常是 SMTP 服务器),以及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 <[email protected]>"/>
<property name="transportUri" value="smtp://client:[email protected]"/>
<property name="storeUri" value="imap://client:[email protected]/INBOX"/>
</bean>
</property>
<property name="defaultUri" value="mailto:[email protected]?subject=SOAP%20Test"/>
</bean>
</beans>
XMPP 传输
Spring-WS 还提供了一个 XMPP (Jabber) 传输,您可以使用它通过 XMPP 发送和接收 Web 服务消息。
客户端 XMPP 功能包含在XmppMessageSender.
此类从请求创建 XMPP 消息WebServiceMessage并通过 XMPP 发送。
然后,它会侦听响应消息是否到达。
要使用XmppMessageSender,将defaultUri或uri参数设置为xmppURI — 例如,xmpp:[email protected].
发件人还需要XMPPConnection工作,这可以通过使用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:[email protected]"/>
</bean>
</beans>
6.1.2. 发送和接收WebServiceMessage
这WebServiceTemplate包含许多用于发送和接收 Web 服务消息的便捷方法。有一些方法接受并返回Source以及那些返回Result. 此外,还有一些方法将对象封送和取消封送到 XML。以下示例将简单的 XML 消息发送到 Web 服务:
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="com.example.WebServiceClient">
<property name="defaultUri" value="http://localhost:8080/WebService"/>
</bean>
</beans>
前面的示例使用WebServiceTemplate向位于http://localhost:8080/WebService(在simpleSendAndReceive()方法),并将结果写入控制台。 这WebServiceTemplate注入了默认 URI,使用该 URI 是因为 Java 代码中没有显式提供 URI。
请注意,WebServiceTemplate一旦配置,类是线程安全的(假设它的所有依赖项也是线程安全的,Spring-WS 附带的所有依赖项都是这种情况),因此多个对象可以使用相同的共享WebServiceTemplate实例。
这WebServiceTemplate公开一个零参数构造函数,并且messageFactory和messageSender可用于构造实例的 bean 属性(通过使用 Spring 容器或纯 Java 代码)。
或者,考虑从 Spring-WS 派生WebServiceGatewaySupport方便基类,它公开了 bean 属性以实现轻松配置。
(您不必扩展此基类,它仅作为方便类提供)。
6.1.3. 发送和接收 POJO — 封送和解封组
为了方便发送纯 Java 对象,WebServiceTemplate有多个send(..)采用Object作为消息数据内容的参数。
方法marshalSendAndReceive(..)在WebServiceTemplateclass 将请求对象到 XML 的转换委托给Marshaller以及将响应 XML 转换为对象的Unmarshaller.
(有关封送和解封处理程序的更多信息,请参阅 Spring Framework 参考文档)。
通过使用封送程序,您的应用程序代码可以专注于正在发送或接收的业务对象,而不必关心它如何表示为 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-WS 还在客户端支持此规范。
要在客户端上设置 WS-Addressing 标头,您可以使用ActionCallback.
此回调将所需的作标头作为参数。
它还具有用于指定 WS-Addressing 版本的构造函数和To页眉。
如果未指定,则To标头默认为正在建立的连接的 URL。
以下示例将Actionheader 到http://samples/RequestOrder:
webServiceTemplate.marshalSendAndReceive(o, new ActionCallback("http://samples/RequestOrder"));
6.1.5. 使用WebServiceMessageExtractor
这WebServiceMessageExtractorinterface 是一个低级回调接口,可让您完全控制提取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 消息的确切内容,尤其是在模拟整个客户端类时。 -
编写集成测试,测试消息的内容。
第一种方法可以通过模拟框架轻松完成,例如 Mockito、EasyMock 等。 下一节重点介绍编写集成测试。
6.2.1. 编写客户端集成测试
Spring-WS 支持创建 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()以确保满足所有期望。
|
|
|
您可以在单元测试中依赖 Spring-WS 中提供的标准日志记录功能。 有时,检查请求或响应消息以找出特定测试失败的原因可能会很有用。 有关详细信息,请参阅消息日志记录和跟踪。 |
例如,考虑以下 Web 服务客户端类:
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延伸WebServiceGatewaySupport,它为其提供了一个webServiceTemplate财产。 |
| 2 | CustomerCountRequest是由封送程序支持的对象。
例如,它可以有一个@XmlRootElementJAXB2 支持的注释。 |
| 3 | 这CustomerClient使用WebServiceTemplate提供方WebServiceGatewaySupport将请求对象封送到 SOAP 消息中,并将其发送到 Web 服务。
响应对象被解编组为CustomerCountResponse. |
以下示例显示了CustomerClient:
@RunWith(SpringJUnit4ClassRunner.class) (1)
@ContextConfiguration("integration-test.xml")
public class CustomerClientIntegrationTest {
@Autowired
private CustomerClient client; (2)
private MockWebServiceServer mockServer; (3)
@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));(4)
int result = client.getCustomerCount(); (5)
assertEquals(10, result);
mockServer.verify(); (6)
}
}
| 1 | 此测试使用 Spring Framework 中提供的标准测试工具。 这不是必需的,但通常是设置测试的最简单方法。 |
| 2 | 这CustomerClient配置在integration-test.xml并使用@Autowired. |
| 3 | 在@Before方法,我们创建一个MockWebServiceServer通过使用createServer工厂方法。 |
| 4 | 我们通过调用expect()使用payload() RequestMatcher由静态导入的RequestMatchers(参见用RequestMatcher和RequestMatchers). 我们还通过调用andRespond()使用withPayload() ResponseCreator由静态导入的ResponseCreators(参见用ResponseCreator和ResponseCreators). 这部分测试可能看起来有点令人困惑,但 IDE 的代码完成功能会很有帮助。在你输入expect(,您的 IDE 可以为您提供可能的请求匹配策略列表,前提是您静态导入RequestMatchers.
这同样适用于andRespond(,前提是您静态导入ResponseCreators. |
| 5 | 我们称getCustomerCount()在CustomerClient,因此使用WebServiceTemplate. 到目前为止,该模板已经设置为“测试模式”,因此此方法调用不会建立真正的 (HTTP) 连接。我们还根据方法调用的结果做出了一些 JUnit 断言。 |
| 6 | 我们称verify()在MockWebServiceServer,验证是否实际收到了预期消息。 |
6.2.2. 使用RequestMatcher和RequestMatchers
为了验证请求消息是否满足某些期望,请MockWebServiceServer使用RequestMatcherstrategy 接口。该接口定义的合约如下:
public interface RequestMatcher {
void match(URI uri, WebServiceMessage request)
throws IOException, AssertionError;
}
你可以编写这个接口的自己的实现,抛出AssertionError当消息不符合您的期望时,但您当然不必这样做。 这RequestMatchers类提供标准RequestMatcher供您在测试中使用的实现。
通常以静态方式导入此类。
这RequestMatchersclass 提供以下请求匹配器:
RequestMatchers方法 |
描述 |
|---|---|
|
期望任何类型的请求。 |
|
需要给定的请求有效负载。 可能包括 XMLUnit 占位符 |
|
期望请求有效负载根据给定的 XSD 架构进行验证。 |
|
期望给定的 XPath 表达式存在、不存在或计算为给定值。 |
|
期望请求消息中存在给定的 SOAP 标头。 |
|
需要给定的 SOAP 有效负载。 可能包括 XMLUnit 占位符 |
|
需要与给定 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供您在测试中使用的实现。
通常以静态方式导入此类。
这ResponseCreatorsclass 提供以下响应:
ResponseCreators方法 |
描述 |
|---|---|
|
使用给定有效负载创建响应消息。 |
|
在响应连接中创建错误。 此方法使您有机会测试错误处理。 |
|
从响应连接读取时引发异常。 此方法使你有机会测试异常处理。 |
|
创建具有给定 SOAP 错误的响应消息。 此方法使您有机会测试故障处理。 |
有关由RequestMatchers,请参阅 Javadoc。
7. 使用 Spring-WS 保护您的 Web 服务
本章介绍如何将 WS-Security 方面添加到 Web 服务中。 我们专注于 WS-Security 的三个不同领域:
-
身份验证:这是确定委托人是否是他们声称的身份的过程。 在这种情况下,“主体”通常是指可以在应用程序中执行作的用户、设备或其他系统。
-
数字签名:消息的数字签名是基于文档和签名者私钥的一条信息。 它是通过使用哈希函数和私有签名函数(使用签名者的私钥加密)创建的。
-
加密和解密:加密是将数据转换为没有适当密钥就无法读取的形式的过程。它主要用于对任何不打算使用的信息隐藏信息。解密是加密的反面。它是将加密数据转换回可读形式的过程。
这三个区域是通过使用XwsSecurityInterceptor或Wss4jSecurityInterceptor,我们在XwsSecurityInterceptor和用Wss4jSecurityInterceptor分别。
|
WS-Security(尤其是加密和签名)需要大量内存,并且会降低性能。 如果性能对您很重要,您可能需要考虑不使用 WS-Security 或使用基于 HTTP 的安全性。 |
7.1.XwsSecurityInterceptor
这XwsSecurityInterceptor是一个EndpointInterceptor(参见拦截请求 —EndpointInterceptor接口)基于 SUN 的 XML 和 Web 服务安全包 (XWSS)。
此 WS-Security 实现是 Java Web Services Developer Pack (Java WSDP) 的一部分。
与任何其他端点拦截器一样,它在端点映射中定义(请参阅端点映射)。 这意味着您可以有选择地添加 WS-Security 支持。 某些端点映射需要它,而其他端点映射则不需要。
|
XWSS 需要 SUN SAAJ 参考实现。
WSS4J 拦截器没有这些要求(请参阅用 |
这XwsSecurityInterceptor需要安全策略文件才能运行。
此 XML 文件告诉拦截器需要从传入的 SOAP 消息中获取哪些安全方面,以及要添加到传出消息中哪些方面。
以下部分将介绍策略文件的基本格式,但提供了更深入的教程。
您可以使用policyConfiguration属性,这需要一个 Spring 资源。
策略文件可以包含多个元素,例如,要求在传入邮件上使用用户名Tokens,并对所有传出邮件进行签名。
它包含一个SecurityConfiguration元素(不是JAXRPCSecurity元素)作为其根。
此外,安全拦截器需要一个或多个CallbackHandler实例进行作。
这些处理程序用于检索证书、私钥、验证用户凭据等。
Spring-WS 为最常见的安全问题提供了处理程序,例如,针对 Spring Security 身份验证管理器进行身份验证以及根据 X509 证书对传出消息进行签名。
以下部分指示要用于哪个安全问题的回调处理程序。
您可以使用callbackHandler或callbackHandlers财产。
以下示例演示了如何连接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.KeyStoreclass 表示加密密钥和证书的存储工具。
它可以包含三种不同类型的元素:
-
私钥:这些密钥用于自我身份验证。 私钥附有相应公钥的证书链。 在 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
要在XwsSecurityInterceptor,您需要定义一个KeyStoreCallbackHandler.
此回调具有三个属性,类型为keystore: (keyStore,trustStore和symmetricStore).
处理程序使用的确切存储取决于此处理程序要执行的加密作。
对于私钥作,请执行keyStore被使用。
对于对称键作,命令symmetricStore被使用。
为了确定信任关系,请trustStore被使用。
下表指出了这一点:
| 加密作 | 使用的密钥库 |
|---|---|
证书验证 |
第一 |
基于私密钥的解密 |
|
基于对称密钥的解密 |
|
基于公密钥证书的加密 |
|
基于对称密钥的加密 |
|
签署 |
|
签名验证 |
|
此外,KeyStoreCallbackHandler有一个privateKeyPassword属性,应设置为解锁“keyStore”中包含的私钥。
如果symmetricStore未设置,则默认为keyStore.
如果未设置密钥或信任存储,则回调处理程序使用标准 Java 机制来加载或创建它。
看KeyStoreCallbackHandler了解更多详情。
例如,如果您想使用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 中,身份验证可以采用两种形式:使用用户名和密码Tokens(使用纯文本密码或密码摘要)或使用 X509 证书。
纯文本用户名身份验证
最简单的用户名身份验证形式使用纯文本密码。
在此方案中,SOAP 消息包含一个UsernameToken元素,其本身包含一个Username元素和Password包含纯文本密码的元素。
纯文本身份验证可以与 HTTP 服务器提供的基本身份验证进行比较。
|
纯文本密码不是很安全。 因此,如果使用传输层,则应始终向传输层添加额外的安全措施(例如,使用 HTTPS 而不是纯 HTTP)。 |
要求每条传入邮件都包含UsernameToken使用纯文本密码时,安全策略文件应包含RequireUsernameToken元素,使用passwordDigestRequired属性设置为false.
如需更多详细信息,请查看可能的子元素的官方参考。
以下列表显示了如何包含RequireUsernameToken元素:
<xwss:SecurityConfiguration xmlns:xwss="http://java.sun.com/xml/ns/xwss/config">
...
<xwss:RequireUsernameToken passwordDigestRequired="false" nonceRequired="false"/>
...
</xwss:SecurityConfiguration>
如果用户名Tokens不存在,则XwsSecurityInterceptor向发送方返回 SOAP 错误。
如果存在,它会触发一个PasswordValidationCallback使用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它创造的。
如果身份验证成功,Tokens将存储在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.authentication.ProviderManager">
<constructor-arg>
<bean class="org.springframework.security.providers.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="userDetailsService"/>
</bean>
</constructor-arg>
</bean>
<bean id="userDetailsService" class="com.mycompany.app.dao.UserDetailService" />
...
</beans>
用JaasPlainTextPasswordValidationCallbackHandler
这JaasPlainTextPasswordValidationCallbackHandler基于标准 Java 身份验证和授权服务。
提供 JAAS 的完整介绍超出了本文档的范围,但提供了教程。
这JaasPlainTextPasswordValidationCallbackHandler只需要一个loginContextName作。
它创建了一个新的 JAASLoginContext通过使用此名称并处理标准 JAASNameCallback和PasswordCallback使用 SOAP 消息中提供的用户名和密码。
这意味着此回调处理程序与任何 JAAS 集成LoginModule在login()阶段,这是标准行为。
您可以连接一个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>
如果用户名Tokens不存在,则XwsSecurityInterceptor向发送方返回 SOAP 错误。
如果存在,它会触发一个PasswordValidationCallback使用DigestPasswordRequest到已注册的处理程序。
在 Spring-WS 中,有两个类处理此特定回调:SimplePasswordValidationCallbackHandler和SpringDigestPasswordValidationCallbackHandler.
用SimplePasswordValidationCallbackHandler
这SimplePasswordValidationCallbackHandler可以处理纯文本密码和密码摘要。
它在用SimplePasswordValidationCallbackHandler.
用SpringDigestPasswordValidationCallbackHandler
这SpringDigestPasswordValidationCallbackHandler需要 Spring SecurityUserDetailService作。
它使用此服务来检索Tokens中指定的用户的密码。
然后,将此详细信息对象中包含的密码摘要与消息中的摘要进行比较。
如果它们相等,则用户已成功进行身份验证,并且UsernamePasswordAuthenticationToken存储在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 密钥库来验证证书。
此证书验证过程包括以下步骤:
-
处理程序检查证书是否在私有
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它创建的。配置的身份验证管理器应提供可以处理此Tokens的提供程序(通常是X509AuthenticationProvider). 如果身份验证成功,Tokens将存储在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 参考文档。
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到已注册的处理程序。
在 Spring-WS 中,一个类处理这个特定的回调:KeyStoreCallbackHandler.
用KeyStoreCallbackHandler
如 KeyStoreCallbackHandler 中所述,KeyStoreCallbackHandler使用java.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 中,KeyStoreCallbackHandlerclass 处理这个特定的回调。
用KeyStoreCallbackHandler
如 KeyStoreCallbackHandler 中所述,KeyStoreCallbackHandler使用java.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到已注册的处理程序。
在 Spring-WS 中,KeyStoreCallbackHandlerclass 处理这个特定的回调。
用KeyStoreCallbackHandler
如 KeyStoreCallbackHandler 中所述,KeyStoreCallbackHandler使用java.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 中,KeyStoreCallbackHandlerclass 处理这个特定的回调。
用KeyStoreCallbackHandler
如 KeyStoreCallbackHandler 中所述,KeyStoreCallbackHandler使用java.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抛出一个WsSecuritySecurementException或WsSecurityValidationException分别。
这些异常绕过标准异常处理机制,但由拦截器本身处理。
WsSecuritySecurementException异常由handleSecurementException方法XwsSecurityInterceptor.
默认情况下,此方法会记录错误并停止对消息的进一步处理。
同样地WsSecurityValidationException异常由handleValidationException方法XwsSecurityInterceptor.
默认情况下,此方法创建 SOAP 1.1 客户端或 SOAP 1.2 发送方错误,并将其作为响应发送回。
双handleSecurementException和handleValidationException是受保护的方法,您可以覆盖这些方法以更改其默认行为。 |
7.2. 使用Wss4jSecurityInterceptor
这Wss4jSecurityInterceptor是一个EndpointInterceptor(参见拦截请求 —EndpointInterceptor接口)基于 Apache 的 WSS4J。
WSS4J 实现以下标准:
-
OASIS Web 服务安全性:SOAP 邮件安全 1.0 标准200401,2004 年 3 月。
-
用户名Tokens配置文件 V1.0。
-
X.509 Tokens配置文件 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>
下表显示了可用的验证作:
| 验证作 | 描述 |
|---|---|
|
验证用户名Tokens |
|
验证时间戳 |
|
解密消息 |
|
验证签名 |
|
未执行任何作 |
下表显示了可用的保护作:
| 安全作 | 描述 |
|---|---|
|
添加用户名Tokens |
|
添加用户名Tokens和签名用户名Tokens密钥 |
|
添加时间戳 |
|
加密响应 |
|
签署响应 |
|
未执行任何作 |
动作的顺序很重要,由拦截器强制执行。
如果其安全作的执行顺序与validationActions,则拦截器拒绝传入的 SOAP 消息。
7.2.2. 处理数字证书
对于需要与密钥库交互或证书处理(签名、加密和解密作)的加密作,WSS4J 需要org.apache.ws.security.components.crypto.Crypto.
Crypto实例可以从 WSS4J 的CryptoFactory或者更方便地使用 Spring-WSCryptoFactoryBean.
加密工厂豆
Spring-WS提供了一个方便的工厂豆,CryptoFactoryBean,用于构造和配置Crypto通过强类型属性(首选)或通过Properties对象。
默认情况下,CryptoFactoryBean返回org.apache.ws.security.components.crypto.Merlin.
您可以通过将cryptoProvider属性(或其等效项org.apache.ws.security.crypto.providerstring 属性)。
以下示例配置使用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.
验证用户名Tokens
Spring-WS 提供了一组回调处理程序以与 Spring Security 集成。
此外,一个简单的回调处理程序SimplePasswordValidationCallbackHandler,用于使用内存中的Properties对象。
回调处理程序通过validationCallbackHandler的Wss4jSecurityInterceptor财产。
用SimplePasswordValidationCallbackHandler
SimplePasswordValidationCallbackHandler根据内存中的纯文本和摘要用户名TokensProperties对象。
您可以按如下方式配置它:
<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 Security 验证纯文本和摘要密码UserDetailService作。
它使用此服务来检索Tokens中指定的用户的密码(或密码摘要)。
然后,将此详细信息对象中包含的密码(或密码摘要)与消息中的摘要进行比较。
如果它们相等,则用户已成功进行身份验证,并且UsernamePasswordAuthenticationToken存储在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>
添加用户名Tokens
将用户名Tokens添加到外发消息就像添加UsernameToken到securementActions属性的Wss4jSecurityInterceptor并指定securementUsername和securementPassword.
可以通过设置securementPasswordType财产。
可能的值为PasswordText对于纯文本密码或PasswordDigest对于摘要密码,这是默认值。
以下示例生成带有摘要密码的用户名Tokens:
<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财产。
该值必须是一个列表,其中包含用空格分隔的所需元素的名称(区分大小写)。
以下示例生成一个带有纯文本密码的用户名Tokens,即Nonce和一个Created元素:
<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元素,并将其包含在传出消息中。
证书的名称和密码通过securementUsername和securementPasswordproperties 的属性,如下例所示:
<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并通过设置timeToLive财产。
拦截器始终拒绝已经过期的时间戳,无论timeToLive是。
在以下示例中,拦截器将时间戳有效期限制为 10 秒,拒绝该窗口之外的任何有效时间戳Tokens:
<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元素:
<property name="securementSignatureParts"
value="{}{http://www.springframework.org/spring-ws/samples/echo}echoResponse"/>
要指定没有命名空间的元素,请使用字符串Null(区分大小写),作为命名空间名称。
如果请求中没有其他元素的本地名称为Body,则 SOAP 命名空间标识符可以为空 ()。{}
签名确认
通过设置enableSignatureConfirmation自true.
请注意,签名确认作跨越请求和响应。
这意味着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必须注射KeyStoreCallbackHandler指定密钥的密码:
<bean class="org.springframework.ws.soap.security.wss4j2.Wss4jSecurityInterceptor">
<property name="validationActions" value="Encrypt"/>
<property name="validationDecryptionCrypto">
<bean class="org.springframework.ws.soap.security.wss4j2.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.wss4j2.callback.KeyStoreCallbackHandler">
<property name="privateKeyPassword" value="mykeypass"/>
</bean>
</property>
</bean>
支持解密具有嵌入密钥名称 (ds:KeyName元素),您可以配置KeyStoreCallbackHandler指向具有对称密钥的密钥库。
这symmetricKeyPassword属性指示密钥的密码,密钥名称是ds:KeyName元素:
<bean class="org.springframework.ws.soap.security.wss4j2.Wss4jSecurityInterceptor">
<property name="validationActions" value="Encrypt"/>
<property name="validationCallbackHandler">
<bean class="org.springframework.ws.soap.security.wss4j2.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.wss4j2.Wss4jSecurityInterceptor">
<property name="securementActions" value="Encrypt"/>
<property name="securementEncryptionUser" value="mycert"/>
<property name="securementEncryptionCrypto">
<bean class="org.springframework.ws.soap.security.wss4j2.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为所需值。
在下一个示例中,传出消息使用别名的密钥进行加密secretKey而myKey出现在ds:KeyName元素:
<bean class="org.springframework.ws.soap.security.wss4j2.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.wss4j2.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从回声样本中:
<property name="securementEncryptionParts"
value="{Content}{http://www.springframework.org/spring-ws/samples/echo}echoResponse"/>
请注意,元素名称、命名空间标识符和加密修饰符区分大小写。
可以省略加密修饰符和命名空间标识符。
如果这样做,则加密模式默认为Content,并且命名空间设置为 SOAP 命名空间。
要指定没有命名空间的元素,请使用值Null(区分大小写),作为命名空间名称。如果未指定列表,则处理程序将加密Content默认模式下。
7.2.7. 安全异常处理
异常处理Wss4jSecurityInterceptor与XwsSecurityInterceptor. 有关详细信息,请参阅安全异常处理。