JAX-RS RESTful webservice 服务端及客户端实现(基于HTTPS双向认证)
来源:互联网 发布:日语笔译知乎 编辑:程序博客网 时间:2024/05/18 03:51
在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);}@Overridepublic 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;}@Overridepublic 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;}@Overridepublic 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;}@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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){//successDefaultHttpClient 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 回合游戏
- JAX-RS RESTful webservice 服务端及客户端实现(基于HTTPS双向认证)
- 基于CXF的JAX-WS、JAX-RS(RESTful)的webService
- 基于CXF的JAX-WS、JAX-RS(RESTful)的webService
- 基于CXF的JAX-WS、JAX-RS(RESTful)的webService
- 基于CXF的JAX-WS、JAX-RS(RESTful)的webService
- 创建基于JAX-WS的WebService的服务端及客户端
- webservice之jax-ws服务端及客户端实现
- 基于HTTPS的双向认证实现
- 使用JAX-WS创建webservice服务,含服务端及客户端
- 关于使用RESTful api上传文件,基于jax rs接口,不是实现
- 使用java(JAX-RS) 搭建 RESTful WebService
- 使用cxf、JAX-RS编写restful风格的webservice
- XFire实现WebService服务端及客户端
- Spring Webservice服务端及客户端实现
- PHP webservice 身份认证 客户端与服务端实现
- PHP webservice 身份认证 客户端与服务端实现
- 基于Restful服务端的客户端实现(WebClient 和 HttpURLConnection)
- Myeclipse 基于JAX-WS 的 WebService 服务端和客户端的搭建
- Sqlite 修改表名称、增加字段、查询表结构、修改表结构字段类型
- CENTOS 7 MYSQL YUM安装
- oj第七周训练C
- 一步步实现redis+sentinel双机热备
- js 去掉空格.回车.换行
- JAX-RS RESTful webservice 服务端及客户端实现(基于HTTPS双向认证)
- linux 创建守护进程
- 一篇文读懂19款数据分析软件,解救选择困难症!
- JPA/Hibernate一些链接
- 性能优化攻略
- getParent().requestDisallowInterceptTouchEvent(true)剥夺父view 对touch 事件的处理权
- 查找树——搜索二叉树(非递归)
- LeetCode209
- 2016MDCC移动开发者大会参后感