CXF基于spring配置X.509数字证书安全验证 有更新!

  |   0 评论   |   858 浏览

    首先开发服务器端

    用的是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  
    

    评论

    发表评论

    validate