`

cxf之https双向认证之二

    博客分类:
  • cxf
阅读更多

相关证书制作已经在http://liuwuhen.iteye.com/admin/blogs/1661493中详细的介

绍了,接下来我们需要配置https双向认证的服务端和客户端

服务端

服务端我们需要在服务器中的server.xml文件中添加keystore和truststoreFile文件信息,具体如下:

<Connector port="8443"  address="0.0.0.0" protocol="HTTP/1.1" SSLEnabled="true"
               maxThreads="150" scheme="https" secure="true"
               clientAuth="true" sslProtocol="TLS"  keystoreFile="conf/temip-id.p12"
      keystoreType="PKCS12" keystorePass="changeit" truststoreFile="conf/eomsca-id.p12" truststoreType="PKCS12" truststorePass="changeit"/>

其中clientAuth为true代表双向认证,false表示单向认证,want表示服务端可以验证客户端,也可以不需要验证客户端。


服务端的配置基本完成,启动服务器,这里通过浏览器访问https://localhost:8443/cxf-test/services 应该是不能看到
正确的信息的,因为我们需要在浏览器器中导入客户端证书即(hw_shangcao-id.p12) 即可看到相关正确的wsdl文件。


客户端

关于cxf的https认证方式可以分为编程式和文件式实现。

编程式

1.采用代码的方式实现,编写webserviceUtils工具类,其代码如下:
package com.liuwuhen.cxf;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;

import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;

public  class WebServiceUtils {
 
public static KeyManager[] getKeyManagers() {
  InputStream is = null;
  try {
   // 获取默认的 X509算法
   String alg = KeyManagerFactory.getDefaultAlgorithm();
   // 创建密钥管理工厂
   KeyManagerFactory factory = KeyManagerFactory.getInstance(alg);
   File certFile = new File("F:\\root\\client\\hw_shancao-id.p12");
   if (!certFile.exists() || !certFile.isFile()) {
    return null;
   }
   is = new FileInputStream(certFile);
   // 构建以证书相应格式的证书仓库
   KeyStore ks = KeyStore.getInstance("pkcs12");
   // 加载证书
   ks.load(is, "123456".toCharArray());
   factory.init(ks, "123456".toCharArray());
   KeyManager[] keyms = factory.getKeyManagers();
   return keyms;
  } catch (Exception e) {
   e.printStackTrace();
  } finally {
   if (is != null) {
    try {
     is.close();
    } catch (IOException e) {
     e.printStackTrace();
    }
   }

  }
  return null;
}

public static TrustManager[] getTrustManagers() {
  // 读取证书仓库输入流
  InputStream is = null;
  try {
   // 信任仓库的默认算法X509
   String alg = TrustManagerFactory.getDefaultAlgorithm();
   // 获取信任仓库工厂
   TrustManagerFactory factory = TrustManagerFactory.getInstance(alg);
   // 读取信任仓库
   is = new FileInputStream(new File("F:\\root\\server\\temip-id.jks"));
   // 密钥类型
   KeyStore ks = KeyStore.getInstance("JKS");
   // 加载密钥
   ks.load(is, "changeit".toCharArray());
   factory.init(ks);
   TrustManager[] tms = factory.getTrustManagers();
   return tms;
  } catch (Exception e) {
   e.printStackTrace();

  } finally {
   if (is != null) {
    try {
     is.close();
    } catch (IOException e) {
     e.printStackTrace();
    }
   }
  }
  return null;
}
}

2.编写测试代码,分别设置TrustManagers和KeyManagers其详细代码如下:
public static void main(String[] args) {
  List list = new ArrayList();
  SoapInterceptor saopInterceptor = new SoapInterceptor();
  Map<String, Object> properties = new HashMap<String, Object>();
  properties.put(WSHandlerConstants.ACTION,
    WSHandlerConstants.USERNAME_TOKEN);
  properties.put(WSHandlerConstants.USER, "cxfClient");
  properties.put(WSHandlerConstants.PASSWORD_TYPE, "PasswordText");
  properties.put(WSHandlerConstants.PW_CALLBACK_CLASS,
    ClientCallback.class.getName());
  WSS4JOutInterceptor wss4jOutInterceptor = new WSS4JOutInterceptor(
    properties);
  LoggingOutInterceptor loggingOutInterceptor = new LoggingOutInterceptor();
  SAAJOutInterceptor saajOutInterceptor = new SAAJOutInterceptor();
  list.add(saopInterceptor);
  list.add(wss4jOutInterceptor);
  list.add(saajOutInterceptor);
  list.add(loggingOutInterceptor);
  JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
  // 注册WebService接口
  factory.setServiceClass(IHelloWorld.class);
  // webservice请求地址
  String wsdlAdder = "https://localhost:8443/cxf-test/services/sayHello";
  // 发布接口
  factory.setAddress(wsdlAdder);
  factory.setOutInterceptors(list);
  IHelloWorld helloWorld = (IHelloWorld) factory.create();
  Client proxy = ClientProxy.getClient(helloWorld);
  HTTPConduit conduit = (HTTPConduit) proxy.getConduit();
  TLSClientParameters tlsParams = conduit.getTlsClientParameters();
  if (tlsParams == null) {
   tlsParams = new TLSClientParameters();
  }
  tlsParams.setSecureSocketProtocol("SSL");
  //设置keystore
  tlsParams.setKeyManagers(WebServiceUtils.getKeyManagers());
  // 设置信任证书
  tlsParams.setTrustManagers(WebServiceUtils.getTrustManagers());
  conduit.setTlsClientParameters(tlsParams);
  helloWorld.sayHello("cxf hello");
}

运行其客户端代码即可输出 say hello 。


声明式


由于cxf与spring能很好的结合,所以可以通过spring配置文件的方式进行客户端配置。
<?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"
xmlns:soap="http://cxf.apache.org/bindings/soap" xmlns:context="http://www.springframework.org/schema/context"
xmlns:sec="http://cxf.apache.org/configuration/security" xmlns:http="http://cxf.apache.org/transports/http/configuration"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://cxf.apache.org/bindings/soap
http://cxf.apache.org/schemas/configuration/soap.xsd
http://cxf.apache.org/jaxws
http://cxf.apache.org/schemas/jaxws.xsd
http://cxf.apache.org/configuration/security
http://cxf.apache.org/schemas/configuration/security.xsd
http://cxf.apache.org/transports/http/configuration
http://cxf.apache.org/schemas/configuration/http-conf.xsd">

<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:client id="test"
  serviceClass="com.shanchao.test"
  address="https://localhost:8443/cxf-test/services/sayHello" />

    <!-- *代码所的客户端可以访问 -->
<http:conduit name="*.http-conduit">
  <http:tlsClientParameters disableCNCheck="true">
   <sec:trustManagers>
    <sec:keyStore type="JKS" password="changeit" file="F:/root/server/temip-id.jks" />
   </sec:trustManagers>
   <!--双向认证 -->
   <sec:keyManagers keyPassword="changeit">
    <sec:keyStore type="PKCS12" password="changeit" file="F:/root/client/hw_shancao-id.p12" />
   </sec:keyManagers>

   <sec:cipherSuitesFilter>
    <!-- these filters ensure that a ciphersuite with export-suitable or
     null encryption is used, but exclude anonymous Diffie-Hellman key change
     as this is vulnerable to man-in-the-middle attacks -->
    <sec:include>.*_EXPORT_.*</sec:include>
    <sec:include>.*_EXPORT1024_.*</sec:include>
    <sec:include>.*_WITH_DES_.*</sec:include>
    <sec:include>.*_WITH_NULL_.*</sec:include>
    <sec:exclude>.*_DH_anon_.*</sec:exclude>
   </sec:cipherSuitesFilter>
  </http:tlsClientParameters>
</http:conduit>
</beans>


特别说明:<http:conduit name="*.http-conduit"> name为*配置的是代表客户端能够访问服务端发布的所有服

务。如果在一个系统中既包含http与https的webservice服务,那么配置为*,则会出现错误。因为*代表的是所有的

webservice服务都进行了上面的配置。 那么我们可以采用https://ip:port/.* 的方式进行过滤,或者采用

{WSDL_Namespace}portName.http-conduit 方式进行过滤。

<wsdl:service name="HelloWorldImplService">

     <wsdl:port binding="tns:HelloWorldImplServiceSoapBinding" name="HelloWorldImplPort">
             <soap:address location="https://localhost:8443/cxf-test/services/sayHello" />
     </wsdl:port>
</wsdl:service>

例如本例中生成的WSDL中portaName为HelloWorldImplPort ,那么可以采用{http://cxf.liuwuhen.com/}

HelloWorldImplPort.http-conduit即可。

这两种方式的优点和缺点是:声明式所采用的代码比较简洁,代码式虽然代码比较的复杂,但是灵活性比较的强,如果我

们所产生的地址为动态的,例如是通过页面配置,存入到数据库中的这种方式,那么只能采用编程式。

关于cxf的相关认证已经介绍完成,由于时间冲忙,如有不对地方,请多多指正,谢谢!

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics