上一篇讲了如何使用UsernameToken的方式来安全访问CXF,这篇将讲解使用证书的签名和加密技术来达到安全访问的目的。
1.证书的签名和加密的原理
在CXF官网关于WS-SECURITY的章节中首先介绍了,签名和加密的原理,图和文字很形象,就不再多说了。
下面附上本文中使用的生成证书的代码:
1. 生成别名和密码为 "serverkey"/"myPassword"的服务端证书,别名都使用小写(在keystore中存储的别名都是小写字符), 并保存在server-keystore.jks中(改证书用来服务端解密) keytool -genkey -alias serverkey -validity 365 -keypass myPassword -keystore server-keystore.jks -storepass myPassword -dname "cn=serverkey" -keyalg RSA 2. 自签名我们的生成的证书(正式环境应该由正式的公司来做这个步骤,比如Verisign) keytool -selfcert -alias serverkey -validity 365 -keystore server-keystore.jks -storepass myPassword -keypass myPassword 3. 从服务端keystore中导出公钥并且命名为 key.cer keytool -export -alias serverkey -file serverkey.cer -keystore server-keystore.jks -storepass myPassword 4. 将步骤3导出的证书导入到客户端的client-truststore.jks(用来做客户端加密) keytool -import -alias serverkey -file serverkey.cer -keystore client-truststore.jks -storepass myPassword 5. 生成别名和密码为 "clientkey"/"myPassword"的客户端证书, 并保存在client-keystore.jks中(改证书用来服务端解密) keytool -genkey -alias clientkey -validity 365 -keypass myPassword -keystore client-keystore.jks -storepass myPassword -dname "cn=clientkey" -keyalg RSA 6. 自签名我们的生成的证书(正式环境应该由正式的公司来做这个步骤,比如Verisign) keytool -selfcert -alias clientkey -validity 365 -keystore client-keystore.jks -storepass myPassword -keypass myPassword 7. 从客户端keystore中导出公钥并且命名为 key.cer keytool -export -alias clientkey -file clientkey.cer -keystore client-keystore.jks -storepass myPassword 8. 将步骤3导出的证书导入到服务端的server-truststore.jks(用来做客户端加密) keytool -import -alias clientkey -file clientkey.cer -keystore server-truststore.jks -storepass myPassword
执行完,你可以在%JDK_HOME%/bin目录得到4个jks文件(数字证书库),这就是我们即将用来加密和签名的证书文件了。
2.添加四个证书配置文件
- Client_Encrypt.properties
- Client_Sign.properties
- Server_Decrypt.properties
- Server_SignVerf.properties
四个文件格式都一样,里面配置的keystore的类型、地址、密码以及做相应操作的证书别名。
内容如下:
org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin org.apache.ws.security.crypto.merlin.keystore.type=jks org.apache.ws.security.crypto.merlin.keystore.password=myPassword org.apache.ws.security.crypto.merlin.keystore.alias=clientKey org.apache.ws.security.crypto.merlin.keystore.file=resource/keystore/server-truststore.jks
3.修改客户端和服务端spring配置文件
各个配置文件中的内容相应做了注释,请看下面的详细文件
服务端配置:
<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" /> <jaxws:endpoint id="helloWorld" implementor="com.demo.cxf.helloword.impl.HelloWordImpl" address="/HelloWorld"> <jaxws:inInterceptors> <ref bean="serverWSS4JInInterceptor" /> <bean class="com.demo.cxf.helloword.ClientIpInInterceptor" /> <bean class="org.apache.cxf.interceptor.LoggingInInterceptor" /> </jaxws:inInterceptors> <jaxws:outInterceptors> <ref bean="serverWSS4JOutInterceptor" /> <bean class="org.apache.cxf.interceptor.LoggingOutInterceptor" /> </jaxws:outInterceptors> </jaxws:endpoint> <bean id="passwordCallback" class="com.demo.cxf.callbacks.PasswordCallback"></bean> <bean id="serverWSS4JInInterceptor" class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor"> <property name="properties"> <map> <entry key="action" value="Timestamp Encrypt Signature" /> <!-- 服务器会自动在SOAP中拿到解码(私钥)的用户名,并在 PasswordCallback中取到密码。 公钥不需要密码。 --> <entry key="passwordCallbackRef"> <ref bean="passwordCallback" /> </entry> <entry key="decryptionPropFile" value="resource/properties/Server_Decrypt.properties" /> <entry key="signaturePropFile" value="resource/properties/Server_SignVerf.properties" /> </map> </property> </bean> <bean id="serverWSS4JOutInterceptor" class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor"> <property name="properties"> <map> <!-- 此处配置需注意,当指定Signature时就必须像UsernameToken那样指定user和passwordCallbackRef。 因为假如我们没指定signatureUser或者encryptionUser,CXF将会使用user来替代之,而signatureUser的 密码必须通过passwordCallbackRef赋值。所以哪怕定义了signatureUser也必须同时定义user, 且不能为空。 公钥不需要密码。 --> <entry key="action" value="Timestamp Encrypt Signature" /> <!-- MD5加密明文密码 --> <entry key="passwordType" value="PasswordDigest" /> <!-- 该用户名只能在激活了UsernameToken时才能拿到并使用 --> <entry key="user" value="admin" /> <entry key="passwordCallbackRef"> <ref bean="passwordCallback" /> </entry> <entry key="encryptionPropFile" value="resource/properties/Server_SignVerf.properties" /> <entry key="encryptionUser" value="clientkey" /> <entry key="signaturePropFile" value="resource/properties/Server_Decrypt.properties" /> <entry key="signatureUser" value="serverkey" /> </map> </property> </bean> </beans>
客户端配置:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:jaxws="http://cxf.apache.org/jaxws" 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.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd"> <jaxws:client id="helloClient" serviceClass="com.demo.cxf.helloword.HelloWord" address="http://10.248.157.51:8080/web_service/services/HelloWorld"> <jaxws:inInterceptors> <ref bean="clientWSS4JInInterceptor"/> </jaxws:inInterceptors> <jaxws:outInterceptors> <ref bean="clientWSS4JOutInterceptor" /> </jaxws:outInterceptors> </jaxws:client> <bean id="passwordCallback" class="com.demo.cxf.callbacks.PasswordCallback"></bean> <bean id="clientWSS4JInInterceptor" class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor"> <property name="properties"> <map> <entry key="action" value="Timestamp Encrypt Signature" /> <entry key="passwordCallbackRef"> <ref bean="passwordCallback" /> </entry> <entry key="decryptionPropFile" value="resource/properties/Client_Sign.properties" /> <entry key="signaturePropFile" value="resource/properties/Client_Encrypt.properties" /> </map> </property> </bean> <bean id="clientWSS4JOutInterceptor" class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor"> <property name="properties"> <map> <entry key="action" value="Timestamp Encrypt Signature" /> <entry key="passwordType" value="PasswordDigest" /> <entry key="user" value="admin" /> <entry key="passwordCallbackRef"> <ref bean="passwordCallback" /> </entry> <entry key="encryptionPropFile" value="resource/properties/Client_Encrypt.properties" /> <entry key="encryptionUser" value="serverkey" /> <entry key="signaturePropFile" value="resource/properties/Client_Sign.properties" /> <entry key="signatureUser" value="clientkey" /> </map> </property> </bean> </beans>
- 客户端发送数据前:使用服务端的公钥进行加密,同时使用客户端的私钥进行签名
- 服务端收到请求:使用服务端的私钥解密,并使用客户端的公钥进行签名验证
- 服务端响应前:使用客户端的公钥进行加密,同时使用服务端的私钥进行签名
- 客户端收到响应:使用客户端的私钥解密,并使用服务端的公钥进行签名验证
这两个配置文件的大致内容如上。
4.添加PasswordCallback
UsernameToken中就使用过,不多做解释,只是需要注意下面代码中的证书密码部分,证书密码在客户端和服务端分别只需要保存己方的私钥密码,公钥是不需要密码的。
package com.demo.cxf.callbacks; 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.ws.security.WSPasswordCallback; public class PasswordCallback implements CallbackHandler { Map<String, String> user = new HashMap<String, String>(); { // 用户名和密码 user.put("admin", "123"); user.put("su", "123"); // 证书的密码 user.put("serverkey", "myPassword"); user.put("clientkey", "myPassword"); } @Override public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { WSPasswordCallback wpc = (WSPasswordCallback) callbacks[0]; System.out.println(wpc.getIdentifier()); if (!user.containsKey(wpc.getIdentifier())) { throw new SecurityException("权限不足!"); } /* * 此处特别注意:: WSPasswordCallback 的passwordType属性和password 属性都为null, * 你只能获得用户名(identifier), 一般这里的逻辑是使用这个用户名到数据库中查询其密码, 然后再设置到password * 属性,WSS4J 会自动比较客户端传来的值和你设置的这个值。 你可能会问为什么这里CXF * 不把客户端提交的密码传入让我们在ServerPasswordCallbackHandler 中比较呢? * 这是因为客户端提交过来的密码在SOAP 消息中已经被加密为MD5 的字符串, * 如果我们要在回调方法中作比较,那么第一步要做的就是把服务端准备好的密码加密为MD5 字符串, 由于MD5 * 算法参数不同结果也会有差别,另外,这样的工作CXF 替我们完成不是更简单吗? */ // 如果包含用户名,就设置该用户名正确密码,由CXF验证密码 wpc.setPassword(user.get(wpc.getIdentifier())); } }
5.其他的SEI和IMPL请参考上一篇中的代码,完全一样,附上代码。
相关推荐
本文将深入探讨CXF安全访问的一个重要方面:HTTP基本认证(Http Basic Auth)。这是一种简单但有效的身份验证机制,适用于对Web服务进行安全控制。 HTTP基本认证是基于HTTP协议的,它在请求头中包含一个Base64编码...
3. **配置CXF**:通过XML配置文件(如`cxf-servlet.xml`)来告诉CXF如何发布你的服务。 4. **发布服务**:将服务绑定到一个URL,使得其他应用可以通过网络访问。 5. **生成客户端代码**:使用CXF的wsdl2java工具,...
本篇将详细探讨如何使用CXF实现单向SSL(Secure Sockets Layer)和双向SSL的安全访问。首先,我们需要理解SSL的基本概念。 SSL是一种网络安全协议,用于在客户端和服务器之间建立加密连接,确保数据传输的隐私和...
这不仅涵盖了基本的Web服务概念,还包括了CXF特有的特性,如WS-Security(安全)、WS-ReliableMessaging(可靠消息传递)等。 为了更好地理解CXF的使用,你需要理解以下几个关键概念: - **JAX-WS**:Java API for...
CXF允许开发者使用JAX-RS和JAX-WS标准来编写Web服务,并提供了丰富的功能,如WSDL(Web Services Description Language)生成、客户端API生成、数据绑定、安全支持等。 **4. CXF与Spring的整合** 将CXF与Spring...
CXF接口所有所需jar包 1.cxf-2.3.3.jar 2.geronimo-annotation_1.0_spec-1.1.1.jar 3.geronimo-jaxws_2.2_spec-1.0.jar 4.geronimo-stax-api_1.0_spec-1.0.1.jar 5.geronimo-ws-metadata_2.0_spec-1.1.3.jar 6...
org.apache.cxf.spring.remoting.Jsr181HandlerMapping.jar
在 Oracle 退出和变更许可后,红帽宣布接手维护 OpenJDK 8 和 OpenJDK 11,为这两个 OpenJDK 的旧版本提供错误修复和安全补丁。
在Java开发中,CXF框架是一个广泛使用的开源服务框架,它支持Web服务的创建和消费。CXF框架提供了丰富的功能,包括SOAP、RESTful API、JAX-RS和JAX-WS等标准的实现。然而,在某些情况下,尤其是在使用Java 6的环境中...
在服务器端,你可以使用CXF和WSS4J来设置安全策略,确保只有经过验证的客户端才能访问服务。 **Android客户端的令牌认证** 在Android客户端,使用Ksoap2进行认证通常涉及以下步骤: 1. **创建SOAP请求**:首先,你...
【标题】"02.CXF功能概述_CXF发展历史和使用CXF开发WebService服务器端"主要探讨了Apache CXF框架在Web服务领域的应用及其发展历程,同时也涵盖了如何利用CXF来构建一个高效的WebService服务器端。 Apache CXF是一...
它还能与其他框架如Hibernate和Struts无缝集成,提供了事务管理和数据访问抽象。 Struts作为MVC(Model-View-Controller)设计模式的实现,主要负责处理HTTP请求和响应,将用户界面与业务逻辑分离。在描述中提到的...
import org.apache.cxf.phase.AbstractPhaseInterceptor; import org.apache.cxf.phase.Phase; public class InInterceptor extends AbstractPhaseInterceptor<Message> { private int limit = 102400; public ...
CXF提供了强大的WSDL第一和WSDL第二类工具,允许从Java代码生成WSDL,或从WSDL生成Java代码。 - **Data Binding**: CXF支持多种数据绑定框架,如JAXB(Java Architecture for XML Binding)和JAX-BRI(Java API for...
3. **JAX-WS和JAX-RS实现**:CXF实现了Java API for XML Web Services (JAX-WS) 和Java API for RESTful Web Services (JAX-RS),使得开发者可以利用这些标准API轻松创建Web服务。 4. **多种绑定和数据格式支持**:...
3. `samples`目录:可能包含CXF官方提供的示例代码,这些示例可以帮助学习和理解如何使用CXF。 4. `docs`目录:可能包含CXF的用户手册、API文档和其他参考资料。 5. `schema`目录:可能包含CXF支持的XML schema定义...