• cxf+spring+数字签名开发webservice(一)


    • 数字证书的准备

            下面做的服务端和客户端证书在例子中无法加解密,不知道什么原因,我是使用正式环境中的客户端和服务端进行开发测试的,所以需要大家自己去准备证书,或者有人知道为什么jdk生成的一对证书无法加解密的原因那在好不过了。(例子中客户端和服务端都放在一起项目中,大家自己分开开发测试即可)下面是我用jdk生成的证书:

            1.1、客户端             

                   1、创建私钥和keystore
                        keytool -genkey -alias clientprivatekey -keypass keypass -keystore client.jks -storepass storepass -dname "CN=zs.com,C=CN" -keyalg RSA
                   2、自签名
                        keytool -selfcert -keystore client.jks -storepass storepass -alias clientprivatekey -keypass keypass
                   3、导出公钥到rsa
                        keytool -export -alias clientprivatekey -file client.rsa -keystore client.jks -storepass storepass 

             1.2、服务端              

                   1、创建私钥和keystore
                        keytool -genkey -alias serverprivatekey -keypass keypass -keystore server.jks -storepass storepass -dname "CN=zs.com,C=CN" -keyalg RSA
                   2、自签名
                        keytool -selfcert -keystore server.jks -storepass storepass -alias serverprivatekey -keypass keypass
                   3、导出公钥到rsa
                        keytool -export -alias serverprivatekey -file server.rsa -keystore server.jks -storepass storepass

             1.3、将服务端公钥加入客户端jks中

                    keytool -import -alias serverprivatekey  -file server.rsa -keystore client.jks -storepass storepass

             1.4、将客户端公钥加入服务端jks中

                    keytool -import -alias clientprivatekey -file client.rsa -keystore server.jks -storepass storepass


    • 证书配置文件

             2.1、客户端配置

                   jks文件放在ca包下,ca包直接在项目的src目录下,配置文件名称为client.properties(放在ca下),注意等号两边不要出现空格     

    1 org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin 
    2 org.apache.ws.security.crypto.merlin.keystore.type=jks 
    3 org.apache.ws.security.crypto.merlin.keystore.password=证书的storepass
    4 org.apache.ws.security.crypto.merlin.keystore.alias=证书的别名
    5 org.apache.ws.security.crypto.merlin.file=ca/client.jks

             2.2、服务端配置

                   jks文件放在ca包下,ca包直接在项目的src目录下,配置文件名称为server.properties(放在ca下),注意等号两边不要出现空格

    1 org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin 
    2 org.apache.ws.security.crypto.merlin.keystore.type=jks 
    3 org.apache.ws.security.crypto.merlin.keystore.password=storepass
    4 org.apache.ws.security.crypto.merlin.keystore.alias=serverprivatekey
    5 org.apache.ws.security.crypto.merlin.file=ca/server.jks

    • 秘钥配置类

             3.1、客户端

     1 package cxf.test.client;
     2 
     3 import java.io.IOException;
     4 import javax.security.auth.callback.Callback;
     5 import javax.security.auth.callback.CallbackHandler;
     6 import javax.security.auth.callback.UnsupportedCallbackException;
     7 import org.apache.ws.security.WSPasswordCallback;
     8 
     9 public class UTPasswordClientCallBack implements CallbackHandler {
    10 
    11     @Override
    12     public void handle(Callback[] callbacks) throws IOException,UnsupportedCallbackException {
    13        WSPasswordCallback pc = (WSPasswordCallback) callbacks[0];
    14        pc.setPassword("证书的keypass");
    15        System.out.println("Client Identifier=" + pc.getIdentifier());
    16        System.out.println("Client Password=" + pc.getPassword());
    17     }
    18 
    19 }

             3.2、服务端

     1 package cxf.test.server;
     2 
     3 import java.io.IOException;
     4 
     5 import javax.security.auth.callback.Callback;
     6 import javax.security.auth.callback.CallbackHandler;
     7 import javax.security.auth.callback.UnsupportedCallbackException;
     8 
     9 import org.apache.ws.security.WSPasswordCallback;
    10 
    11 public class UTPasswordServerCallBack implements CallbackHandler {
    12 
    13     @Override
    14     public void handle(Callback[] callbacks) throws IOException,UnsupportedCallbackException {
    15        WSPasswordCallback pc = (WSPasswordCallback) callbacks[0];
    16        pc.setPassword("证书的keypass");
    17        System.out.println("Client Identifier=" + pc.getIdentifier());
    18        System.out.println("Client Password=" + pc.getPassword());
    19     }
    20 
    21  }

    • 配置文件

            4.1、web.xml配置

     1 <context-param>
     2     <param-name>contextConfigLocation</param-name>
     3     <param-value>WEB-INF/beans.xml</param-value><!-- spring配置文件 -->
     4 </context-param>
     5 <listener>
     6     <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
     7 </listener>
     8 <servlet>
     9     <servlet-name>CXFServlet</servlet-name>
    10     <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
    11 </servlet>
    12 <servlet-mapping>
    13     <servlet-name>CXFServlet</servlet-name>
    14     <url-pattern>/services/*</url-pattern><!-- 匹配services/*后缀url -->
    15 </servlet-mapping>

             4.2、beans.xml配置(spring配置文件)

     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <beans xmlns="http://www.springframework.org/schema/beans"
     3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxws="http://cxf.apache.org/jaxws"
     4     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">
     5     <!-- jar包中自带的cxf文件夹下的*.xml文件 -->
     6     <import resource="classpath:META-INF/cxf/cxf.xml" />
     7     <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
     8     <import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
     9     <!-- 配置服务端方法所在的类及数字签名 -->
    10     <jaxws:endpoint id="TestWebService" implementor="cxf.test.server.TestWebServiceImpl"
    11         address="/wstest">
    12         <!-- 通过拦截器对客户端发送的数据和签名进行解密处理 -->
    13         <jaxws:inInterceptors>
    14             <bean class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor">
    15                 <constructor-arg>
    16                     <map>
    17                         <entry key="action" value="Signature Encrypt" />
    18                         <entry key="signaturePropFile" value="ca/server.properties" />
    19                         <entry key="decryptionPropFile" value="ca/server.properties" />
    20                         <entry key="passwordCallbackClass" value="cxf.test.server.UTPasswordServerCallBack" />
    21                     </map>
    22                 </constructor-arg>
    23             </bean>
    24         </jaxws:inInterceptors>
    25         <!-- 通过拦截器将服务端发送给客户端的数据和签名进行加密 -->
    26         <jaxws:outInterceptors>
    27             <bean class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor">
    28                 <constructor-arg>
    29                     <map>
    30                         <entry key="action" value="Signature Encrypt" />
    31                         <entry key="user" value="服务端证书别名" />
    32                         <entry key="signatureUser" value="服务端证书别名" />
    33                         <entry key="signaturePropFile" value="ca/server.properties" />
    34                         <entry key="encryptionUser" value="useReqSigCert"></entry>
    35                         <entry key="decryptionPropFile" value="ca/server.properties" />
    36                         <entry key="passwordCallbackClass" value="cxf.test.server.UTPasswordServerCallBack" />
    37                     </map>
    38                 </constructor-arg>
    39                 <property name="allowMTOM" value="true"></property>
    40             </bean>
    41         </jaxws:outInterceptors>
    42     </jaxws:endpoint>
    43 </beans>

    • 服务端类

            5.1、接口类

     1 package cxf.test.server;
     2 
     3 import javax.jws.WebParam;
     4 import javax.jws.WebService;
     5 
     6 //配置targetNamespace,不进行配置,将使用当前类所在的包名顺序反向(如果这里没设置,默认命名空间为:http://server.test.cxf/)为命名空间。
     7 //不进行命名空间设置,会存在客户端和服务端需要保持包名一致的问题,这在现实开发中肯定是很麻烦的事
     8 @WebService(name = "TestWebService", targetNamespace = "http://cxf.test.tns/")
     9 public interface TestWebService {
    10     // 为了更好的兼容不同的开发语言、jdk版本等,接口方法的参数和返回结果最好是字符串,对象按xml格式进行编写和解析
    11     // 接口方法的参数最好使用annotation设定接口方法的参数名,不进行设置,默认会在参数前加上“{”等符号(具体符号可能有误),
    12     // 这样可能会给客户端调用带来不必要的麻烦,cxf有好几个annotation配置,如果客户端和服务端传递数据复杂类型参数时出现null等情况,一般进行相应annotation配置即可
    13     public abstract String testMethod(@WebParam(name = "xml") String xml);
    14 }

            5.2、接口实现类

    1 package cxf.test.server;
    2 
    3 
    4 public class TestWebServiceImpl implements TestWebService {
    5     public String testMethod(String xml) {
    6         System.out.println(xml);
    7         return "服务端被调用";
    8     }
    9 }
    • 客户端类

            6.1、接口类

     1 package cxf.test.client;
     2 
     3 import javax.jws.WebParam;
     4 import javax.jws.WebService;
     5 
     6 //使用annotation设置接口的命名空间。如果不进行设置,会出现客户端和服务端的接口类和bean类所在包名需保持一致的问题。出现对象作为参数
     7 //或返回结果时为null的情况,不进行设置,默认按包名的反向顺序(如果该接口方法不进行设置,默认命名空间为:http://server.test.cxf/)作为命名空间
     8 //最好是设置命名空间,包名暴露也不是好事
     9 @WebService(name = "TestWebService", targetNamespace = "http://cxf.test.tns/")
    10 public interface TestWebService {
    11     // 为了更好的兼容不同的开发语言、jdk版本等,接口方法的参数和返回结果最好是字符串,对象按xml格式进行编写和解析
    12     // 接口方法的参数最好使用annotation设定接口方法的参数名,不进行设置,默认会在参数前加上“{”等符号(具体符号可能有误),
    13     // 这样可能会给客户端调用带来不必要的麻烦
    14     public abstract String testMethod(@WebParam(name = "xml") String xml);
    15 }

             6.2、公共方法

     1 package cxf.test.client;
     2 
     3 import java.util.HashMap;
     4 import java.util.Map;
     5 import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
     6 import org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor;
     7 import org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor;
     8 
     9 /**
    10  * 客户端调用服务端公共类
    11  * 
    12  * @author yu
    13  * 
    14  */
    15 public class Client {
    16     /**
    17      * 调用服务端接口时,先调用该方法,获得服务端接口方法,该方法设置数字签名的加解密信息
    18      * 
    19      * @param address
    20      *            wsdl地址,包含“?wsdl”后缀
    21      * @return
    22      */
    23     public TestWebService call(String address) throws Exception {
    24         JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
    25         factory.setServiceClass(TestWebService.class);
    26         factory.setAddress(address);
    27         Map<String, Object> properties = new HashMap<String, Object>();
    28         properties.put("mtom-enabled", Boolean.TRUE);
    29         factory.setProperties(properties);
    30         // 客户端请求服务端时,客户端进行签名和加密,对应服务端请求拦截器。
    31         Map<String, Object> outProps = new HashMap<String, Object>();
    32         outProps.put("action", "Signature Encrypt");
    33         outProps.put("user", "85e46503a2958c9137b832ebca4ee304_e9c397b2-2e69-4de0-8183-e978aa23b1d5");// 客户端证书别名
    34         outProps.put("passwordCallbackClass",
    35                 UTPasswordClientCallBack.class.getName());
    36         outProps.put("encryptionUser", "9598de1694450bdbe56ff58d7fa55cb9_e9c397b2-2e69-4de0-8183-e978aa23b1d5");// 服务端证书别名
    37         outProps.put("encryptionPropFile", "ca/client.properties");// 客户端证书配置信息
    38         outProps.put("signatureUser", "85e46503a2958c9137b832ebca4ee304_e9c397b2-2e69-4de0-8183-e978aa23b1d5");// 客户端证书别名
    39         outProps.put("signaturePropFile", "ca/client.properties");
    40         WSS4JOutInterceptor outInterceptor = new WSS4JOutInterceptor(outProps);
    41         outInterceptor.setAllowMTOM(true);
    42         factory.getOutInterceptors().add(outInterceptor);
    43         // 服务端响应客户端请求时,客户端对服务端签名和加密进行处理,对应服务端响应拦截器
    44         Map<String, Object> inProps = new HashMap<String, Object>();
    45         inProps.put("action", "Signature Encrypt");
    46         inProps.put("passwordCallbackClass",
    47                 UTPasswordClientCallBack.class.getName());
    48         inProps.put("decryptionPropFile", "ca/client.properties");
    49         inProps.put("signaturePropFile", "ca/client.properties");
    50         factory.getInInterceptors().add(new WSS4JInInterceptor(inProps));
    51         TestWebService webService = (TestWebService) factory.create();
    52         return webService;
    53     }
    54 
    55 }

              6.3、测试    

     1 package cxf.test.client;
     2 
     3 public class TestClient {
     4     // 服务端wsdl地址,包含"?wsdl"后缀,wsdl地址一般放在数据库等地方,方便服务端改动时,客户端好更改.
     5     private static String address = "http://127.0.0.1:7001/cxfserver/services/wstest?wsdl";
     6 
     7     public static void main(String[] args) {
     8         try {
     9             Client client = new Client();
    10             TestWebService webService = client.call(address);
    11             String result = webService.testMethod("客户端调用服务端");
    12             System.out.println(result);
    13         } catch (Exception e) {
    14             e.printStackTrace();
    15         }
    16     }
    17 }
    • 说明

            如果webservice的方法参数和返回值是xml(即将对象编写成xml作为参数传递),可以使用JiBx对对象和xml之间的转换做操作,可以参考下面文章:

                  http://www.ibm.com/developerworks/cn/java/tutorials/j-jibx1/

  • 相关阅读:
    VS2012 for SharePoint2013 Tool安装
    SharePoint 2013网站管理网站策略(关闭和删除策略)
    呼风唤雨的公交
    ASP、Access、80040e14、保留关键字、INSERT INTO 语句的语法错误
    表格边框设置
    DotNet开发中关于SQLServer连接的两种方法之比较
    SQL语句集锦
    动态网页设计笔记
    SQL SERVER中日期 where 问题的解决
    配置你的ASP.NET运行环境
  • 原文地址:https://www.cnblogs.com/yuyuj/p/4526514.html
Copyright © 2020-2023  润新知