CXF系列之JAX-WS:基于SOAP的安全控制

来源:互联网 发布:爱快路由 windows版 编辑:程序博客网 时间:2024/04/30 07:14

在 WS 领域有一个很强悍的解决方案,名为 WS-Security,它仅仅是一个规范,在 Java 业界里有一个很权威的实现,名为 WSS4J

下面我将一步步让您学会,如何使用 Spring + CXF + WSS4J 实现一个安全可靠的 WS 调用框架。

本文是基于CXF与Spring集成,基础之上的,CXF与Spring集成请看:CXF系列之JAX-WS:与Spring3集成并在tomcat部署

其实您需要做也就是两件事情:

  1. 认证WS请求
  2. 加密SOAP消息
怎样对 WS 进行身份认证呢?可使用如下解决方案:

1、基于用户令牌的身份认证

第一步、添加CXF提供的WS-Security的依赖

也就是相关jar包,CXF安装包里都有自带的,比如CXF3.0安装包里:
cxf-rt-ws-security-3.0.10.jar,将其添加到构建路径中,其底层实现还是WSS4J,CXF仅仅是对其做了一个封装而已。

第二步、完成服务端CXF相关配置

spring.xml配置:
<?xml version="1.0" encoding="UTF-8"?>  <beans xmlns="http://www.springframework.org/schema/beans"      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"      xsi:schemaLocation="http://www.springframework.org/schema/beans  http://www.springframework.org/schema/beans/spring-beans-3.0.xsd         http://www.springframework.org/schema/context   http://www.springframework.org/schema/context/spring-context-3.0.xsd">      <bean id="helloServiceImpl" class="com.test.service.impl.HelloServiceImpl"></bean>      <import resource="spring-cxf.xml" />    </beans> 
spring-cxf.xml配置:
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"       xmlns:cxf="http://cxf.apache.org/core"       xmlns:jaxws="http://cxf.apache.org/jaxws"       xsi:schemaLocation="http://www.springframework.org/schema/beans       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd       http://cxf.apache.org/core       http://cxf.apache.org/schemas/core.xsd       http://cxf.apache.org/jaxws       http://cxf.apache.org/schemas/jaxws.xsd">            <bean id="serverPasswordCallback" class="com.test.handler.ServerPasswordCallback"></bean>    <bean id="wss4jInInterceptor" class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor">        <constructor-arg>            <map>                <!-- 用户认证(明文密码) -->                <entry key="action" value="UsernameToken"/>                <entry key="passwordType" value="PasswordText"/>                <entry key="passwordCallbackRef" value-ref="serverPasswordCallback"/>            </map>        </constructor-arg>    </bean>    <jaxws:endpoint id="helloService" implementor="#helloServiceImpl" address="/soap/hello">        <jaxws:inInterceptors>            <ref bean="wss4jInInterceptor"/>        </jaxws:inInterceptors>    </jaxws:endpoint>    <cxf:bus>        <cxf:features>            <cxf:logging/>        </cxf:features>    </cxf:bus></beans>
首先定义了一个基于WSS4J的拦截器(WSS4JInInterceptor),然后通过<jaxws:inInterceptors>将其配置到helloService上,最后使用了CXF提供的Bus特性,只需要在Bus上配置一个logging feature,就可以监控每次WS请求与响应的日志了。
注意这个WSS4JInInterceptor是一个InInterceptor,表示对输入的消息进行拦截,同样也有OutInterceptor,表示对输出消息进行拦截。由于以上是对服务端的配置,因此我们只需要配置InInterceptor即可,对于OutInterceptor,我们可以在客户端进行配置。
有必要对以上配置WSS4JInInterceptor构造函数的参数进行说明:
  1. action=UsernameToken:表示基于“用户名令牌”的方式,进行身份认证。
  2. passwordType=passwordText:表示密码以明文的方式出现。
  3. passwordCallbackRef=serverPasswordCallback,需要提供一个用于密码验证的回调处理器(CallbackHandler)。
一下便是ServerPasswordCallback的具体实现:
package com.test.handler;import java.io.IOException;import java.util.HashMap;import java.util.Map;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;public class ServerPasswordCallback implements CallbackHandler { private static final Map<String, String> userMap = new HashMap<String, String>(); static{ userMap.put("client", "clientpass");     userMap.put("server", "serverpass"); }@Overridepublic void handle(Callback[] callbacks) throws IOException,UnsupportedCallbackException { WSPasswordCallback callback = (WSPasswordCallback) callbacks[0];  String clientUsername = callback.getIdentifier(); String serverPassword = userMap.get(clientUsername);  if (serverPassword != null) {            callback.setPassword(serverPassword);        }}}
可见,它实现了javax.security.auth.callback.CallbackHandler接口,这是JDK提供的用于安全认证的回调处理器接口,在代码中,提供了两个用户:server和client,用户名和密码都放在userMap中,这里需要将JDK提供的javax.security.auth.callback.Callback转型为WSS4J提供的org.apache.wss4j.common.ext.WSPasswordCallback,在handle方法中实现对客户端密码的验证,最终需要将密码放入到callback对象中。

第三步、完成客户端CXF相关配置

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"       xmlns:context="http://www.springframework.org/schema/context"       xmlns:jaxws="http://cxf.apache.org/jaxws"       xsi:schemaLocation="http://www.springframework.org/schema/beans       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd       http://www.springframework.org/schema/context       http://www.springframework.org/schema/context/spring-context-3.0.xsd       http://cxf.apache.org/jaxws       http://cxf.apache.org/schemas/jaxws.xsd"><bean id="clientPasswordCallback" class="com.test.handler.ClientPasswordCallback"></bean>    <bean id="wss4jOutInterceptor" class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor">        <constructor-arg>            <map>                <!-- 用户认证(明文密码) -->                <entry key="action" value="UsernameToken"/>                <entry key="user" value="client"/>                <entry key="passwordType" value="PasswordText"/>                <entry key="passwordCallbackRef" value-ref="clientPasswordCallback"/>            </map>        </constructor-arg>    </bean>    <jaxws:client id="helloService"                  serviceClass="com.test.service.inter.HelloService"                  address="http://localhost:8080/springCxfWSS4J/services/soap/hello">        <jaxws:outInterceptors>            <ref bean="wss4jOutInterceptor"/>        </jaxws:outInterceptors>    </jaxws:client></beans>
注意:这是使用的是WSS4JOutInterceptor拦截器,它是一个OutInterceptor,使客户端对输出的消息进行拦截。
WSS4JOutInterceptor的配置WSS4JInInterceptor的配置大同小异,这里需要提供客户端的用户名(user=client),还需要提供一个客户端密码回调处理器(passwordCallbackRef = clientPasswordCallback),代码如下:
package com.test.handler;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;public class ClientPasswordCallback implements CallbackHandler {@Overridepublic void handle(Callback[] callbacks) throws IOException,UnsupportedCallbackException {WSPasswordCallback callback = (WSPasswordCallback) callbacks[0];        callback.setPassword("clientpass");}}
在ClientPasswordCallback中,无非是这只客户端用户的密码,其他的什么也不用做了,客户端密码只能通过回调处理器的方式进行提供,而不能再spring配置文件中进行配置。

第四步:调用WS并观察控制台日志

部署应用并启动tomcat,再次调用WS,此时在控制台里的Inbound Message中会看到如下Payload:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Header><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-76497858-8992-4ed3-bcb9-009103b097eb"><wsse:Username>client</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:Header><soap:Body><ns2:sayHello xmlns:ns2="http://inter.service.test.com/"><arg0>cxf</arg0></ns2:sayHello></soap:Body></soap:Envelope>

可见,在SOAP Header中提供了UsernameToken的相关信息,但Username与Password都是明文的,SOAP Body也是明文的,这显然不是最好的解决方案,
如果你将passwordType由PasswordText改为PasswordDigest(服务端与客户端都需要做同样的修改),那么就会看到一个加密过的密码:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Header><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-7704c9f2-510e-4029-b3cb-40cd3bf9f5d6"><wsse:Username>client</wsse:Username><wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">HvElE//KB8gGhIv5u8RKI+hoUYc=</wsse:Password><wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">2+E62ViJx7kbw13ZlMgGBA==</wsse:Nonce><wsu:Created>2016-11-28T02:57:07.932Z</wsu:Created></wsse:UsernameToken></wsse:Security></soap:Header><soap:Body><ns2:sayHello xmlns:ns2="http://inter.service.test.com/"><arg0>cxf</arg0></ns2:sayHello></soap:Body></soap:Envelope>
除了这种基于用户名与密码的身份认证方式外,还有一种更加安全的身份认证方式,名为“数字签名”。

2、基于数字签名的身份认证

数字签名从字面上理解就是一种基于数字的签名方式。也就是说,当客户端发送 SOAP 消息时,需要对其进行“签名”,来证实自己的身份,当服务端接收 SOAP 消息时,需要对其签名进行验证(简称“验签”)。
在客户端与服务端上都有各自的“密钥库”,这个密钥库里存放了“密钥对”,而密钥对实际上是由“公钥”与“私钥”组成的。当客户端发送 SOAP 消息时,需要使用自己的私钥进行签名,当客户端接收 SOAP 消息时,需要使用客户端提供的公钥进行验签。
因为有请求就有相应,所以客户端与服务端的消息调用实际上是双向的,也就是说,客户端与服务端的密钥库里所存放的信息是这样的:
  • 客户端密钥库:客户端的私钥(用于签名)、服务端的公钥(用于验签)
  • 服务端密钥库:服务端的私钥(用于签名)、客户端的公钥(用于验签)
记住一句话:使用自己的私钥进行签名,使用对方的公钥进行验签。
因此,生成密钥库是我们第一步需要做的事情。

第一步:生成密钥库

现在你需要创建一个keystore.bat的批处理文件,其内容如下:
@echo offkeytool -genkeypair -alias server -keyalg RSA -dname "cn=server" -keypass serverpass -keystore server_store.jks -storepass storepasskeytool -exportcert -alias server -file server_key.rsa -keystore server_store.jks -storepass storepasskeytool -importcert -alias server -file server_key.rsa -keystore client_store.jks -storepass storepass -nopromptdel server_key.rsakeytool -genkeypair -alias client -dname "cn=client" -keyalg RSA -keypass clientpass -keystore client_store.jks -storepass storepasskeytool -exportcert -alias client -file client_key.rsa -keystore client_store.jks -storepass storepasskeytool -importcert -alias client -file client_key.rsa -keystore server_store.jks -storepass storepass -nopromptdel client_key.rsa
在以上这些命令中,使用了 JDK 提供的 keytool 命令行工具,关于该命令的使用方法,可点击以下链接:
http://docs.oracle.com/javase/6/docs/technotes/tools/solaris/keytool.html
运行该批处理程序,将生成两个文件:server_store.jks 与 client_store.jks,随后将 server_store.jks 放入服务端的 classpath 下,将 client_store.jks 放入客户端的 classpath 下。如果您在本机运行,那么本机既是客户端又是服务端。

第二步:完成服务端CXF相关配置

spring-cxf.xml文件配置:
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"       xmlns:cxf="http://cxf.apache.org/core"       xmlns:jaxws="http://cxf.apache.org/jaxws"       xsi:schemaLocation="http://www.springframework.org/schema/beans       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd       http://cxf.apache.org/core       http://cxf.apache.org/schemas/core.xsd       http://cxf.apache.org/jaxws       http://cxf.apache.org/schemas/jaxws.xsd">            <bean id="serverPasswordCallback" class="com.test.handler.ServerPasswordCallback"></bean>        <bean id="wss4jInInterceptor" class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor">        <constructor-arg>            <map>                <!-- 验签(使用对方的公钥) -->                <entry key="action" value="Signature"/>                <entry key="signaturePropFile" value="server.properties"></entry>            </map>        </constructor-arg>    </bean>    <jaxws:endpoint id="helloService" implementor="#helloServiceImpl" address="/soap/hello">        <jaxws:inInterceptors>            <ref bean="wss4jInInterceptor"/>        </jaxws:inInterceptors>    </jaxws:endpoint>    <cxf:bus>        <cxf:features>            <cxf:logging/>        </cxf:features>    </cxf:bus></beans>
其中action为Signature,server.properties内容如下:
org.apache.ws.security.crypto.provider=org.apache.wss4j.common.crypto.Merlinorg.apache.ws.security.crypto.merlin.file=server_store.jksorg.apache.ws.security.crypto.merlin.keystore.type=jksorg.apache.ws.security.crypto.merlin.keystore.password=storepass

第三步:完成客户端CXF相关配置

spring-client.xml文件配置:
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"       xmlns:context="http://www.springframework.org/schema/context"       xmlns:jaxws="http://cxf.apache.org/jaxws"       xsi:schemaLocation="http://www.springframework.org/schema/beans       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd       http://www.springframework.org/schema/context       http://www.springframework.org/schema/context/spring-context-3.0.xsd       http://cxf.apache.org/jaxws       http://cxf.apache.org/schemas/jaxws.xsd"><bean id="clientPasswordCallback" class="com.test.handler.ClientPasswordCallback"></bean>        <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>    <jaxws:client id="helloService"                  serviceClass="com.test.service.inter.HelloService"                  address="http://localhost:8080/springCxfWSS4J/services/soap/hello">        <jaxws:outInterceptors>            <ref bean="wss4jOutInterceptor"/>        </jaxws:outInterceptors>    </jaxws:client></beans>

其中action为Signature,client.properties文件内容如下:
org.apache.ws.security.crypto.provider=org.apache.wss4j.common.crypto.Merlinorg.apache.ws.security.crypto.merlin.file=client_store.jksorg.apache.ws.security.crypto.merlin.keystore.type=jksorg.apache.ws.security.crypto.merlin.keystore.password=storepass
此外,客户端同样需要提供签名用户(signatureUser)和密码回调处理器(passwordCallbackRef)。

第四步:发布、调用WS服务,并观察控制台日志

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Header><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"><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#" Id="SIG-9f4bad52-a973-439b-8eda-db7bde3fb06e"><ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"><ec:InclusiveNamespaces xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="soap"/></ds:CanonicalizationMethod><ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/><ds:Reference URI="#id-c68b56d9-7ed4-4b3b-80cd-73283e09f1ab"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><ds:DigestValue>+CL/+5vsXW07y9PThudROCUYV30=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>PfqRLe2lIKHOLC/LGGsVdMLFTPx9/88fGDNLgwAWS+/XTsVvPRKF5tAEGo5KQ1bO2DBSUeJzCV776EKXC6S1GGDP70DU43eyOPfnZfYPF8NJZQFCJJe9Ir1lUZ8XJNEK9Puriw2XM/UO+P4/OJVRJql/EFFuhES1cNlsvDxM5yYkfEwKhv8qc5IuwvviJMN7jIbrQOFP8/xDE35wabrLp+xOUNLGRiyWDPAVEb+rYdPbHwJHVo/KwWKkje43IRIFc+KXL833aQHm27UBWuOcHl5tL4aZHydcc8/UvWU7IK9soU+zis3R1z9Ks9jwDlsHkvR6xT+cCGxfsKSqR4m+ZA==</ds:SignatureValue><ds:KeyInfo Id="KI-c3ebd63c-0772-476a-ad65-05b82ef4c304"><wsse:SecurityTokenReference 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" wsu:Id="STR-766e16c6-5a5d-4b1c-b06e-a91a9f4a2d1f"><ds:X509Data><ds:X509IssuerSerial><ds:X509IssuerName>CN=client</ds:X509IssuerName><ds:X509SerialNumber>508496482</ds:X509SerialNumber></ds:X509IssuerSerial></ds:X509Data></wsse:SecurityTokenReference></ds:KeyInfo></ds:Signature></wsse:Security></soap:Header><soap:Body xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="id-c68b56d9-7ed4-4b3b-80cd-73283e09f1ab"><ns2:sayHello xmlns:ns2="http://inter.service.test.com/"><arg0>cxf</arg0></ns2:sayHello></soap:Body></soap:Envelope>
可见,数字签名是一种更为安全的身份认证方式,但无法对SOAP Body中的数据进行加密,究竟怎样才能加密并解密SOAP中的数据呢?

3、SOAP消息的加密与解密

WSS4J 除了提供签名与验签(Signature)这个特性以外,还提供了加密与解密(Encrypt)功能,您只需要在服务端与客户端的配置中稍作修改即可。
服务端:
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"       xmlns:cxf="http://cxf.apache.org/core"       xmlns:jaxws="http://cxf.apache.org/jaxws"       xsi:schemaLocation="http://www.springframework.org/schema/beans       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd       http://cxf.apache.org/core       http://cxf.apache.org/schemas/core.xsd       http://cxf.apache.org/jaxws       http://cxf.apache.org/schemas/jaxws.xsd">            <bean id="serverPasswordCallback" class="com.test.handler.ServerPasswordCallback"></bean>        <!-- 使用数字签名的方式进行身份认证,并对SOAP消息数据进行加密与解密 -->    <bean id="wss4jInInterceptor" class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor">        <constructor-arg>            <map>                <!-- 验签与解密 -->                <entry key="action" value="Signature Encrypt"/>                <!-- 验签(使用对方的公钥) -->                <entry key="signaturePropFile" value="server.properties"></entry>                <!-- 解密(使用自己的私钥) -->                <entry key="decryptionPropFile" value="server.properties"></entry>                <entry key="passwordCallbackRef" value-ref="serverPasswordCallback"></entry>            </map>        </constructor-arg>    </bean>    <jaxws:endpoint id="helloService" implementor="#helloServiceImpl" address="/soap/hello">        <jaxws:inInterceptors>            <ref bean="wss4jInInterceptor"/>        </jaxws:inInterceptors>    </jaxws:endpoint>    <cxf:bus>        <cxf:features>            <cxf:logging/>        </cxf:features>    </cxf:bus></beans>
客户端:
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"       xmlns:context="http://www.springframework.org/schema/context"       xmlns:jaxws="http://cxf.apache.org/jaxws"       xsi:schemaLocation="http://www.springframework.org/schema/beans       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd       http://www.springframework.org/schema/context       http://www.springframework.org/schema/context/spring-context-3.0.xsd       http://cxf.apache.org/jaxws       http://cxf.apache.org/schemas/jaxws.xsd"><bean id="clientPasswordCallback" class="com.test.handler.ClientPasswordCallback"></bean>    <!-- 使用数字签名的方式进行身份认证,并对SOAP消息数据进行加密与解密 -->    <bean id="wss4jOutInterceptor" class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor">        <constructor-arg>            <map>                <!-- 签名与加密 -->                <entry key="action" value="Signature Encrypt"/>                <!-- 签名(使用自己的私钥) -->                <entry key="signaturePropFile" value="client.properties"/><entry key="signatureUser" value="client"/>                <entry key="passwordCallbackRef" value-ref="clientPasswordCallback"/>                <!-- 加密(使用对方的公钥) -->                <entry key="encryptionPropFile" value="client.properties"></entry>                <entry key="encryptionUser" value="server"></entry>            </map>        </constructor-arg>    </bean>    <jaxws:client id="helloService"                  serviceClass="com.test.service.inter.HelloService"                  address="http://localhost:8080/springCxfWSS4J/services/soap/hello">           <jaxws:outInterceptors>            <ref bean="wss4jOutInterceptor"/>        </jaxws:outInterceptors>    </jaxws:client></beans>
可见,客户端发送 SOAP 消息时进行签名(使用自己的私钥)与加密(使用对方的公钥),服务端接收 SOAP 消息时进行验签(使用对方的公钥)与解密(使用自己的私钥)。
在您看到的 SOAP 消息应该是这样的:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Header><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"><xenc:EncryptedKey xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" Id="EK-6cc23c21-78b9-42af-826b-43bf848ee75d"><xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p"/><ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><wsse:SecurityTokenReference><ds:X509Data><ds:X509IssuerSerial><ds:X509IssuerName>CN=server</ds:X509IssuerName><ds:X509SerialNumber>979617998</ds:X509SerialNumber></ds:X509IssuerSerial></ds:X509Data></wsse:SecurityTokenReference></ds:KeyInfo><xenc:CipherData><xenc:CipherValue>fk/AirRnUfefQwjWViwuCNbKM7KbVVKLwF5xn+V1g4XfHZZ8/i/QrXcfnmGcojub8nvLZNg6ux2/PT+Nay6Sb9+PRuC8vqsGSbWSVRv9P1nNMpN4Ie5CBCwLM9iYCfgMSCx9KfqnRyxFlQBRcHmVKhwicUuszFSR/IrZ1BGAjtJw11EO5cCSmKJcrI2Pl8qB63u2LUE8lN0TjbkoIo9ll/PlamL5sw06rTa1ZDa/07rUZzIfhoU4grRHCY1skkdy3RQwnjG3mgpve7eZ1DTf05y3TW+NDQgDpv871V/nS4x0thYIIX6umgIOFMoV/MGzQ1ccyADNh8P27lI7yjbI4g==</xenc:CipherValue></xenc:CipherData><xenc:ReferenceList><xenc:DataReference URI="#ED-e61cc751-ab7f-4902-ad40-7485e7564081"/></xenc:ReferenceList></xenc:EncryptedKey><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#" Id="SIG-f61e980c-1d65-45ca-91a2-1451a3022487"><ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"><ec:InclusiveNamespaces xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="soap"/></ds:CanonicalizationMethod><ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/><ds:Reference URI="#id-e9cfcc39-770a-4ba2-9acd-0dc65e7dcfbb"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><ds:DigestValue>INGDwn+fnUGbhuwdNCvujDahddc=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>NASMeogydnAQE0oAEDf0V1/llihU2kuD/R0ZforidXDX+EJOZkfQ6uc1XIhZGI31yxYjZ5h3WxSZaIJuurxPZiq+DNlDEKUntYKMOKljClJyjjZTCGUor7fuiIgh9W2tbarQewxfWRes0I5kNWREebpyN3qkYMb0MRBwl5r2J8guJYCgr2zVvK6Bo08aSuIpSR3/ylrAgV3UaVIsM00fV9OEXFYpbSJGbrE+VV2uoDoCjN805MT0kyaYjOTPQSZdYNy/pnGUV8uxVVD55lKIdiyabnGC+J+wG2u80JeILb5m6wvtLyHSk2bgQeAqdZ3DTqTz7Bn6Up4xX8C0L+ifrw==</ds:SignatureValue><ds:KeyInfo Id="KI-34532e42-8ec8-4cd8-bb94-a705ccbc10bd"><wsse:SecurityTokenReference 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" wsu:Id="STR-fddf7d5a-3230-491d-b84a-a8dc2838a23d"><ds:X509Data><ds:X509IssuerSerial><ds:X509IssuerName>CN=client</ds:X509IssuerName><ds:X509SerialNumber>508496482</ds:X509SerialNumber></ds:X509IssuerSerial></ds:X509Data></wsse:SecurityTokenReference></ds:KeyInfo></ds:Signature></wsse:Security></soap:Header><soap:Body xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="id-e9cfcc39-770a-4ba2-9acd-0dc65e7dcfbb"><xenc:EncryptedData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" Id="ED-e61cc751-ab7f-4902-ad40-7485e7564081" Type="http://www.w3.org/2001/04/xmlenc#Content"><xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc"/><ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><wsse:SecurityTokenReference xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsse11="http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd" wsse11:TokenType="http://docs.oasis-open.org/wss/oasis-wss-soap-message-security-1.1#EncryptedKey"><wsse:Reference URI="#EK-6cc23c21-78b9-42af-826b-43bf848ee75d"/></wsse:SecurityTokenReference></ds:KeyInfo><xenc:CipherData><xenc:CipherValue>BWCrzKudYIxdpOOCE+y3cSnSoJiZu22p4FxgSR2grJ8IVD2hHmMTBQlPT+o1RruK3kg6jM/fGlgabHzpwHBQ+z4uTTsAuXAnkkAeB9UPaxC2iZ0b9y4kBlCD/qQEBb6M+vRD05SvIgv5QhJPpf72Vg==</xenc:CipherValue></xenc:CipherData></xenc:EncryptedData></soap:Body></soap:Envelope>

可见,SOAP 请求不仅签名了,而且还加密了,这样的通讯更加安全可靠。

4. 总结

本文的内容有些多,确实需要稍微总结一下:

  1. WSDL 是用于描述 WS 的具体内容的
  2. SOAP 是用于封装 WS 请求与响应的
  3. 可使用“用户令牌”方式对 WS 进行身份认证(支持明文密码与密文密码)
  4. 可使用“数字签名”方式对 WS 进行身份认证
  5. 可对 SOAP 消息进行加密与解密











0 0
原创粉丝点击