经过上篇已经基本掌握了使用CXF开发基于SOAP的WS.在此基础上了解一下WSDL,SOAP等一些常见的术语.html
WSDL 的全称是 Web Services Description Language(Web 服务描述语言),用于描述 WS 的具体内容。java
当成功发布一个 WS 后,就能在浏览器中经过一个地址查看基于 WSDL 文档,它是一个基于 XML 的文档。一个典型的 WSDL 地址以下:node
http://localhost:8080/webservice/testService?wsdl
其中的?wsdl必须带上,才能返回一个基于xml的文档.web
一个典型的wdsl文档以下:spring
<wsdl:definitions xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:tns="http://webservices.chuyu.com/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:ns1="http://schemas.xmlsoap.org/soap/http" name="MyServiceImplService" targetNamespace="http://webservices.chuyu.com/"> <wsdl:types>...</wsdl:types> <wsdl:message name="saygodby">...</wsdl:message> <wsdl:message name="saygodbyResponse">...</wsdl:message> <wsdl:message name="sayHelloResponse">...</wsdl:message> <wsdl:message name="sayHello">...</wsdl:message> <wsdl:portType name="Myservice">...</wsdl:portType> <wsdl:binding name="MyServiceImplServiceSoapBinding" type="tns:Myservice">...</wsdl:binding> <wsdl:service name="MyServiceImplService">...</wsdl:service> </wsdl:definitions>
其中,definitions 是 WSDL 的根节点,它包含两个重要的属性:数据库
在 definitions 这个根节点下,有五种类型的子节点,它们分别是:apache
其中包括了两个重要信息:浏览器
<wsdl:service name="MyServiceImplService"> <wsdl:port binding="tns:MyServiceImplServiceSoapBinding" name="MyServiceImplPort"> <soap:address location="http://localhost:8080/webservice/testService"/> </wsdl:port> </wsdl:service>
portName:WS 的端口名称,默认为“WS 实现类 + Port”,例如:HelloServiceImplPort安全
endpointInterface:WS 的接口名称,默认为“WS 实现类所实现的接口+Service”,例如:MyServiceImplService.服务器
若是说wsdl只是一个描述文档的话,那SOAP就是具体的调用内容了.
其实 SOAP 就是一个信封(Envelope),在这个信封里包括两个部分,一是头(Header),二是体(Body)。用于传输的数据都放在 Body 中了,一些特殊的属性须要放在 Header 中(下面会看到)。
通常状况下,将须要传输的数据放入 Body 中,而 Header 是没有任何内容的,看起来整个 SOAP 消息是这样的:
查阅WS的相关资料及可能的应用场景,在实际的应用中可能会有如下的需求:
在 WS 领域有一个很强悍的解决方案,名为 WS-Security,它仅仅是一个规范,在 Java 业界里有一个很权威的实现,名为 WSS4J。
下面我将一步步让您学会,如何使用 Spring + CXF + WSS4J 实现一个安全可靠的 WS 调用框架。
将以上的需求抽象为如下步骤:
添加Jar包依赖
<dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-ws-security</artifactId> <version>${cxf.version}</version> </dependency>
服务端的cxf-servlet.xml 配置文件
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxws="http://cxf.apache.org/jaxws" xmlns:cxf="http://cxf.apache.org/core" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd"> <!--1. cxf-servlet.xml中import导入的文件不用本身建立,这是在依赖包中的。 --> <import resource="classpath:META-INF/cxf/cxf.xml"/> <import resource="classpath:META-INF/cxf/cxf-servlet.xml"/> <bean id="myService" class="com.chuyu.webservices.MyServiceImpl" /> <!-- 2. webservice发布配置中implementor能够直接写入实现类,如: <jaxws:endpoint id="testService" implementor="test.service.impl.MyServiceImpl" address="/testService"/> --> <bean id="serverPasswordCallback" class="com.chuyu.util.ServerPasswordCallback"/> <jaxws:endpoint id="testService" implementor="#myService" address="/testService"> <!--3.address参数是重点,这是webservice发布后其wsdl的相对路径,其绝对路径为应用访问路径/cxf拦截路径/address?wsdl--> <jaxws:inInterceptors> <bean class="org.apache.cxf.interceptor.LoggingInInterceptor"/> <bean class="org.apache.cxf.binding.soap.saaj.SAAJInInterceptor"/> <bean class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor"> <constructor-arg> <map> <!-- action:UsernameToken使用基于“用户名令牌”的方式进行身份认证--> <entry key="action" value="UsernameToken"/> <!--密码加密策略.PasswordText:明文密码 ;PasswordDigest 密文密码--> <entry key="passwordType" value="PasswordDigest"/> <!--服务端别名 ,可不指定--> <entry key="user" value="cxfServer"/> <!--提供一个用于密码验证的回调处理器--> <entry key="passwordCallbackRef"> <ref bean="serverPasswordCallback"/> </entry> </map> </constructor-arg> </bean> </jaxws:inInterceptors> </jaxws:endpoint> <cxf:bus> <cxf:features> <cxf:logging/> </cxf:features> </cxf:bus> </beans>
首先定义了一个基于 WSS4J 的拦截器(WSS4JInInterceptor),而后经过 <jaxws:inInterceptors> 将其配置到 testService上,最后使用了 CXF 提供的 Bus 特性,只须要在 Bus 上配置一个 logging feature,就能够监控每次 WS 请求与响应的日志了。
注意:这个 WSS4JInInterceptor 是一个 InInterceptor,表示对输入的消息进行拦截,一样还有 OutInterceptor,表示对输出的消息进行拦截。因为以上是服务器端的配置,所以咱们只须要配置 InInterceptor 便可,对于客户端而言,咱们能够配置 OutInterceptor(下面会看到)。
回调函数的实现类
package com.chuyu.util; import org.apache.ws.security.WSPasswordCallback; import org.springframework.stereotype.Component; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.UnsupportedCallbackException; import java.io.IOException; import java.util.HashMap; import java.util.Map; @Component public class ServerPasswordCallback implements CallbackHandler { /** * 假定userMap为存放的客户端和服务端的名称和密码, * 在实际应用场景中可使用数据库等存储机制来验证用户名和密码组合. */ private static final Map<String, String> userMap = new HashMap<String, String>(); static { userMap.put("client", "clientpass"); userMap.put("server", "serverpass"); } @Override public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { //将 JDK 提供的 javax.security.auth.callback.Callback 转型为 WSS4J 提供的 //org.apache.wss4j.common.ext.WSPasswordCallback WSPasswordCallback callback=(WSPasswordCallback) callbacks[0]; //客户端标识(用户名) String clientUsername= callback.getIdentifier(); //密码 String clientPassword = userMap.get(clientUsername); if (serverPassword != null) { callback.setPassword(serverPassword); } else { throw new SecurityException("验证失败"); } } }
客户端的cxf-servlet配置
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxws="http://cxf.apache.org/jaxws" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd"> <import resource="classpath:META-INF/cxf/cxf.xml"/> <import resource="classpath:META-INF/cxf/cxf-servlet.xml"/> <!-- 客户端xml配置 --> <bean id="webTest" class="com.chuyu.client.Myservice" factory-bean="client" factory-method="create"/> <bean id="client" class="org.apache.cxf.jaxws.JaxWsProxyFactoryBean"> <property name="address" value="http://192.168.9.47:8080/ws-demo-server/webservice/testService?wsdl"> </property> <property name="serviceClass" value="com.chuyu.client.Myservice"></property> <property name="outInterceptors"> <list> <bean class="org.apache.cxf.interceptor.LoggingOutInterceptor" /> <bean class="org.apache.cxf.binding.soap.saaj.SAAJOutInterceptor" /> <bean class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor"> <constructor-arg> <map> <entry key="action" value="UsernameToken" /> <entry key="passwordType" value="PasswordDigest" /> <entry key="user" value="client" /> <entry key="passwordCallbackRef"> <ref bean="clientPasswordCallback" /> </entry> </map> </constructor-arg> </bean> </list> </property> </bean> <bean id="clientPasswordCallback" class="com.chuyu.webservice.ClientPasswordCallback"></bean> </beans>
与服务端的配置相似,注释能够在服务端中找.
客户端的密码设置回调函数.
package com.chuyu.webservice; import java.io.IOException; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.UnsupportedCallbackException; import org.apache.wss4j.common.ext.WSPasswordCallback; import org.springframework.stereotype.Component; @Component public class ClientPasswordCallback implements CallbackHandler{ @Override public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { WSPasswordCallback callback = (WSPasswordCallback) callbacks[0]; System.out.println("identifier: " + callback.getIdentifier()); //客户端标识 及xml中配置的User用户名 callback.setPassword("clientpass"); } }
测试:
使用spring提供的test以及Junit 单元测试来测试
package com.chuyu.webservice; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import com.chuyu.client.Myservice; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations={"classpath:cxf-servlet.xml","classpath:spring.xml"}) public class Clientclass{ @Autowired private Myservice webTest; @Test public void testSayhello(){ System.out.println(webTest.sayHello("张三")); } }
若是指定的表标识符不存在即xml配置的user,在服务端的回调函数中userMap中不存在该键则抛出异常:
当密码和用户名都正确时,能够在控制台看到发送的SOAP信息
<?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <SOAP-ENV:Header xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"> <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" soap:mustUnderstand="1"> <wsse:UsernameToken wsu:Id="UsernameToken-c9246452-7027-4bc4-9775-b8e34fd4439c"> <wsse:Username>cxfClient</wsse:Username> <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">clientpass</wsse:Password> </wsse:UsernameToken> </wsse:Security> </SOAP-ENV:Header> <soap:Body> <ns2:sayHello xmlns:ns2="http://webservices.chuyu.com/"> <name>张三</name> </ns2:sayHello> </soap:Body> </soap:Envelope>
可见,在 SOAP Header 中提供了 UsernameToken 的相关信息,但 Username 与 Password 都是明文的,SOAP Body 也是明文的,这显然不是最好的解决方案。
若是您将 passwordType 由 PasswordText 改成 PasswordDigest(服务端与客户端都须要作一样的修改),那么就会看到一个加密过的密码,在此不作演示.
对于上面的这种方式,根据SOAP的信息能够看出,加密的过程其实是在SOAP的Header头部加上了验证信息,咱们也能够采用另外的一种方式直接在header头部加上验证信息
服务端的cxf-servlet.xml配置
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxws="http://cxf.apache.org/jaxws" xmlns:cxf="http://cxf.apache.org/core" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd"> <!--1. cxf-servlet.xml中import导入的文件不用本身建立,这是在依赖包中的。 --> <import resource="classpath:META-INF/cxf/cxf.xml"/> <import resource="classpath:META-INF/cxf/cxf-servlet.xml"/> <bean id="myService" class="com.chuyu.webservices.MyServiceImpl" /> <!-- 2. webservice发布配置中implementor能够直接写入实现类,如: <jaxws:endpoint id="testService" implementor="test.service.impl.MyServiceImpl" address="/testService"/> --> <!--<bean id="serverPasswordCallback" class="com.chuyu.util.ServerPasswordCallback"/>--> <jaxws:endpoint id="testService" implementor="#myService" address="/testService"> <!--3.address参数是重点,这是webservice发布后其wsdl的相对路径,其绝对路径为应用访问路径/cxf拦截路径/address?wsdl--> <jaxws:inInterceptors> <bean class="org.apache.cxf.interceptor.LoggingInInterceptor"/> <bean class="com.chuyu.util.ReadSoapHeader"></bean> <!--<bean class="org.apache.cxf.binding.soap.saaj.SAAJInInterceptor"/>--> <!--<bean class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor">--> <!--<constructor-arg>--> <!--<map>--> <!--<!– action:UsernameToken使用基于“用户名令牌”的方式进行身份认证–>--> <!--<entry key="action" value="UsernameToken"/>--> <!--<!–密码加密策略.PasswordText:明文密码 ;PasswordDigest 密文密码–>--> <!--<entry key="passwordType" value="PasswordDigest"/>--> <!--<!–服务端别名 ,可不指定–>--> <!--<entry key="user" value="cxfServer"/>--> <!--<!–提供一个用于密码验证的回调处理器–>--> <!--<entry key="passwordCallbackRef">--> <!--<ref bean="serverPasswordCallback"/>--> <!--</entry>--> <!--</map>--> <!--</constructor-arg>--> <!--</bean>--> </jaxws:inInterceptors> </jaxws:endpoint> <cxf:bus> <cxf:features> <cxf:logging/> </cxf:features> </cxf:bus> </beans>
ReadSoapHeader类
package com.chuyu.util; import org.apache.cxf.binding.soap.SoapMessage; import org.apache.cxf.binding.soap.saaj.SAAJInInterceptor; import org.apache.cxf.interceptor.Fault; import org.apache.cxf.phase.AbstractPhaseInterceptor; import org.apache.cxf.phase.Phase; import org.w3c.dom.NodeList; import javax.xml.soap.SOAPException; import javax.xml.soap.SOAPHeader; import javax.xml.soap.SOAPMessage; /** * Created by caowenhui on 2017/6/6. */ public class ReadSoapHeader extends AbstractPhaseInterceptor<SoapMessage> { //消息输入拦截 private SAAJInInterceptor saa=new SAAJInInterceptor(); public ReadSoapHeader(){ //指定拦截阶段 super(Phase.PRE_PROTOCOL); getAfter().add(SAAJInInterceptor.class.getName()); } public void handleMessage(SoapMessage message) throws Fault { // 获取Soap信息的xml表示 SOAPMessage mess=message.getContent(SOAPMessage.class); if(mess==null){ saa.handleMessage(message); mess=message.getContent(SOAPMessage.class); } //获取SOAP xml的Hander头部信息 SOAPHeader head=null; try { head = mess.getSOAPHeader(); } catch (SOAPException e) { e.printStackTrace(); } if(head==null){ return; } //用户名和密码节点 NodeList nodes=head.getElementsByTagName("tns:spId"); NodeList nodepass=head.getElementsByTagName("tns:spPassword"); System.out.println(nodes.item(0).getTextContent()); System.out.println(nodepass.item(0).getTextContent()); if(nodes.item(0).getTextContent().indexOf("client")!=-1){ if(nodepass.item(0).getTextContent().equals("clientpass")){ System.out.println("认证成功"); } } else{ SOAPException soapExc=new SOAPException("认证错误"); throw new Fault(soapExc); } } }
客户端cxf-servlet配置与服务端大同小异
cxf-servlet.xml配置
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxws="http://cxf.apache.org/jaxws" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd"> <import resource="classpath:META-INF/cxf/cxf.xml"/> <import resource="classpath:META-INF/cxf/cxf-servlet.xml"/> <!-- 客户端xml配置 --> <bean id="webTest" class="com.chuyu.client.Myservice" factory-bean="client" factory-method="create"/> <bean id="client" class="org.apache.cxf.jaxws.JaxWsProxyFactoryBean"> <property name="address" value="http://192.168.9.47:8080/ws-demo-server/webservice/testService?wsdl"></property> <property name="serviceClass" value="com.chuyu.client.Myservice"></property> <property name="outInterceptors"> <list> <bean class="org.apache.cxf.interceptor.LoggingOutInterceptor" /> <bean class="com.chuyu.webservice.AddSoapHeader"></bean> <!-- <bean class="org.apache.cxf.binding.soap.saaj.SAAJOutInterceptor" /> --> <!-- <bean class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor"> <constructor-arg> <map> <entry key="action" value="UsernameToken" /> <entry key="passwordType" value="PasswordDigest" /> <entry key="user" value="client" /> <entry key="passwordCallbackRef"> <ref bean="clientPasswordCallback" /> </entry> </map> </constructor-arg> </bean> --> </list> </property> </bean> <!-- <bean id="clientPasswordCallback" class="com.chuyu.webservice.ClientPasswordCallback"></bean> --> </beans>
AddSoapHeader.java
package com.chuyu.webservice; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; import javax.xml.namespace.QName; import org.apache.cxf.binding.soap.SoapHeader; import org.apache.cxf.binding.soap.SoapMessage; import org.apache.cxf.binding.soap.interceptor.AbstractSoapInterceptor; import org.apache.cxf.headers.Header; import org.apache.cxf.helpers.DOMUtils; import org.apache.cxf.interceptor.Fault; import org.apache.cxf.phase.Phase; import org.w3c.dom.Document; import org.w3c.dom.Element; public class AddSoapHeader extends AbstractSoapInterceptor{ private static String nameURI="http://www.WsAuthentication.com//authentication"; public AddSoapHeader(){ // 指定该拦截器在哪一个阶段被激发 super(Phase.WRITE); } @Override public void handleMessage(SoapMessage message) throws Fault { SimpleDateFormat sd=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Date date=new Date(); String time =sd.format(date); String spPassword="client"; String spName="clientpass"; QName qname=new QName("RequestSOAPHeader"); Document doc=DOMUtils.createDocument(); Element spId=doc.createElement("tns:spId"); spId.setTextContent(spName); Element spPass=doc.createElement("tns:spPassword"); spPass.setTextContent(spPassword); Element root=doc.createElementNS(nameURI, "tns:RequestSOAPHeader"); root.appendChild(spId); root.appendChild(spPass); SoapHeader head=new SoapHeader(qname,root); List<Header> headers=message.getHeaders(); headers.add(head); } }
测试一样采用上面的单元测试.发送的SAOP信息
Header节点下即为添加的SAOP验证信息
数字签名从字面上理解就是一种基于数字的签名方式。也就是说,当客户端发送 SOAP 消息时,须要对其进行“签名”,来证明本身的身份,当服务端接收 SOAP 消息时,须要对其签名进行验证(简称“验签”)。
在客户端与服务端上都有各自的“密钥库”,这个密钥库里存放了“密钥对”,而密钥对其实是由“公钥”与“私钥”组成的。当客户端发送 SOAP 消息时,须要使用本身的私钥进行签名,当客户端接收 SOAP 消息时,须要使用客户端提供的公钥进行验签。
参考Apache CXF官网帮助文档上的介绍:(http://cxf.apache.org/docs/ws-security.html)
由于有请求就有相应,因此客户端与服务端的消息调用其实是双向的,也就是说,客户端与服务端的密钥
库里所存放的信息是这样的:
总结成一句话:使用本身的私钥进行签名,使用对方的公钥进行验签。
新建keystore.bat,使用 JDK 提供的 keytool 命令行工具建立数字证书
@echo off keytool -genkeypair -alias server -keyalg RSA -dname "cn=server" -keypass serverpass -keystore server_store.jks -storepass storepass keytool -exportcert -alias server -file server_key.rsa -keystore server_store.jks -storepass storepass keytool -importcert -alias server -file server_key.rsa -keystore client_store.jks -storepass storepass -noprompt del server_key.rsa keytool -genkeypair -alias client -dname "cn=client" -keyalg RSA -keypass clientpass -keystore client_store.jks -storepass storepass keytool -exportcert -alias client -file client_key.rsa -keystore client_store.jks -storepass storepass keytool -importcert -alias client -file client_key.rsa -keystore server_store.jks -storepass storepass -noprompt del client_key.rsa
运行该批处理程序,将生成两个文件:server_store.jks 与 client_store.jks,随后将 server_store.jks 放入服务端的 classpath 下,将 client_store.jks 放入客户端的 classpath 下.
服务端cxf配置
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxws="http://cxf.apache.org/jaxws" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd"> <import resource="classpath:META-INF/cxf/cxf.xml"/> <import resource="classpath:META-INF/cxf/cxf-servlet.xml"/> <!-- 客户端xml配置 --> <bean id="webTest" class="com.chuyu.client.Myservice" factory-bean="client" factory-method="create"/> <bean id="client" class="org.apache.cxf.jaxws.JaxWsProxyFactoryBean"> <property name="address" value="http://192.168.9.47:8080/ws-demo-server/webservice/testService?wsdl"></property> <property name="serviceClass" value="com.chuyu.client.Myservice"></property> <property name="outInterceptors"> <list> <bean class="org.apache.cxf.interceptor.LoggingOutInterceptor" /> <!-- <bean class="com.chuyu.webservice.AddSoapHeader"></bean> --> <bean class="org.apache.cxf.binding.soap.saaj.SAAJOutInterceptor"/> <bean class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor"> <constructor-arg> <map> <!-- 签名(使用本身的私钥) --> <entry key="action" value="Signature" /> <entry key="signaturePropFile" value="client_sign.properties"/> <entry key="signatureUser" value="client"/> <entry key="passwordCallbackRef"> <ref bean="clientPasswordCallback" /> </entry> </map> </constructor-arg> </bean> </list> </property> </bean> <!-- <bean id="clientPasswordCallback" class="com.chuyu.webservice.ClientPasswordCallback"></bean> --> </beans>
其中 action 为 Signature,client.properties 内容以下
org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin org.apache.ws.security.crypto.merlin.file=server_store.jks org.apache.ws.security.crypto.merlin.keystore.type=jks org.apache.ws.security.crypto.merlin.keystore.password=storepass
客户端配置:
//.......相同省略 <bean id="wss4jOutInterceptor" class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor"> <constructor-arg> <map> <!-- 签名(使用本身的私钥) --> <entry key="action" value="Signature"/> <entry key="signaturePropFile" value="client.properties"/> <entry key="signatureUser" value="client"/> <entry key="passwordCallbackRef" value-ref="clientPasswordCallback"/> </map> </constructor-arg> </bean> //.....省略
其中 action 为 Signature,client.properties 内容以下
org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin org.apache.ws.security.crypto.merlin.file=client_store.jks org.apache.ws.security.crypto.merlin.keystore.type=jks org.apache.ws.security.crypto.merlin.keystore.password=storepass
经过单元测试结果以下:
WSS4J 除了提供签名与验签(Signature)这个特性之外,还提供了加密与解密(Encrypt)功能.
服务端:
客户端:
其中的回调函数与第一章节中相同.能够看到,发送的消息也被加密了.
参考文献:https://my.oschina.net/huangyong/blog/287791