II. 参考资料
4. 共享组件
本章探讨了在客户端和服务器端 Spring-WS 开发之间共享的组件。这些接口和类代表了 Spring-WS 的构建块,因此即使你不直接使用它们,你也需要了解它们的作用。
4.1. Web 服务消息
本节描述了 Spring-WS 使用的消息和消息工厂。
4.1.1.WebServiceMessage
Spring Web 服务的核心接口之一是WebServiceMessage
.此接口表示与协议无关的 XML 消息。该接口包含一些方法,这些方法以javax.xml.transform.Source
或javax.xml.transform.Result
.Source
和Result
是标记接口,表示 XML 输入和输出的抽象。具体实现包装各种 XML 表示形式,如下表所示:
源或结果实现 | 包装的 XML 表示 |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
除了读取和写入有效负载之外,Web 服务消息还可以将自身写入输出流。
4.1.2.SoapMessage
SoapMessage
是WebServiceMessage
.它包含特定于 SOAP 的方法,例如获取 SOAP 标头、SOAP 错误等。通常,您的代码不应依赖于SoapMessage
,因为 SOAP 正文的内容(消息的有效负载)可以通过使用getPayloadSource()
和getPayloadResult()
在WebServiceMessage
.仅当需要执行特定于 SOAP 的作(例如添加标头、获取附件等)时,才需要强制转换WebServiceMessage
自SoapMessage
.
4.1.3. 消息工厂
具体消息实现由WebServiceMessageFactory
.此工厂可以创建空消息或从 input 流中读取消息。有两种具体的实现WebServiceMessageFactory
.一个基于 SAAJ,即用于 Java 的带附件的 SOAP API。另一个基于 Axis 2 的 AXIOM(AXis 对象模型)。
SaajSoapMessageFactory
这SaajSoapMessageFactory
使用带有附件的 SOAP API for Java (SAAJ) 创建SoapMessage
实现。SAAJ 是 J2EE 1.4 的一部分,因此大多数现代应用程序服务器都应该支持它。以下是常见应用程序服务器提供的 SAAJ 版本的概述:
应用服务器 | SAAJ 版本 |
---|---|
BEA WebLogic 8 |
1.1 |
BEA WebLogic 9 |
1.1/1.21 |
IBM WebSphere 6 |
1.2 |
SUN 琉璃鱼 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
这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.1.4.MessageContext
通常,消息成对出现:请求和响应。在客户端创建一个请求,该请求通过某种传输方式发送到服务器端,在那里生成响应。此响应将发送回客户端,并在客户端中读取。
在 Spring Web 服务中,这样的对话包含在MessageContext
,它具有用于获取请求和响应消息的属性。在客户端,消息上下文由WebServiceTemplate
.在服务器端,消息上下文从特定于传输的 input 流中读取。例如,在 HTTP 中,它是从HttpServletRequest
,响应将写回HttpServletResponse
.
4.2.TransportContext
SOAP 协议的一个关键属性是它试图与传输无关。这就是为什么,例如, Spring-WS 不支持通过 HTTP 请求 URL 而是通过消息内容将消息映射到端点的原因。
但是,有时需要在客户端或服务器端访问底层传输。为此,Spring Web 服务具有TransportContext
.传输上下文允许访问底层WebServiceConnection
,通常为HttpServletConnection
在服务器端或HttpUrlConnection
或CommonsHttpConnection
在客户端。例如,您可以在服务器端终端节点或拦截器中获取当前请求的 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 表达式往往对 Importing 文档中的意外但可能无关紧要的更改更加健壮。
Spring Web 服务有两种方法可以在应用程序中使用 XPath:更快的XPathExpression
或更灵活的XPathTemplate
.
4.3.1.XPathExpression
这XPathExpression
是对已编译的 XPath 表达式(如 Java 5javax.xml.xpath.XPathExpression
interface 或 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
object,我们稍后会使用它。
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 服务通过标准的 Commons Logging 接口提供此功能。
确保使用 Commons Logging 版本 1.1 或更高版本。早期版本存在类加载问题,并且不与 Log4J TRACE 级别集成。 |
要记录所有服务器端消息,请将org.springframework.ws.server.MessageTracing
logger 级别设置为DEBUG
或TRACE
.在DEBUG
级别,则仅记录 payload 根元素。在TRACE
级别,则会记录整个消息内容。如果您只想记录已发送的消息,请使用org.springframework.ws.server.MessageTracing.sent
记录。同样,您可以使用org.springframework.ws.server.MessageTracing.received
以仅记录收到的消息。
在客户端,存在类似的 Logger:org.springframework.ws.client.MessageTracing.sent
和org.springframework.ws.client.MessageTracing.received
.
以下示例log4j.properties
配置文件在客户端记录已发送消息的完整内容,并且仅记录客户端接收消息的 payload 根元素。在服务器端,将记录已发送和已接收消息的有效负载根:
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] 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
annotation 并具有一个或多个处理方法。这些方法通过检查消息的各个部分(通常是有效负载)来处理传入的 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-WS 的MessageDispatcher
非常灵活,允许你使用任何类型的类作为端点,只要它可以在 Spring IoC 容器中进行配置即可。在某种程度上,消息调度程序类似于 Spring 的DispatcherServlet
这
Spring Web MVC 中使用的 “Front Controller”。
以下序列图显示了MessageDispatcher
:

当MessageDispatcher
设置为使用,并且收到针对该特定调度程序的请求,MessageDispatcher
开始处理请求。以下过程描述了如何MessageDispatcher
处理请求:
-
配置的
EndpointMapping(s)
将搜索适当的端点。如果找到终端节点,则调用与终端节点关联的调用链(预处理器、后处理器和终端节点)以创建响应。 -
为终端节点找到合适的适配器。这
MessageDispatcher
委托此适配器来调用端点。 -
如果返回响应,则会在发送过程中发送响应。如果未返回响应(这可能是由于前处理器或后处理器拦截了请求,例如,出于安全原因),则不会发送任何响应。
在处理请求期间引发的异常将由应用程序上下文中声明的任何端点异常解析程序选取。使用这些异常解析程序可以定义自定义行为(例如返回 SOAP 错误),以防引发此类异常。
这MessageDispatcher
具有多个用于设置端点适配器、映射、异常解析程序的属性。但是,不需要设置这些属性,因为 Dispatcher 会自动检测在应用程序上下文中注册的所有类型。仅当需要覆盖检测时,才应设置这些属性。
消息调度程序在消息上下文上运行,而不是在特定于传输的输入流和输出流上运行。因此,特定于传输的请求需要读取到MessageContext
.对于 HTTP,这是通过WebServiceMessageReceiverHandlerAdapter
(这是一个 Spring WebHandlerInterceptor
),这样MessageDispatcher
可以按照标准DispatcherServlet
.但是,有一种更方便的方法可以做到这一点,如MessageDispatcherServlet
.
5.2. 传输
Spring Web 服务支持多种传输协议。最常见的是 HTTP 传输,为此提供了自定义 Servlet,但您也可以通过 JMS 甚至电子邮件发送消息。
5.2.1.MessageDispatcherServlet
这MessageDispatcherServlet
是标准Servlet
方便地从标准 Spring Web 扩展DispatcherServlet
并包装一个MessageDispatcher
.因此,它将这些属性合二为一。作为MessageDispatcher
,它遵循与上一节中描述的相同的请求处理流程。作为 Servlet,MessageDispatcherServlet
在web.xml
的 Web 应用程序。请求MessageDispatcherServlet
要处理必须由同一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
是标准 SpringDispatcherServlet
它
-servlet.xml在WEB-INF
目录中,并在 Spring 容器中创建在那里定义的 bean。在前面的示例中,它查找 '/WEB-INF/spring-ws-servlet.xml'。此文件包含所有 Spring Web Services bean,例如端点、编组器等。
作为web.xml
,如果您在 Servlet 3+ 环境中运行,则可以以编程方式配置 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 定义(通常是 services、repositories 等)可以在MyRootConfig
类。默认情况下,AbstractAnnotationConfigMessageDispatcherServletInitializer
将 Servlet 映射到两种模式:/services
和*.wsdl
,但您可以通过覆盖getServletMappings()
方法。有关MessageDispatcherServlet
,请参阅 JavadocAbstractMessageDispatcherServletInitializer
和AbstractAnnotationConfigMessageDispatcherServletInitializer
.
自动 WSDL 公开
这MessageDispatcherServlet
自动检测任何WsdlDefinition
bean 的 bean 定义。所有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
文件在 Classpath 上通过GET
请求到以下形式的 URL(根据需要替换 Host、Port 和 Servlet 上下文路径):
http://localhost:8080/spring-ws/orders.wsdl
都WsdlDefinition bean 定义由MessageDispatcherServlet 在其 Bean 名称下,后缀为 '.wsdl'。因此,如果 bean 名称为echo ,则 host name 为server ,而 Servlet 上下文(war 名称)为spring-ws 中,可以在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()
method 返回true
.
请查阅WsdlDefinitionHandlerAdapter
类以了解有关整个转换过程的更多信息。
作为手动编写 WSDL 并使用<static-wsdl>
,Spring Web 服务还可以从 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
package 和ProviderBasedWsdl4jDefinition
类来生成 WSDL。请参阅这些类的类级 Javadoc,了解如何在必要时扩展此机制。
这DefaultWsdl11Definition
(因此,<dynamic-wsdl>
标记中)使用约定从 XSD 架构构建 WSDL。它迭代所有element
元素,并创建一个message
for all elements 的接下来,它创建一个 WSDLoperation
对于以 defined request 或 response 后缀结尾的所有消息。默认请求后缀为Request
.默认响应后缀为Response
,但可以通过设置requestSuffix
和responseSuffix
attributes 开启<dynamic-wsdl />
分别。它还构建了一个portType
,binding
和service
基于作。
例如,如果Orders.xsd
schema 定义GetOrdersRequest
和GetOrdersResponse
元素<dynamic-wsdl>
创建一个GetOrdersRequest
和GetOrdersResponse
message 和GetOrders
作,该作被放入Orders
端口类型。
要使用多个模式,无论是通过包含还是导入,您都可以将 Commons XMLSchema 放在类路径上。如果 Commons XMLSchema 位于类路径上,则<dynamic-wsdl>
元素遵循所有 XSD 导入,并将它们作为单个 XSD 包含在 WSDL 中并内联。这大大简化了架构的部署,同时仍允许单独编辑它们。
尽管在运行时从 XSD 创建 WSDL 很方便,但这种方法有几个缺点。首先,尽管我们试图在版本之间保持 WSDL 生成过程的一致性,但它仍然有可能发生变化(略有变化)。其次,生成速度有点慢,但是,一旦生成,WSDL 就会被缓存以供以后参考。 |
因此,您应该使用<dynamic-wsdl>
仅在项目的开发阶段。我们建议使用浏览器下载生成的 WSDL,将其存储在项目中,并使用<static-wsdl>
.这是真正确保 WSDL 不会随时间变化的唯一方法。
5.2.2. 在DispatcherServlet
作为MessageDispatcherServlet
,您可以将MessageDispatcher
在标准的 Spring-Web MVC 中DispatcherServlet
.默认情况下,DispatcherServlet
can delegate only to (只能委托给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/myServiceDefintion.wsdl"/>
</bean>
...
</beans>
5.2.3. JMS 传输
Spring Web 服务通过 Spring 框架中提供的 JMS 功能支持服务器端 JMS 处理。Spring Web 服务提供了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 Web 服务还提供服务器端电子邮件处理。此功能通过MailMessageReceiver
类。此类监视 POP3 或 IMAP 文件夹,将电子邮件转换为WebServiceMessage
,并使用 SMTP 发送任何响应。您可以通过storeUri
,它指示要监控请求的邮件文件夹(通常是 POP3 或 IMAP 文件夹),以及transportUri
,它指示用于发送响应的服务器(通常是 SMTP 服务器)。
您可以配置MailMessageReceiver
使用可插拔策略监控传入消息:MonitoringStrategy
.默认情况下,使用轮询策略,其中每 5 分钟轮询一次传入文件夹以获取新邮件。您可以通过设置pollingInterval
策略上的属性。默认情况下,所有MonitoringStrategy
implementations 删除已处理的消息。您可以通过设置deleteMessages
财产。
作为效率相当低的轮询方法的替代方法,有一种使用 IMAP IDLE 的监视策略。IDLE 命令是 IMAP 电子邮件协议的可选扩展,它允许邮件服务器将新的邮件更新发送到MailMessageReceiver
异步。如果使用支持 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 <[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 Web 服务提供了一种基于 Sun 的 JRE 1.6 HTTP 服务器的传输。嵌入式 HTTP Server 是一个易于配置的独立服务器。它为传统的 servlet 容器提供了一种更轻量级的替代方案。
使用嵌入式 HTTP 服务器时,不需要外部部署描述符 (web.xml
).您只需定义服务器的实例并将其配置为处理传入请求。核心 Spring 框架中的 remoting 模块包含一个用于 HTTP 服务器的便捷工厂 Bean:SimpleHttpServerFactoryBean
.最重要的属性是contexts
,它将上下文路径映射到相应的HttpHandler
实例。
Spring Web 服务提供了HttpHandler
接口:WsdlDefinitionHttpHandler
和WebServiceMessageReceiverHttpHandler
.前者将传入的 GET 请求映射到WsdlDefinition
.后者负责处理 Web 服务消息的 POST 请求,因此需要一个WebServiceMessageFactory
(通常为SaajSoapMessageFactory
) 和WebServiceMessageReceiver
(通常SoapMessageDispatcher
) 完成其任务。
为了与 servlet 世界进行比较,contexts
property 在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.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 服务对 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;
@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 method 采用Element (注释为@RequestPayload ) 作为参数。这意味着消息的有效负载作为 DOM 元素在此方法上传递。该方法具有void return 类型,表示未发送响应消息。
有关终端节点方法的更多信息,请参阅@Endpoint 处理方法. |
4 | 这getOrder method 采用OrderRequest (也注释有@RequestPayload ) 作为参数。此参数是 JAXB2 支持的对象(它带有@XmlRootElement ).这意味着消息的有效负载将作为未编组的对象传递给此方法。这SoapHeader type 也作为参数给出。在调用时,此参数包含请求消息的 SOAP 标头。该方法还用@ResponsePayload ,表示返回值(Order ) 用作响应消息的有效负载。
有关终端节点方法的更多信息,请参阅@Endpoint 处理方法. |
5 | 此终端节点的两种处理方法都标有@PayloadRoot ,指示该方法可以处理哪种类型的请求消息:该getOrder 方法的请求中,使用orderRequest local name 和http://samples namespace URI 的 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
configuration 中,您可以实现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 一样,端点的 Scopes 默认为 singleton。也就是说,每个容器创建一个 bean 定义的实例。作为单例意味着多个线程可以同时使用它,因此端点必须是线程安全的。如果要使用不同的范围,例如 prototype,请参阅 Spring 参考文档。 |
Note that all abstract base classes provided in Spring-WS are thread safe, unless otherwise indicated in the class-level Javadoc.
5.3.1.@Endpoint
处理方法
要使终端节点实际处理传入的 XML 消息,它需要具有一个或多个处理方法。处理方法可以采用广泛的参数和返回类型。但是,它们通常有一个包含消息有效负载的参数,并且它们返回响应消息的有效负载(如果有)。本节介绍支持哪些参数和返回类型。
为了指示方法可以处理哪种类型的消息,该方法通常使用@PayloadRoot
或@SoapAction
注解。您可以在 终端节点映射 中了解有关这些注释的更多信息。
以下示例显示了一种处理方法:
@PayloadRoot(localPart = "order", namespace = "http://samples")
public void order(@RequestPayload Element orderElement) {
Order order = createOrder(orderElement);
orderService.createOrder(order);
}
这order
method 采用Element
(注释为@RequestPayload
) 作为参数。这意味着消息的有效负载作为 DOM 元素在此方法上传递。该方法具有void
return 类型,表示未发送响应消息。
处理方法参数
处理方法通常具有一个或多个参数,这些参数引用传入 XML 消息的各个部分。最常见的是,处理方法有一个映射到消息有效负载的参数,但它也可以映射到请求消息的其他部分,例如 SOAP 标头。本节介绍您可以在处理方法签名中使用的参数。
要将参数映射到请求消息的有效负载,您需要使用@RequestPayload
注解。这个 Comments 告诉 Spring-WS 该参数需要绑定到请求有效负载。
下表描述了支持的参数类型。它显示了支持的类型,是否应使用参数进行注释@RequestPayload
和任何其他注释。
名字 | 支持的参数类型 | @RequestPayload 必填? |
其他说明 |
---|---|---|---|
TrAX 系列 |
|
是的 |
默认启用。 |
W3C DOM |
|
是的 |
默认启用 |
DOM4J |
|
是的 |
当 dom4j 位于 Classpath 上时启用。 |
JDOM 系列 |
|
是的 |
当 JDOM 位于 Classpath 上时启用。 |
XOM 系列 |
|
是的 |
当 XOM 位于类路径上时启用。 |
斯塔克斯 |
|
是的 |
当 StAX 位于 Classpath 上时启用。 |
XPath |
Any boolean、double、 |
不 |
默认启用,请参阅名为 |
消息上下文 |
|
不 |
默认启用。 |
肥皂 |
|
不 |
默认启用。 |
JAXB2 系列 |
任何带有 |
是的 |
当 JAXB2 位于 Classpath 上时启用。 |
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)
正如你所看到的,在定义如何处理方法签名时,有很多可能性。您甚至可以扩展此机制以支持您自己的参数类型。请参阅 JavadocDefaultMethodEndpointAdapter
和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
prefix 的 API 中,我们必须将其绑定到 XPath 表达式中的http://samples
Namespace。这是通过@Namespace
注解。或者,我们可以将此注释放在类型级别上,以便对所有处理程序方法甚至包级别使用相同的命名空间映射(在package-info.java
) 将其用于多个终端节点。
通过使用@XPathParam
中,您可以绑定到 XPath 支持的所有数据类型:
-
boolean
或Boolean
-
double
或Double
-
String
-
Node
-
NodeList
除了此列表之外,您还可以使用可从String
由 Spring 转换服务。
处理方法返回类型
要发送响应消息,处理需要指定返回类型。如果不需要响应消息,该方法可以将void
return 类型。最常见的是,return 类型用于创建响应消息的有效负载。但是,您也可以映射到响应消息的其他部分。本节介绍您可以在处理方法签名中使用的返回类型。
要将返回值映射到响应消息的有效负载,您需要使用@ResponsePayload
注解。这个 Comments 告诉 Spring-WS 返回值需要绑定到响应有效负载。
下表描述了支持的返回类型。它显示了支持的类型,是否应使用参数进行注释@ResponsePayload
和任何其他注释。
名字 | 支持的返回类型 | @ResponsePayload 必填? |
其他说明 |
---|---|---|---|
无响应 |
|
不 |
默认启用。 |
TrAX 系列 |
|
是的 |
默认启用。 |
W3C DOM |
|
是的 |
默认启用 |
DOM4J |
|
是的 |
当 dom4j 位于 Classpath 上时启用。 |
JDOM 系列 |
|
是的 |
当 JDOM 位于 Classpath 上时启用。 |
XOM 系列 |
|
是的 |
当 XOM 位于类路径上时启用。 |
JAXB2 系列 |
任何带有 |
是的 |
当 JAXB2 位于 Classpath 上时启用。 |
OXM 公司 |
Spring OXM 支持的任何类型 |
是的 |
启用时 |
在定义处理方法签名时,有很多可能性。甚至可以扩展此机制以支持您自己的参数类型。请参阅 的类级 JavadocDefaultMethodEndpointAdapter
和MethodReturnValueHandler
以查看作方法。
5.4. 端点映射
终端节点映射负责将传入消息映射到相应的终端节点。默认情况下,某些终端节点映射处于启用状态,例如,PayloadRootAnnotationMethodEndpointMapping
或SoapActionAnnotationMethodEndpointMapping
.但是,我们首先需要研究EndpointMapping
.
一EndpointMapping
提供EndpointInvocationChain
,其中包含与传入请求匹配的终端节点,还可能包含应用于请求和响应的终端节点拦截器列表。当请求传入时,MessageDispatcher
将其交给 Endpoint Map,让它检查请求并提出适当的EndpointInvocationChain
.然后MessageDispatcher
调用 endpoint 和链中的任何拦截器。
可以选择性地包含拦截器(反过来可以纵请求和/或响应)的可配置端点映射的概念非常强大。许多支持功能可以内置到自定义中EndpointMapping
实现。例如,自定义终端节点映射不仅可以根据消息的内容选择终端节点,还可以根据特定的 SOAP 标头(或者实际上是多个 SOAP 标头)来选择终端节点。
大多数终端节点映射继承自AbstractEndpointMapping
,它提供了一个 'interceptors' 属性,这是要使用的拦截器列表。EndpointInterceptors
在拦截请求 — 该EndpointInterceptor
接口.此外,还有defaultEndpoint
,这是此终端节点映射未产生匹配终端节点时使用的默认终端节点。
如 端点 中所述,@Endpoint
style 允许您在一个终端节点类中处理多个请求。这是MethodEndpointMapping
.此映射确定要为传入请求消息调用的方法。
有两个终端节点映射可以将请求定向到方法:PayloadRootAnnotationMethodEndpointMapping
和SoapActionAnnotationMethodEndpointMapping
您可以使用<sws:annotation-driven/>
在您的应用程序上下文中。
这PayloadRootAnnotationMethodEndpointMapping
使用@PayloadRoot
注解,使用localPart
和namespace
元素,以使用特定的限定名称标记方法。每当消息带有 payload 根元素的此限定名称时,都会调用该方法。有关示例,请参阅上文。
或者,SoapActionAnnotationMethodEndpointMapping
使用@SoapAction
注解来标记具有特定 SOAP Action 的方法。每当有消息进入时 thisSOAPAction
header,则调用该方法。
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 服务中,WS-Addressing 被实现为端点映射。通过使用此映射,您可以将 WS-Addressing作与端点相关联,类似于SoapActionAnnotationMethodEndpointMapping
前面描述过。
用AnnotationActionEndpointMapping
这AnnotationActionEndpointMapping
类似于SoapActionAnnotationMethodEndpointMapping
但使用 WS-Addressing 标头而不是 SOAP Action 传输标头。
要使用AnnotationActionEndpointMapping
中,使用@Action
注解,类似于@PayloadRoot
和@SoapAction
中描述的注释@Endpoint
处理方法和 Endpoint mappings。以下示例显示了如何执行此作:
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
注解。如果设置,则该值将与To
header 属性。
最后,还有messageSenders
属性,这是向非匿名的越界地址发送响应消息所必需的。您可以设置MessageSender
实现,就像你在WebServiceTemplate
.请参阅 URI 和传输。
5.4.2. 拦截请求 —EndpointInterceptor
接口
端点映射机制具有端点拦截器的概念。当您希望将特定功能应用于某些请求时(例如,处理与安全相关的 SOAP 标头或记录请求和响应消息),这些 API 可能非常有用。
端点拦截器通常使用<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
以进一步限制 Interceptor 适用的消息。最后,我们定义了两个拦截器,当消息具有http://www.example.com/SoapAction
SOAP作。请注意,第二个拦截器实际上是对<interceptors>
元素。您可以在<interceptors>
元素。
当您使用@Configuration
类,您可以从WsConfigurerAdapter
要添加侦听器:
@Configuration
@EnableWs
public class MyWsConfiguration extends WsConfigurerAdapter {
@Override
public void addInterceptors(List<EndpointInterceptor> interceptors) {
interceptors.add(new MyPayloadRootInterceptor());
}
}
拦截器必须实现EndpointInterceptor
接口中org.springframework.ws.server
包。此接口定义了三种方法,一种可用于在处理实际端点之前处理请求消息,一种可用于处理普通响应消息,另一种可用于处理错误消息。后两个在处理 endpoint 后调用。这三种方法应该提供足够的灵活性来执行各种预处理和后处理。
这handleRequest(..)
method 返回一个布尔值。您可以使用此方法中断或继续处理调用链。当此方法返回true
,则端点处理链将继续。当它返回false
这MessageDispatcher
将此解释为拦截器本身已经处理了事情,并且不再继续处理其他拦截器和调用链中的实际端点。这handleResponse(..)
和handleFault(..)
方法也具有布尔返回值。当这些方法返回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
,该 URL 可以设置为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 服务提供了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
.请注意,由于端点拦截器是在端点映射级别注册的,因此您可以创建适用于“旧式”消息的端点映射,并将拦截器添加到该映射中。因此,转换仅适用于这些 “old style” 消息。
您可以使用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
是默认值,但可以通过显式添加另一个 Resolver 来覆盖它。
5.5.1.SoapFaultMappingExceptionResolver
这SoapFaultMappingExceptionResolver
是一种更复杂的实现。此解析程序允许您获取可能引发的任何异常的类名,并将其映射到 SOAP Fault:
<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
,其中只需要故障代码。如果未设置错误字符串,则默认为异常消息。如果未设置语言,则默认为 English。前面的配置映射了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>
如果发生任何其他异常,则返回 default 错误:服务器端错误,异常消息作为错误字符串。
5.5.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.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)
或MockWebServiceClient.createClient(WebServiceMessageReceiver, WebServiceMessageFactory)
. -
通过调用 Send request messages
sendRequest(RequestCreator)
,可能通过使用默认的RequestCreator
中提供的实现RequestCreators
(可以静态导入)。 -
通过调用
andExpect(ResponseMatcher)
,可能通过使用默认的ResponseMatcher
中提供的实现ResponseMatchers
(可以静态导入)。可以通过链接来设置多个期望andExpect(ResponseMatcher)
调用。
请注意,MockWebServiceClient (和相关类)提供了一个 “Fluent” API,因此您通常可以在 IDE 中使用代码完成功能来指导您完成设置模拟服务器的过程。 |
还要注意,你可以在单元测试中依赖 Spring Web Services 中提供的标准日志记录功能。有时,检查请求或响应消息以找出特定测试失败的原因可能很有用。有关更多信息,请参见消息日志记录和跟踪。 |
例如,请考虑以下 Web 服务终端节点类:
@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() method 采用CustomerCountRequest 作为其参数,并返回一个CustomerCountResponse .这两个类都是编组处理程序支持的对象。例如,他们可以有一个@XmlRootElement 注解。 |
以下示例显示了CustomerEndpoint
:
@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 .在这种情况下,应用程序上下文包含CustomerEndpoint (或者可能是<context:component-scan /> )。 |
4 | 在@Before 方法中,我们会创建一个MockWebServiceClient 通过使用createClient Factory 方法。 |
5 | 我们通过调用sendRequest() 替换为withPayload() RequestCreator 由静态导入的RequestCreators (参见用RequestCreator 和RequestCreators ).我们还通过调用 这部分测试可能看起来有点令人困惑,但 IDE 的代码完成功能有很大帮助。键入后 |
5.6.2. 使用RequestCreator
和RequestCreators
最初,MockWebServiceClient
需要创建请求消息供终端节点消费。客户端使用RequestCreator
strategy 接口:
public interface RequestCreator {
WebServiceMessage createRequest(WebServiceMessageFactory messageFactory)
throws IOException;
}
您可以编写此接口的自己的实现,使用消息工厂创建请求消息,但当然不必这样做。这RequestCreators
class 提供了一种创建RequestCreator
基于withPayload()
方法。您通常静态导入RequestCreators
.
5.6.3. 使用ResponseMatcher
和ResponseMatchers
当终端节点处理请求消息并收到响应时,MockWebServiceClient
可以验证此响应消息是否满足特定预期。客户端使用ResponseMatcher
strategy 接口:
public interface ResponseMatcher {
void match(WebServiceMessage request,
WebServiceMessage response)
throws IOException, AssertionError;
}
同样,您可以编写此接口的 implemented ,抛出AssertionError
实例,但您肯定不必这样做,因为ResponseMatchers
类提供标准ResponseMatcher
implementations 供您在测试中使用。您通常静态导入此类。
这ResponseMatchers
class 提供以下响应匹配器:
ResponseMatchers 方法 |
描述 |
---|---|
|
需要给定的响应有效负载。 |
|
期望响应有效负载针对给定的 XSD 架构进行验证。 |
|
期望给定的 XPath 表达式存在、不存在或计算为给定值。 |
|
期望响应消息中存在给定的 SOAP 标头。 |
|
期望响应消息不包含 SOAP 错误。 |
|
期望响应消息包含特定的 SOAP 错误。 |
您可以通过链接andExpect()
调用:
mockClient.sendRequest(...).
andExpect(payload(expectedResponsePayload)).
andExpect(validPayload(schemaResource));
有关提供的响应匹配程序的更多信息ResponseMatchers
,请参阅 Javadoc。
6. 在客户端上使用 Spring Web 服务
Spring-WS 提供了一个客户端 Web 服务 API,它允许对 Web 服务进行一致的、XML 驱动的访问。它还满足了封送处理程序和解组处理程序的使用,以便您的服务层代码可以专门处理 Java 对象。
这org.springframework.ws.client.core
package 提供了使用客户端访问 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
class 使用 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 服务提供了JmsMessageSender
.此类使用 Spring 框架的功能将WebServiceMessage
导入到 JMS 中Message
,将其发送到Queue
或Topic
,并接收响应(如果有)。
要使用JmsMessageSender
,您需要设置defaultUri
或uri
参数添加到 JMS URI 中,该 JMS URI 至少由jms:
前缀和目标名称。JMS URI 的一些示例包括:jms:SomeQueue
,jms:SomeTopic?priority=3&deliveryMode=NON_PERSISTENT
和jms:RequestQueue?replyToName=ResponseName
.有关此 URI 语法的更多信息,请参阅Javadoc 的JmsMessageSender
.
默认情况下,JmsMessageSender
发送 JMSBytesMessage
,但您可以覆盖此 API 以使用TextMessages
通过使用messageType
参数 - 例如jms:Queue?messageType=TEXT_MESSAGE
.请注意,BytesMessages
是首选类型,因为TextMessages
不支持 attachments 和 character encoding 可靠。
以下示例显示了如何将 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 服务还提供了一种电子邮件传输,你可以使用它通过 SMTP 发送 Web 服务消息,并通过 POP3 或 IMAP 检索它们。客户端电子邮件功能包含在MailMessageSender
类。此类从请求创建电子邮件WebServiceMessage
并通过 SMTP 发送。然后,它等待响应消息到达传入的 POP3 或 IMAP 服务器。
要使用MailMessageSender
中,将defaultUri
或uri
参数设置为mailto
URI — 例如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 Web Services 2.0 引入了 XMPP (Jabber) 传输,您可以使用它通过 XMPP 发送和接收 Web 服务消息。客户端 XMPP 功能包含在XmppMessageSender
类。此类从请求创建 XMPP 消息WebServiceMessage
并通过 XMPP 发送它。然后,它会侦听响应消息的到达。
要使用XmppMessageSender
中,将defaultUri
或uri
参数设置为xmpp
URI — 例如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。以下示例向 Web 服务发送一条简单的 XML 消息:
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()
方法),并将结果写入控制台。这WebServiceTemplate
注入了默认 URI,使用该 URI 是因为 Java 代码中未明确提供 URI。
请注意,WebServiceTemplate
class 一旦配置完毕,就是线程安全的(假设它的所有依赖项也是线程安全的,Spring-WS 附带的所有依赖项都是这种情况),因此多个对象可以使用相同的共享WebServiceTemplate
实例。这WebServiceTemplate
公开一个零参数构造函数,并且messageFactory
和messageSender
可用于构造实例的 bean 属性(通过使用 Spring 容器或纯 Java 代码)。或者,考虑从 Spring-WS 的WebServiceGatewaySupport
convenience 基类,它公开了方便的 bean 属性以实现轻松配置。(您不必扩展此基类。它仅作为便利类提供。
6.1.3. 发送和接收 POJO — 编组和解组
为了便于发送普通 Java 对象,WebServiceTemplate
具有多个send(..)
采用Object
作为消息数据内容的参数。方法marshalSendAndReceive(..)
在WebServiceTemplate
类将请求对象到 XML 的转换委托给Marshaller
以及将响应 XML 转换为对象到Unmarshaller
.(有关编组和解组器的更多信息,请参阅 Spring Framework 参考文档。通过使用封送处理程序,您的应用程序代码可以专注于正在发送或接收的业务对象,而不必关心如何将其表示为 XML 的详细信息。要使用编组功能,您必须使用marshaller
和unmarshaller
的属性WebServiceTemplate
类。
6.1.4. 使用WebServiceMessageCallback
为了适应对消息的 SOAP 标头和其他设置,WebServiceMessageCallback
interface 允许您在创建消息之后但在发送消息之前访问消息。下面的示例演示如何在通过封送对象创建的消息上设置 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 服务在客户端也支持这个规范。
要在客户端上设置 WS-Addressing 标头,可以使用org.springframework.ws.soap.addressing.client.ActionCallback
.该回调将需要的 action header 作为参数。它还具有用于指定 WS-Addressing 版本的构造函数和To
页眉。如果未指定,则To
header 默认为正在建立的连接的 URL。
以下示例将Action
header 设置为http://samples/RequestOrder
:
webServiceTemplate.marshalSendAndReceive(o, new ActionCallback("http://samples/RequestOrder"));
6.1.5. 使用WebServiceMessageExtractor
这WebServiceMessageExtractor
interface 是一个低级回调接口,您可以完全控制提取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
interface 或 complete client 类。这种方法的优点是很容易完成。缺点是,您没有真正测试通过网络发送的 XML 消息的确切内容,尤其是在模拟整个客户端类时。
-
编写集成测试,这些测试会测试消息的内容。
第一种方法可以通过模拟框架(如 EasyMock、JMock 等)轻松完成。下一节重点介绍如何使用 Spring Web Services 2.0 中引入的测试功能编写集成测试。
6.2.1. 编写客户端集成测试
Spring Web Services 2.0 引入了对创建 Web Service Client 端集成测试的支持。在此上下文中,客户端是使用WebServiceTemplate
以访问 Web 服务。
集成测试支持位于org.springframework.ws.test.client
包。该包中的核心类是MockWebServiceServer
.基本思想是 Web 服务模板连接到这个模拟服务器并向其发送请求消息,然后模拟服务器根据注册的期望验证该消息。如果满足预期,则模拟服务器会准备一条响应消息,该消息将发送回模板。
典型用法MockWebServiceServer
是:。
-
创建一个
MockWebServiceServer
实例MockWebServiceServer.createServer(WebServiceTemplate)
,MockWebServiceServer.createServer(WebServiceGatewaySupport)
或MockWebServiceServer.createServer(ApplicationContext)
. -
通过调用 Set request expectations
expect(RequestMatcher)
,可能通过使用默认的RequestMatcher
中提供的实现RequestMatchers
(可以静态导入)。可以通过链接来设置多个期望andExpect(RequestMatcher)
调用。 -
通过调用
andRespond(ResponseCreator)
,可能通过使用默认的ResponseCreator
中提供的实现ResponseCreators
(可以静态导入)。 -
使用
WebServiceTemplate
像往常一样,要么直接或通过客户端代码。 -
叫
MockWebServiceServer.verify()
以确保满足所有期望。
请注意,MockWebServiceServer (和相关类)提供了一个 “Fluent” API,因此您通常可以在 IDE 中使用代码完成功能来指导您完成设置模拟服务器的过程。 |
还要注意,你可以在单元测试中依赖 Spring Web Services 中提供的标准日志记录功能。有时,检查请求或响应消息以找出特定测试失败的原因可能很有用。有关更多信息,请参见消息日志记录和跟踪。 |
例如,请考虑以下 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 是封送处理程序支持的对象。例如,它可以有一个@XmlRootElement 注解。 |
3 | 这CustomerClient 使用WebServiceTemplate 提供方WebServiceGatewaySupport 将请求对象封送到 SOAP 消息中,并将其发送到 Web 服务。响应对象被解组为CustomerCountResponse . |
以下示例显示了CustomerClient
:
@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 Factory 方法。 |
5 | 我们通过调用expect() 替换为payload() RequestMatcher 由静态导入的RequestMatchers (参见用RequestMatcher 和RequestMatchers ).我们还通过调用 这部分测试可能看起来有点令人困惑,但 IDE 的代码完成功能有很大帮助。键入后 |
6 | 我们调用getCustomerCount() 在CustomerClient ,因此使用WebServiceTemplate .模板现在已经设置为“测试模式”,因此此方法调用不会建立真正的 (HTTP) 连接。我们还根据方法调用的结果进行一些 JUnit 断言。 |
7 | 我们调用verify() 在MockWebServiceServer ,验证是否实际收到了预期的消息。 |
6.2.2. 使用RequestMatcher
和RequestMatchers
为了验证请求消息是否满足特定预期,MockWebServiceServer
使用RequestMatcher
strategy 界面。该接口定义的合约如下:
public interface RequestMatcher {
void match(URI uri,
WebServiceMessage request)
throws IOException,
AssertionError;
}
你可以编写自己的这个接口的实现,抛出AssertionError
消息不符合您的期望时出现异常,但您当然不必这样做。这RequestMatchers
类提供标准RequestMatcher
implementations 供您在测试中使用。您通常静态导入此类。
这RequestMatchers
class 提供以下请求匹配器:
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
strategy 接口:
public interface ResponseCreator {
WebServiceMessage createResponse(URI uri,
WebServiceMessage request,
WebServiceMessageFactory messageFactory)
throws IOException;
}
同样,您可以编写此接口的自己的实现,使用消息工厂创建响应消息,但您当然不必这样做,因为ResponseCreators
类提供标准ResponseCreator
implementations 供您在测试中使用。您通常静态导入此类。
这ResponseCreators
class 提供以下响应:
ResponseCreators 方法 |
描述 |
---|---|
|
创建具有给定有效负载的响应消息。 |
|
在响应连接中创建错误。此方法为您提供了测试错误处理的机会。 |
|
从响应连接读取时引发异常。此方法为您提供了测试异常处理的机会。 |
|
创建包含给定 SOAP 错误的响应消息。此方法让您有机会测试 Fault 处理。 |
有关由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 1.5 JDK 和 SUN SAAJ 参考实现。WSS4J 拦截器没有这些要求(请参阅用Wss4jSecurityInterceptor ). |
这XwsSecurityInterceptor
需要安全策略文件才能运行。此 XML 文件告诉侦听器对传入 SOAP 消息要求哪些安全方面,以及向传出消息添加哪些方面。以下部分介绍了策略文件的基本格式,但您可以在此处找到更深入的教程。您可以使用policyConfiguration
属性,这需要 Spring 资源。策略文件可以包含多个元素,例如,要求在传入消息上使用用户名令牌并对所有传出消息进行签名。它包含一个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
file 的它使用稍后在文件中定义的两个回调处理程序。
7.1.1. 密钥库
对于大多数加密作,您可以使用标准的java.security.KeyStore
对象。这些作包括证书验证、消息签名、签名验证和加密。它们不包括用户名和时间戳验证。本节旨在为您提供一些有关密钥库的背景知识,以及可用于在密钥库文件中存储密钥和证书的 Java 工具。此信息主要与 Spring-WS 无关,而是与 Java 的一般加密功能有关。
这java.security.KeyStore
class 表示加密密钥和证书的存储工具。它可以包含三种不同类型的元素:
-
Private Keys(私钥):这些密钥用于自我验证。私钥附带相应公钥的证书链。在 WS-Security 领域中,这考虑了消息签名和消息解密。
-
对称密钥:对称(或秘密)密钥也用于消息加密和解密——区别在于双方(发件人和收件人)共享相同的密钥。
-
可信证书:这些 X509 证书称为“可信证书”,因为密钥库所有者相信证书中的公钥确实属于证书的所有者。在 WS-Security 中,这些证书用于证书验证、签名验证和加密。
用keytool
这keytool
程序是密钥和证书管理实用程序,随 Java 虚拟机一起提供。您可以使用此工具创建新的密钥库,向其添加新的私钥和证书,等等。提供keytool
命令,但您可以在此处找到参考,或者使用keytool -help
命令。
用KeyStoreFactoryBean
要使用 Spring 配置轻松加载密钥库,您可以使用KeyStoreFactoryBean
.它有一个 resource location 属性,您可以将其设置为指向要加载的密钥库的路径。可以提供密码来检查密钥库数据的完整性。如果未提供密码,则不会执行完整性检查。下面的清单配置了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
.此回调具有三个属性,其中 typekeystore
: (keyStore
,trustStore
和symmetricStore
).处理程序使用的确切存储取决于此处理程序要执行的加密作。对于私钥作,keyStore
被使用。对于对称密钥作,symmetricStore
被使用。为了确定信任关系,trustStore
被使用。下表表明了这一点:
加密作 | 使用的密钥库 |
---|---|
证书验证 |
第一 |
基于私钥的解密 |
|
基于对称密钥的解密 |
|
基于公钥证书的加密 |
|
基于对称密钥的加密 |
|
签署 |
|
签名验证 |
|
此外,KeyStoreCallbackHandler
具有privateKeyPassword
属性,应将其设置为解锁 'keyStore' 中包含的私钥。
如果symmetricStore
未设置,则默认为keyStore
.如果未设置密钥或信任库,回调处理程序将使用标准 Java 机制加载或创建它。请参阅 JavaDoc 的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 中,身份验证可以采用两种形式:使用用户名和密码令牌(使用纯文本密码或密码摘要)或使用 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>
如果 username 令牌不存在,则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
它创造的。如果身份验证成功,则令牌将存储在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
进行作。它会创建一个新的 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>
如果 username 令牌不存在,则XwsSecurityInterceptor
向发送方返回 SOAP 错误。如果存在,则会触发PasswordValidationCallback
替换为DigestPasswordRequest
添加到已注册的处理程序中。在 Spring-WS 中,有两个类处理这个特定的回调:SimplePasswordValidationCallbackHandler
和SpringDigestPasswordValidationCallbackHandler
.
用SimplePasswordValidationCallbackHandler
这SimplePasswordValidationCallbackHandler
可以处理纯文本密码和密码摘要。它被描述在用SimplePasswordValidationCallbackHandler
.
用SpringDigestPasswordValidationCallbackHandler
这SpringDigestPasswordValidationCallbackHandler
需要 Spring SecurityUserDetailService
进行作。它使用此服务检索令牌中指定的用户的密码。然后,将此 details 对象中包含的密码摘要与消息中的摘要进行比较。如果它们相等,则用户已成功进行身份验证,并且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
它创造的。配置的身份验证管理器应提供可以处理此令牌的提供程序(通常是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
进行作。它会创建一个新的 JAASLoginContext
通过使用此名称和X500Principal
的证书。这意味着此回调处理程序与任何 JAAS 集成LoginModule
处理 X500 个主体。
您可以将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
添加到已注册的处理程序中。在 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 中,KeyStoreCallbackHandler
类处理此特定回调。
用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 ault 返回给发送方。如果存在,则会触发DecryptionKeyCallback
添加到已注册的处理程序中。在 Spring-WS 中,KeyStoreCallbackHandler
类处理此特定回调。
用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 中,KeyStoreCallbackHandler
类处理此特定回调。
用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 月
-
用户名 Token profile V1.0
-
X.509 令牌配置文件 v1.0
此侦听器支持由AxiomSoapMessageFactory
和SaajSoapMessageFactory
.
7.2.1. 配置Wss4jSecurityInterceptor
WSS4J 不使用外部配置文件。侦听器完全由 properties 配置。此侦听器调用的验证和安全作是通过validationActions
和securementActions
属性。作以空格分隔的字符串形式传递。下面的清单显示了一个示例配置:
<bean class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
<property name="validationActions" value="UsernameToken Encrypt"/>
...
<property name="securementActions" value="Encrypt"/>
...
</bean>
下表显示了可用的验证作:
验证作 | 描述 |
---|---|
|
验证用户名令牌 |
|
验证时间戳 |
|
解密消息 |
|
验证签名 |
|
未执行任何作 |
下表显示了可用的安全保护作:
Securement作 | 描述 |
---|---|
|
添加用户名令牌 |
|
添加用户名令牌和签名用户名令牌密钥 |
|
添加时间戳 |
|
加密响应 |
|
对响应进行签名 |
|
未执行任何作 |
作的顺序很重要,由侦听器强制执行。如果其安全作的执行顺序与“validationActions”指定的顺序不同,则拦截器将拒绝传入的 SOAP 消息。
7.2.2. 处理数字证书
对于需要与密钥库交互或证书处理的加密作(签名、加密和解密作),WSS4J 需要“org.apache.ws.security.components.crypto.Crypto”的实例。
Crypto
实例可以从 WSS4J 的CryptoFactory
或者更方便地使用 Spring-WS'CryptoFactoryBean'。
加密工厂豆
Spring-WS 提供了一个方便的工厂 bean,CryptoFactoryBean
,该 API 的构造和配置Crypto
实例通过强类型属性 (preferred) 或通过Properties
对象。
默认情况下,CryptoFactoryBean
返回 的实例org.apache.ws.security.components.crypto.Merlin
.您可以通过设置cryptoProvider
property(或其等效项org.apache.ws.security.crypto.provider
string 属性)。
以下示例配置使用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 Security 验证纯文本和摘要密码UserDetailService
进行作。它使用此服务检索令牌中指定的用户的密码(或密码的摘要)。然后,将此 details 对象中包含的密码(或密码的摘要)与消息中的摘要进行比较。如果它们相等,则用户已成功进行身份验证,并且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>
添加用户名令牌
将用户名令牌添加到外发邮件中就像添加UsernameToken
到securementActions
属性的Wss4jSecurityInterceptor
并指定securementUsername
和 'securementPassword'。
密码类型可以通过设置securementPasswordType
财产。可能的值为PasswordText
对于纯文本密码或PasswordDigest
对于 Digest Passwords,这是默认值。
以下示例生成具有摘要密码的用户名令牌:
<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
财产。该值必须是一个列表,其中包含以空格分隔的所需元素名称(区分大小写)。
以下示例使用纯文本密码生成用户名令牌,即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
property 必须设置为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),方法是将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
action 添加到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
property 设置为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
property 表示密钥的密码,密钥名称是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
元素采用securementEncryptionUser
财产。要指示不同的名称,您可以设置securementEncryptionEmbeddedKeyName
替换为所需的值。在下一个示例中,传出邮件使用别名为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 加密规范,了解 Element 加密和内容加密之间的区别。以下示例标识echoResponse
来自 Echo 示例:
<property name="securementEncryptionParts"
value="{Content}{http://www.springframework.org/spring-ws/samples/echo}echoResponse"/>
请注意,元素名称、命名空间标识符和加密修饰符区分大小写。您可以省略 encryption 修饰符和命名空间标识符。如果这样做,则加密模式默认为Content
,并将命名空间设置为 SOAP 命名空间。
要指定没有命名空间的元素,请使用值Null
(区分大小写),作为命名空间名称。如果未指定 list,则处理程序将加密Content
模式。
7.2.7. 安全异常处理
对Wss4jSecurityInterceptor
与XwsSecurityInterceptor
.有关更多信息,请参阅安全异常处理。