在ApacheCXF的Sample里以及网上很多有关RESTful HTTPS双向认证的文章介绍仅仅是理论,没有涉及实际环境的实现(客户端和服务端都是localhost);这几天使用Apache的CXF以及 Apache portable HttpClient实现跨IP的JAXRS HTTPS双向认证实现。
在实践中发现tomcat版本7.0.70和7.0.68在TLS/SSL支持上也存在差异。
一,尝试成功的环境
1,JDK7u71(Server端&服务端);
2,Service端Tomcat7.0.68
3,ServiceSDK:apache-cxf-3.1.6
二,建立自签名服务端和客户端密钥库并都加入对方导出的授权证书
1,密钥及证书生成和导入过程:
(Server端IP:192.168.245.133,Client端IP:192.168.245.1)
密钥库生成:keytool -genkeypair -validity 730 -alias serverkey -keystore serverkeystore.jks -dname "CN=192.168.245.133"
keytool -genkeypair -validity 730 -alias clientkey -keystore clientkeystore.jks -dname "CN=merrick"
客户端密钥库导入服务端证书:
keytool -export -rfc -keystore serverkeystore.jks -alias serverkey -file myserver.cer
keytool -import -noprompt -trustcacerts -file myserver.cer -alias serverkey -keystore clientkeystore.jks
服务端密钥库导入客户端证书:
keytool -export -rfc -keystore clientkeystore.jks -alias clientkey -file myclient.cer
keytool -import -noprompt -keystore serverkeystore.jks -trustcacerts -file myclient.cer -alias clientkey
三,建立JAX-RS CXF服务端(集成Spring)
1,建立Web项目,导入必要的库文件:
commons-logging-1.0.3.jar
cxf-core-3.1.6.jar
cxf-rt-frontend-jaxrs-3.1.6.jar
cxf-rt-rs-json-basic-3.1.6.jar
cxf-rt-transports-http-3.1.6.jar
javax.annotation-api-1.2.jar
javax.servlet-api-3.1.0.jar
javax.ws.rs-api-2.0.1.jar
spring-aop-4.1.9.RELEASE.jar
spring-beans-4.1.9.RELEASE.jar
spring-context-4.1.9.RELEASE.jar
spring-core-4.1.9.RELEASE.jar
spring-expression-4.1.9.RELEASE.jar
spring-web-4.1.9.RELEASE.jar
woodstox-core-asl-4.4.1.jar
xmlschema-core-2.2.1.jar
2,配置web.xml
<span style="font-size:12px;"><?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-app_3_0.xsd" id="WebApp_ID" version="3.0"> <display-name>cxfjaxrsbasicrestserver1</display-name> <context-param> <param-name>contextConfigLocation</param-name> <param-value>WEB-INF/beans.xml</param-value> </context-param> <listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener> <servlet> <servlet-name>CXFServlet</servlet-name> <servlet-class> org.apache.cxf.transport.servlet.CXFServlet </servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>CXFServlet</servlet-name> <url-pattern>/basiccxf/*</url-pattern> </servlet-mapping> <welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file> <welcome-file>default.html</welcome-file> <welcome-file>default.htm</welcome-file> <welcome-file>default.jsp</welcome-file> </welcome-file-list> </web-app></span>
3,配置Spring的beans.xml(路径如上述)
<span style="font-size:12px;"><?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:aop="http://www.springframework.org/schema/aop" xmlns:jaxrs="http://cxf.apache.org/jaxrs" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd"> <import resource="classpath:META-INF/cxf/cxf.xml"/> <import resource="classpath:META-INF/cxf/cxf-servlet.xml"/> <bean id="gameinfoservice" class="cxf.rest.basic.server.GameInfoServiceImpl"/> <jaxrs:server id="gameabc" address="/"> <jaxrs:serviceBeans> <ref bean="gameinfoservice"/> </jaxrs:serviceBeans> </jaxrs:server> </beans></span>
4,建立服务接口、服务实现类、XMLBean
<span style="font-size:12px;">package cxf.rest.basic.server; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; @Path("/gameinfo/") public interface GameInfoService { @GET @Path("/basicinfo/") Response getBasicinfo(@Context HttpServletRequest req); @GET @Path("/basicinfo/{id}/") @Produces(MediaType.APPLICATION_XML) GameEntity getOneGameinfo(@PathParam(value = "id") int id); @GET @Path("/basicinfo2/{id}/") Response getOneGameinfo2(@PathParam("id") String id); @GET @Path("/basicinfo3/") Response getOneGameinfo3(); } </span>
<span style="font-size:12px;">package cxf.rest.basic.server; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement(name="EGame") public class GameEntity { private int id; private String name; private String publishdate; private String producer; private String type; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPublishdate() { return publishdate; } public void setPublishdate(String publishdate) { this.publishdate = publishdate; } public String getProducer() { return producer; } public void setProducer(String producer) { this.producer = producer; } public String getType() { return type; } public void setType(String type) { this.type = type; } } </span>
<span style="font-size:12px;">package cxf.rest.basic.server; import java.util.Date; import java.util.Hashtable; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; public class GameInfoServiceImpl implements GameInfoService { Map<Integer,GameEntity> gms = new Hashtable<Integer,GameEntity>(); public GameInfoServiceImpl() { System.out.println("-----GameInfoServiceImpl constructor "); GameEntity et = new GameEntity(); et.setId(123); et.setName("HEROS"); et.setProducer("3DO ltd."); et.setPublishdate("1995-01-01"); et.setType("Stratigy 回合游戏"); gms.put(123, et); } @Override public Response getBasicinfo(HttpServletRequest req) { System.out.println("-------Invoking "+ this.getClass().getName() + ".getBasicinfo(), "+ req.getServletContext().getServerInfo()); Response r = Response.ok(new String("123"),MediaType.TEXT_PLAIN).build(); return r; } @Override public GameEntity getOneGameinfo(int id) { System.out.println("-------Invoking "+ this.getClass().getName() + ".getOneGameinfo(), " + id); System.out.println(new Date().toString()); GameEntity e = gms.get(123); System.out.println("entity null? " + (e==null)); return e; } @Override public Response getOneGameinfo2(String id) { System.out.println("-------Invoking "+ this.getClass().getName() + ".getOneGameinfo2()"); Response r = Response.ok(gms.get(123),MediaType.APPLICATION_XML_TYPE).build(); return r; } @Override public Response getOneGameinfo3() { System.out.println("-------Invoking "+ this.getClass().getName() + ".getOneGameinfo3()"); GameEntity e = gms.get(123); Response r = Response.ok(gms.get(123),MediaType.APPLICATION_JSON_TYPE).build(); //to be continued..............................//JSON return r; } } </span>
5,RuntimeServer Tomcat7.0.68的Server.xml中配置HTTPS有关的Connector
由于是双向认证,服务端同样需要验证客户端,所以clientAuth需要配置为true
<span style="font-size:12px;"><Connector port="8443" protocol="org.apache.coyote.http11.Http11Protocol" maxThreads="150" SSLEnabled="true" scheme="https" secure="true" clientAuth="true" sslProtocol="TLS" keystoreFile="conf/serverkeystore.jks" keystorePass="passwd" truststoreFile="conf/serverkeystore.jks" truststorePass="passwd" /></span>
四,建立Client端
仅列出无论localhost还是跨IP的情况下都成功的code:
public static void getEntity_51() throws Throwable{//SSL client get /** * Fail!: Javax.net.ssl.sslhandshakeexception: * received fatal alert: handshake_failure(when Server tomcat7.0.70, Server JDK 7u9; Client JDK 7u71) * * Fail!: javax.net.ssl.SSLHandshakeException: * Received fatal alert: handshake_failure(when Server tomcat7.0.70, Server JDK 7u71; Client JDK 7u71) * * Success !(server tomcat7.0.68, JDK 7u71; client JDK 7u71) * * tomcat7.0.70的ssl支持有问题 * * */ DefaultHttpClient httpclient = getHTTPsClient("/clientkeystore.jks","passwd",8443); HttpGet httpget = new HttpGet("https://192.168.245.133:8443/cxfjaxrsbasicrestserver1/basiccxf/gameinfo/basicinfo2/1234/"); // httpget.addHeader(new BasicHeader("Accept" , "text/xml")); httpclient.addRequestInterceptor(new HttpRequestInterceptor() { @Override public void process(HttpRequest arg0, HttpContext arg1) throws HttpException, IOException { Header[] h = arg0.getAllHeaders(); for (int i = 0; i < h.length; i++) { System.out.println("---request header: "+h[i]); } } }); httpclient.addResponseInterceptor(new HttpResponseInterceptor() { @Override public void process(HttpResponse arg0, HttpContext arg1) throws HttpException, IOException { Header[] h = arg0.getAllHeaders(); for (int i = 0; i < h.length; i++) { System.out.println("reponse header: "+h[i]); } } }); HttpResponse response = httpclient.execute(httpget); HttpEntity entity = response.getEntity(); String rss = EntityUtils.toString(entity, "utf-8"); System.out.println(rss);//注意字符编码 XMLparse.parsestringtoxml(rss);//Dom4j API, be used to parse response string to xml document object//DocumentHelper.parseText(s); // entity.writeTo(System.out); httpclient.getConnectionManager().shutdown(); httpclient.close(); }
public static DefaultHttpClient getHTTPsClient(String jksname, String password, int httpsserverport){//success DefaultHttpClient httpclient = null; try { String keyStoreLoc = Client1.class.getClassLoader().getResource("").getPath() + jksname; System.out.println("jks path: " + keyStoreLoc); KeyStore trustStore = KeyStore.getInstance("JKS"); trustStore.load(new FileInputStream(keyStoreLoc), password.toCharArray()); SSLSocketFactory sf = new SSLSocketFactory(trustStore, password, trustStore); Scheme httpsScheme = new Scheme("https", httpsserverport, sf); httpclient = new DefaultHttpClient(); httpclient.getConnectionManager().getSchemeRegistry().register(httpsScheme); } catch (Throwable e) { e.printStackTrace(); } return httpclient; }
测试情况:
jks path: /D:/workspace_ElipseJEE_mars2/cxfjaxrsbasicrestclient1/bin//clientkeystore.jks
---request header: Host: 192.168.245.133:8443
---request header: Connection: Keep-Alive
---request header: User-Agent: Apache-HttpClient/4.5.2 (Java/1.7.0_71)
reponse header: Server: Apache-Coyote/1.1
reponse header: Date: Tue, 18 Oct 2016 05:58:41 GMT
reponse header: Content-Type: application/xml
reponse header: Content-Length: 200
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><EGame><id>123</id><name>HEROS</name><producer>3DO ltd.</producer><publishdate>1995-01-01</publishdate><type>Stratigy 回合游戏</type></EGame>
root name: EGame
Node : id ====> 123
Node : name ====> HEROS
Node : producer ====> 3DO ltd.
Node : publishdate ====> 1995-01-01
Node : type ====> Stratigy 回合游戏
相关推荐
本实践项目主要涉及WebService服务端与客户端的实现,参考了Apache的官方源码,下面将详细介绍相关知识点。 一、WebService基础 1. WSDL(Web Service Description Language):WebService的接口定义语言,用于描述...
JAX-WS专注于SOAP和WSDL的Web服务,而JAX-RS(Java API for RESTful Web Services)则用于构建RESTful服务,它们在服务模型、消息格式和接口设计上有显著区别。 总结,JAX-WS是Java EE中用于构建SOAP Web服务的关键...
2. **CXF框架**:CXF是一个全面的Web服务框架,支持JAX-WS(Java API for XML Web Services)和JAX-RS(Java API for RESTful Web Services)。它允许开发者创建符合W3C标准的SOAP和RESTful服务,同时还提供了多种...
本教程将深入探讨如何使用CXF来实现WebService服务端和客户端,帮助开发者更好地理解和应用这一强大的工具。 一、CXF概述 CXF是一个全方位的Web服务框架,它集成了多种协议和技术,如SOAP、REST、WS-*规范等,提供...
本示例将详细讲解如何使用Spring、Apache CXF和Maven来创建一个基于REST的WebService服务端Demo。 首先,让我们理解这些关键组件的作用: 1. **Spring**:这是一个全面的开发框架,用于构建Java应用程序,特别是...
首先,我们需要理解Java中实现WebService的两种主要技术:JAX-WS(Java API for XML Web Services)和JAX-RS(Java API for RESTful Web Services)。JAX-WS是用于创建SOAP(Simple Object Access Protocol)服务的...
CXF不仅简化了Web服务的开发,而且允许开发者无缝地在客户端和服务端之间切换,从而实现双向通信。 **1. CXF入门** 对于初学者,了解CXF的基本概念是至关重要的。CXF基于JAX-WS(Java API for XML Web Services)...
【SSH REST WS JAX-RS Jersey 小网站后台及Web Service接口详解】 SSH(Spring、Struts、Hibernate)是Java Web开发中常见的三层架构,它整合了Spring的依赖注入、Struts的MVC模式和Hibernate的对象关系映射,提供...
JAX-RS(Java API for RESTful Web Services)是另一种Java Web服务标准,专注于RESTful风格的服务。相比JAX-WS,JAX-RS更简洁,不需要WSDL,直接使用HTTP方法(GET、POST等)和URI来定义资源。JAX-RS通常更适合轻量...
Java WebService CXF客户端调用和服务端的实现是企业级应用程序中常见的通信方式,它基于标准的SOAP(Simple Object Access Protocol)协议,提供了一种在分布式环境中交换信息的方法。CXF是一个开源框架,它简化了...
虽然Spring提供了自己的REST支持,但也可以通过JAX-RS规范(如Jersey或Apache CXF)与Spring集成,提供另一种实现REST的方式。 **JAX-WS(Java API for XML Web Services)**: 主要用于创建基于SOAP的Web服务,...
JAX-RS(Java API for RESTful Web Services)是Java平台中用于开发基于REST风格的WebService的规范。与JAX-WS不同,JAX-RS更加侧重于资源导向的服务设计,它支持多种数据格式,如JSON和XML。 1. **RESTful风格**:...
在Java中,我们通常使用JAX-WS(Java API for XML Web Services)或者JAX-RS(Java API for RESTful Web Services)来实现对WebService的调用。 **1. JAX-WS介绍** JAX-WS是Java中用于创建和消费SOAP(Simple ...
2. **服务端点**:服务端点实现了服务接口,并处理来自客户端的请求。通常使用`@WebService`注解的类作为服务端点。 3. **WSDL(Web Service Description Language)**:JAX-WS自动生成WSDL文件,描述服务的接口、...
1. **客户端支持**:JAX-RS 2.0引入了一个更加强大的客户端API,使得开发者能够更轻松地构建客户端应用程序来调用RESTful服务。 2. **资源类和方法**:JAX-RS 2.0增强了资源类和方法的支持,包括更灵活的路径匹配...
JAX-WS能够通过注解简化Web服务的开发过程,并且支持Web服务的动态客户端编程,即客户端可以利用JAX-WS提供的工具动态生成与服务端通信所需的代理类和相关代码。JAX-WS 2.1版本是JDK 1.6自带的版本,并且底层支持为...
2. **Java JAX-WS**: Java中实现SOAP服务端的API,可以创建WSDL(Web Service Description Language)文件,定义服务接口和数据类型。 3. **Java JAX-RS**: 用于创建RESTful API的Java标准,例如使用Spring Boot,...
它提供了多种方式来创建和消费WebService,包括基于Java API for XML Web Services (JAX-WS) 和Java API for RESTful Web Services (JAX-RS)。CXF不仅支持SOAP协议,还支持RESTful风格的服务,使得开发者可以根据...
在这个项目中,"restful-webService" 提供了一个框架,用于构建RESTful风格的服务,包括客户端和服务端两大部分。这个框架允许开发者专注于业务逻辑,而无需从零开始实现所有基础结构。 1. **RESTful原则**: REST...
2. **Apache CXF**:这是一个开源框架,支持JAX-WS和JAX-RS(RESTful服务)标准,提供丰富的工具和功能来创建和部署WebService服务端。 3. **Spring-WS**:Spring Web Services项目专注于基于契约的WebService开发...