CXF基于spring配置X.509数字证书安全验证 有更新!
首先开发服务器端
用的是spring集成cxf,可以直接发布到tomcat中。
新建一个web项目:cxftest,导入cxf和spring相关jar包,pom.xml如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.skong</groupId>
<artifactId>cxf-test</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>skong-cxf-demo</name>
<description>通过类来生成wsdl文件, 通过pom中的cxf-codegen生成对应的客户端,服务端</description>
<properties>
<cxf.version>3.1.8</cxf.version>
<spring.version>4.2.3.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-ws-security</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-tools-java2ws</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http-jetty</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
在web.xml中配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app30.xsd"
id="WebApp_ID" version="3.0">
<display-name>cxfserver</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- Spring 刷新Introspector防止内存泄露 -->
<listener>
<listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>
</listener>
<!-- CXF Servlet -->
<servlet>
<servlet-name>cxfservlet</servlet-name>
<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>cxfservlet</servlet-name>
<!-- 匹配/services下的所有请求 -->
<url-pattern>/ws/*</url-pattern>
</servlet-mapping>
<!-- End CXF Servlet -->
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
然后开发WebService接口:
HelloService:
import org.apache.cxf.interceptor.InInterceptors;
import org.apache.cxf.interceptor.OutInterceptors;
import javax.jws.WebService;
/**
- Created by sily on 2016/10/25.
*/
@WebService
@InInterceptors(interceptors = {"org.apache.cxf.interceptor.LoggingInInterceptor"})
@OutInterceptors(interceptors = {"org.apache.cxf.interceptor.LoggingOutInterceptor"})
public interface HelloService {
String say(String name);
}
开发实现类:
HelloServiceImpl:
package com.skong.springcxf;
import org.springframework.stereotype.Component;
import javax.jws.WebService;
/**
* Created by sily on 2016/10/25.
*/
@WebService
@Component
public class HelloServiceImpl implements HelloService {
public String say(String name) {
return "hello " + name;
}
}
import javax.jws.WebService;
/**
* Created by sily on 2016/10/25.
*/
@WebService
@Component
public class HelloServiceImpl implements HelloService {
public String say(String name) {
return "hello " + name;
}
}
下面在spring中集成,直接发布到tomcat:beans.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: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">
<!-- jar包中自带的cxf文件夹下的*.xml文件 -->
<import resource="classpath:META-INF/cxf/cxf.xml"/>
<!--<import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />-->
<import resource="classpath:META-INF/cxf/cxf-servlet.xml"/>
<!-- 配置服务端方法所在的类及数字签名 -->
<jaxws:endpoint id="ISecuriyService" implementor="com.tongtech.ti.cxf.demo.security.service.ISecuriyServicePortImpl"
address="/sec">
<!-- 通过拦截器对客户端发送的数据和签名进行解密处理 -->
<jaxws:inInterceptors>
<bean class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor">
<constructor-arg>
<map>
<entry key="action" value="Timestamp Signature Encrypt"/>
<entry key="signaturePropFile" value="cert/Server_SignVerf.properties"/>
<entry key="decryptionPropFile" value="cert/Server_Decrypt.properties"/>
<entry key="passwordCallbackClass"
value="com.tongtech.ti.cxf.demo.security.X509.server.UTPasswordServerCallBack"/>
</map>
</constructor-arg>
</bean>
</jaxws:inInterceptors>
<!-- 通过拦截器将服务端发送给客户端的数据和签名进行加密 -->
<jaxws:outInterceptors>
<bean class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor">
<constructor-arg>
<map>
<entry key="action" value="Timestamp Signature Encrypt"/>
<entry key="user" value="serverprivatekey"/>
<entry key="encryptionUser" value="clientpublickey"/>
<entry key="signaturePropFile" value="cert/Server_Decrypt.properties"/>
<entry key="encryptionPropFile" value="cert/Server_SignVerf.properties"/>
<entry key="passwordCallbackClass"
value="com.tongtech.ti.cxf.demo.security.X509.server.UTPasswordServerCallBack"/>
</map>
</constructor-arg>
<property name="allowMTOM" value="true"></property>
</bean>
</jaxws:outInterceptors>
</jaxws:endpoint>
<jaxws:client id="renderClient" address="http://localhost:8080/ws/sec"
serviceClass="com.tongtech.ti.cxf.demo.security.service.ISecuriyDemo">
<jaxws:inInterceptors>
<bean class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor">
<constructor-arg>
<map>
<entry key="action" value="Timestamp Signature Encrypt"/>
<entry key="signaturePropFile" value="cert/Client_Encrypt.properties"/>
<entry key="decryptionPropFile" value="cert/Client_Sign.properties"/>
<entry key="passwordCallbackClass"
value="com.tongtech.ti.cxf.demo.security.X509.server.UTPasswordServerCallBack"/>
</map>
</constructor-arg>
</bean>
</jaxws:inInterceptors>
<jaxws:outInterceptors>
<bean class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor">
<constructor-arg>
<map>
<entry key="action" value="Timestamp Signature Encrypt"/>
<entry key="user" value="clientprivatekey"/>
<entry key="encryptionUser" value="serverpublickey"/>
<entry key="signaturePropFile" value="cert/Client_Sign.properties"/>
<entry key="encryptionPropFile" value="cert/Client_Encrypt.properties"/>
<entry key="passwordCallbackClass"
value="com.tongtech.ti.cxf.demo.security.X509.server.UTPasswordServerCallBack"/>
</map>
</constructor-arg>
<property name="allowMTOM" value="true"></property>
</bean>
</jaxws:outInterceptors>
</jaxws:client>
</beans>
UTPasswordServerCallBack 证书密码回调类
package com.tongtech.ti.cxf.demo.security.X509.server;
import org.apache.wss4j.common.ext.WSPasswordCallback;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import java.io.IOException;
/**
- Created by sily on 2016/10/24.
*/
public class UTPasswordServerCallBack implements CallbackHandler {
public void handle(Callback[] callbacks) throws IOException,
UnsupportedCallbackException {
WSPasswordCallback pc = (WSPasswordCallback) callbacks[0];
pc.setPassword("keypass");
System.out.println("Server Identifier=" + pc.getIdentifier());
System.out.println("Server Password=" + pc.getPassword());
}
}
这个文件就是上面web.xml中引入的文件。
所有的配置都在spring的配置文件中,服务器端开发完毕,下面请看客户端的开发:
客户端的开发
客户端要想进行认证,首先也要同样实现一个回调类(大家可以测一下,用常规的不带认证的方法写一个Client.java,是无法访问webservice的)
这个回调函数的类如下:
UTPasswordClientCallBack
package com.tongtech.ti.cxf.demo.security.X509.client;
import org.apache.wss4j.common.ext.WSPasswordCallback;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import java.io.IOException;
/**
- Created by sily on 2016/10/24.
*/
public class UTPasswordClientCallBack implements CallbackHandler {
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
WSPasswordCallback pc = (WSPasswordCallback) callbacks[0];
pc.setPassword("keypass");
System.out.println("Client Identifier=" + pc.getIdentifier());
System.out.println("Client Password=" + pc.getPassword());
}
}
最后是客户端的完整测试代码:
ClientCxfSpring
package com.tongtech.ti.cxf.demo.security.X509.client;
import com.skong.springcxf.HelloService;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Created by sily on 2016/10/26.
*/
public class ClientCxfSpring {
public static void main(String[] args) {
System.out.println("Start client.....");
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
new String[] { "classpath:bean.xml" });
HelloService renderClient = (HelloService) context.getBean("renderClient");
System.out.println(renderClient.say("test"));
System.exit(0);
}
}
生成证书的文件夹放入cert文件夹中
(一)生成证书
生成证书还是比较麻烦的,要用到jdk的一个工具——keytool
首先,创建客户端KeyStore和公钥
在命令行运行:
1、创建私钥和KeyStore:
Java代码
1. keytool -genkey -alias clientprivatekey -keypass keypass -keystore Client_KeyStore.jks -storepass storepass -dname "CN=tongtech.com,C=CN" -keyalg RSA
创建KeyStore,文件名字为Client_KeyStore.jks,里面有个名为clientprivatekey的私钥。
2、给私钥进行自签名:
Java代码
1. keytool -selfcert -keystore Client_KeyStore.jks -storepass storepass -alias clientprivatekey -keypass keypass
签名成功,无任何提示。
3、导出私钥
作用是导出的证书将作为公钥保存到TrustStore中。
Java代码
1. keytool -export -alias clientprivatekey -file Client_PublicCert.cer -keystore Client_KeyStore.jks -storepass storepass
如果成功,可以看到提示:
保存在文件中的认证 <Client_PublicCert.cer>
然后创建服务端KeyStore
1、创建私钥和KeyStore
Java代码
1. keytool -genkey -alias serverprivatekey -keypass keypass -keystore Server_KeyStore.jks -storepass storepass -dname "CN=tongtech.com,C=CN" -keyalg RSA
2、给私钥进行自签名
Java代码
1. keytool -selfcert -keystore Server_KeyStore.jks -storepass storepass -alias serverprivatekey -keypass keypass
3、导出私钥
Java代码
1. keytool -export -alias serverprivatekey -file Server_PublicCert.cer -keystore Server_KeyStore.jks -storepass storepass
接下来,将客户端公钥导入到服务端TrustStore中,将服务端公钥导入到客户端TrustStore中。
在命令行中输入:
Java代码
1. keytool -import -alias clientpublickey -file Client_PublicCert.cer -keystore Server_TrustStore.jks -storepass storepass
回车后会提示
引用
所有者:CN=tongtech.com, C=CN
签发人:CN=tongtech.com, C=CN
序列号:4cc7e86c
有效期: Wed Oct 27 16:53:00 CST 2010 至Tue Jan 25 16:53:00 CST 2011
证书指纹:
MD5:FB:AB:71:9F:56:F3:CB:65:16:DC:52:E0:2D:27:FF:F6
SHA1:06:A8:B1:B4:E2:42:9D:B2:F7:99:E7:70:34:08:96:52:E1:CD:4A:76
签名算法名称:SHA1withRSA
版本: 3
信任这个认证? [否]:
打y即可,然后提示
引用
认证已添加至keystore中
同理,将服务端公钥导入到客户端TrustStore中
Java代码
1.
同样会出现提示,打y回车,提示成功就可以了。
到这里会有个疑问,为什么都叫keystore?在最上面已经提到,KeyStore和TrustStore是概念上的区分。
(二)编写代码
将上面生成好的maven工程导入到eclipse中,在src/main/java下建立新的包,起名叫cert,将刚刚生成好的KeyStore和TrustStore拷到cert包下。
1、创建配置文件
建立客户端加密/解密配置:Client_Encrypt.properties
Java代码
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=storepass
org.apache.ws.security.crypto.merlin.keystore.alias=serverpublickey
org.apache.ws.security.crypto.merlin.file=cert/Client_TrustStore.jks
建立客户端验签/签名配置:Client_Sign.properties
Java代码
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=storepass
org.apache.ws.security.crypto.merlin.keystore.alias=clientprivatekey
org.apache.ws.security.crypto.merlin.file=cert/Client_KeyStore.jks
建立服务端加密/解密配置:Server_Decrypt.properties
Java代码
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=storepass
org.apache.ws.security.crypto.merlin.keystore.alias=serverprivatekey
org.apache.ws.security.crypto.merlin.file=cert/Server_KeyStore.jks
建立服务端验签/签名配置:Server_SignVerf.properties
Java代码
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=storepass
org.apache.ws.security.crypto.merlin.keystore.alias=clientpublickey
org.apache.ws.security.crypto.merlin.file=cert/Server_TrustStore.jks