转载

Jersey框架

编辑推荐:
本文来自于cnblogs,主要从Jersey RESTful WebService框架简介,Jersey对JSON的支持,Jersey对HTTPS的支持等方面详细讲解。

Jersey RESTful WebService框架简介

开发RESTful WebService意味着支持在多种媒体类型以及抽象底层的客户端-服务器通信细节,如果没有一个好的工具包可用,这将是一个困难的任务

为了简化使用Java开发RESTful WebService及其客户端,一个轻量级的标准被提出:JAX-RS API

Jersey RESTful WebService框架是一个开源的、产品级别的JAVA框架,支持JAX-RS API并且是一个JAX-RS(JSR 311和 JSR 339)的参考实现

Jersey不仅仅是一个JAX-RS的参考实现,Jersey提供自己的API,其API继承自JAX-RS,提供更多的特性和功能以进一步简化RESTful service和客户端的开发

Maven版本:3.1.0

Jersey版本:1.18

JDK版本:1.7.0_65

一,服务端

Maven配置如下:

 <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>JERSEY_SERVER</groupId> 
 <artifactId>JERSEY_SERVER</artifactId> 
 <version>1.0</version> 
 <dependencies> 
 <dependency> 
 <groupId>com.sun.jersey</groupId> 
 <artifactId>jersey-server</artifactId> 
 <version>1.18</version> 
 </dependency> 
 <dependency> 
 <groupId>com.sun.jersey</groupId> 
 <artifactId>jersey-grizzly2</artifactId> 
 <version>1.18</version> 
 </dependency> 
 </dependencies> 
 </project>

首先介绍几个注解:

@Path

用来为资源类或方法定义URI,当然除了静态URI也支持动态URI

 @Path("service") 
 public class MyResource { 
 @Path("{sub_path}") 
 @GET 
 public String getResource(@PathParam("sub_path") String resourceName) { 
 ...... 
 如果此时客户端请求的U

如果此时客户端请求的URI为http://127.0.0.1:10000/service/sean,则sub_path的值为sean

@PathParam用来将请求URI的一部分作为方法参数传入方法中

对URI的动态部分,可以自定义校验正则表达式,如果请求参数校验失败,容器返回404 Not Found

@Path("{sub_path:[A-Z]*}")

表明被注解的方法响应HTTP GET请求,@POST、@PUT和@DELETE同理

@Consumes

定义请求的媒体类型,如果不指定,则容器默认可接受任意媒体类型,容器负责确认被调用的方法可接受HTTP请求的媒体类型,否则返回415 Unsupported Media Type

方法级注解将覆盖类级注解

@Produces

定义响应媒体类型,如果不指定,则容器默认可接受任意媒体类型,容器负责确认被调用的方法可返回HTTP请求可以接受媒体类型,否则返回406 Not Acceptable

方法级注解将覆盖类级注解

@QueryParam

 public String getResource( 
 @DefaultValue("Just a test!") @QueryParam("desc") String description) { 
 ...... 
 } 

如果请求URI中包含desc参数,例如:http://127.0.0.1:10000/service/sean?desc=123456,则desc参数的值将会赋给方法的参数description,否则方法参数description的值将为@DefaultValue注解定义的默认值

@Context

将信息注入请求或响应相关的类,可注入的类有:Application,UriInfo,Request,HttpHeaders和SecurityContext

@Singleton和@PerRequest

默认情况下,资源类的生命周期是per-request,也就是系统会为每个匹配资源类URI的请求创建一个实例,这样的效率很低,可以对资源类使用@Singleton注解,这样在应用范围内,只会创建资源类的一个实例

服务端程序如下:

 package com.sean; 
 
 import java.io.IOException; 
 import java.net.URI; 
 import java.util.Iterator; 
 
 import javax.ws.rs.Consumes; 
 import javax.ws.rs.DefaultValue; 
 import javax.ws.rs.GET; 
 import javax.ws.rs.Path; 
 import javax.ws.rs.PathParam; 
 import javax.ws.rs.Produces; 
 import javax.ws.rs.QueryParam; 
 import javax.ws.rs.core.Context; 
 import javax.ws.rs.core.HttpHeaders; 
 import javax.ws.rs.core.MediaType; 
 import javax.ws.rs.core.MultivaluedMap; 
 import javax.ws.rs.core.Request; 
 import javax.ws.rs.core.UriBuilder; 
 import javax.ws.rs.core.UriInfo; 
 
 import org.glassfish.grizzly.http.server.HttpServer; 
 
 import com.sun.jersey.api .container.grizzly2.GrizzlyServerFactory; 
 import com.sun.jersey.api .core.PackagesResourceConfig; 
 import com.sun.jersey.api.core.ResourceConfig; 
 import com.sun.jersey.spi.resource.Singleton; 
 
 @Singleton 
 @Path("service") 
 public class MyResource { 
 
 @Path("{sub_path:[a-zA-Z0-9]*}") 
 @GET 
 @Consumes ({MediaType.TEXT_PLAIN, MediaType.APPLICATION_JSON}) 
 @Produces (MediaType.TEXT_PLAIN) 
 public String getResourceName( 
 @PathParam ("sub_path") String resourceName, 
 @DefaultValue ("Just a test!") @QueryParam("desc") String description, 
 @Context Request request, 
 @Context UriInfo uriInfo, 
 @Context HttpHeaders httpHeader) { 
 System.out.println (this.hashCode()); 
 
 // 将HTTP请求打印出来 
 System.out.println ("****** HTTP request ******"); 
 StringBuilder strBuilder = new StringBuilder(); 
 strBuilder.append (request.getMethod() + " "); 
 strBuilder.append(uriInfo.getRequestUri().toString() + " "); 
 strBuilder.append("HTTP/1.1[//r//n]"); 
 System.out.println (strBuilder.toString()); 
 MultivaluedMap<String, String> headers = httpHeader.getRequestHeaders(); 
 Iterator<String> iterator = headers.keySet().iterator(); 
 while(iterator.hasNext()){ 
 String headName = iterator.next(); 
 System.out.println (headName + ":" + headers.get(headName) + "[//r//n]"); 
 } 
 System.out.println ("[//r//n]"); 
 String responseStr =resourceName + "[" + description + "]"; 
 return responseStr; 
 } 
 
 public static void main (String[] args) { 
 URI uri = UriBuilder.fromUri ("http://127.0.0.1").port (10000).build(); 
 ResourceConfig rc = new PackagesResourceConfig ("com.sean"); 
 try { 
 HttpServer server = GrizzlyServerFactory.createHttpServer (uri, rc); 
 server.start(); 
 } catch (IllegalArgumentException e) { 
 e.printStackTrace(); 
 } catch (NullPointerException e) { 
 e.printStackTrace(); 
 } catch (IOException e) { 
 e.printStackTrace(); 
 } 
 try { 
 Thread.sleep(1000*1000); 
 } catch (InterruptedException e) { 
 e.printStackTrace(); 
 } 
 } 
 }

二,客户端

Maven配置如下:

 <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>JERSEY_CLIENT</groupId> 
 <artifactId>JERSEY_CLIENT</artifactId> 
 <version>1.0</version> 
 <dependencies> 
 <dependency> 
 <groupId>com.sun.jersey</groupId> 
 <artifactId>jersey-client</artifactId> 
 <version>1.18</version> 
 </dependency> 
 <dependency> 
 <groupId>com.sun.jersey</groupId> 
 <artifactId>jersey-grizzly2</artifactId> 
 <version>1.18</version> 
 </dependency> 
 </dependencies> 
 </project>

客户端程序如下:

 package com.sean; 
 
 import java.net.URI; 
 import java.util.Iterator; 
 
 import javax.ws.rs.core.MediaType; 
 import javax.ws.rs.core.MultivaluedMap; 
 import javax.ws.rs.core.UriBuilder; 
 
 import com.sun.jersey.api.client.Client; 
 import com.sun.jersey.api.client.ClientResponse; 
 import com.sun.jersey.api.client.WebResource; 
 import com.sun.jersey.api.client.config.ClientConfig; 
 import com.sun.jersey.api.client.config.DefaultClientConfig; 
 
 public class JerseyClient { 
 
 public static void main(String[] args) { 
 // 要使用Jersey Client API,必须首先创建Client的实例 
 // 有以下两种创建Client实例的方式 
 
 // 方式一 
 ClientConfig cc = new DefaultClientConfig(); 
 cc.getProperties().put(ClientConfig.PROPERTY_CONNECT_TIMEOUT, 10*1000); 
 // Client实例很消耗系统资源,需要重用 
 // 创建web资源,创建请求,接受响应都是线程安全的 
 // 所以Client实例和WebResource实例可以在多个线程间安全的共享 
 Client client = Client.create(cc); 
 
 // 方式二 
 // Client client = Client.create(); 
 // client.setConnectTimeout(10*1000); 
 // client.getProperties().put (ClientConfig.PROPERTY _CONNECT_TIMEOUT, 10*1000); 
 
 // WebResource将会继承Client中timeout的配置 
 WebResource resource = client.resource("http://127.0.0.1:10000/ service/sean?desc =description"); 
 
 String str = resource 
 .accept(MediaType.TEXT_PLAIN) 
 .type(MediaType.TEXT_PLAIN) 
 .get(String.class); 
 System.out.println("String:" + str); 
 
 URI uri = UriBuilder.fromUri ("http://127.0.0.1/service/sean" ).port(10000) 
 .queryParam ("desc", "description").build(); 
 resource = client.resource(uri); 
 
 //header方法可用来添加HTTP头 
 ClientResponse response = resource.header("auth", "123456") 
 .accept(MediaType.TEXT_PLAIN) 
 .type(MediaType.TEXT_PLAIN) 
 .get(ClientResponse.class); 
 // 将HTTP响应打印出来 
 System.out.println ("****** HTTP response ******"); 
 StringBuilder strBuilder = new StringBuilder(); 
 strBuilder.append ("HTTP/1.1 "); 
 strBuilder.append (response.getStatus() + " "); 
 strBuilder.append (response.getStatusInfo() + "[//r//n]"); 
 System.out.println (strBuilder.toString()); 
 MultivaluedMap<String, String> headers = response.getHeaders(); 
 Iterator<String> iterator = headers.keySet().iterator(); 
 while (iterator.hasNext()){ 
 String headName = iterator.next(); 
 System.out.println (headName + ":" + headers.get(headName) + "[//r//n]"); 
 } 
 System.out.println ("[//r//n]"); 
 System.out.println (response.getEntity(String.class) + "[//r//n]"); 
 } 
 }

服务端日志如下:

 二月 06, 2015 4:33:33 下午 com.sun.jersey.api.core.PackagesResourceConfig init 
 INFO: Scanning for root resource and provider classes in the packages: 
 com.sean 
 二月 06, 2015 4:33:33 下午 com.sun.jersey.api.core.ScanningResourceConfig logClasses 
 INFO: Root resource classes found: 
 class com.sean.Test 
 class com.sean.MyResource 
 二月 06, 2015 4:33:33 下午 com.sun.jersey.api.core.ScanningResourceConfig init 
 INFO: No provider classes found. 
 二月 06, 2015 4:33:33 下午 com.sun.jersey.server.impl.application.WebApplicationImpl _initiate 
 INFO: Initiating Jersey application, version 'Jersey: 1.18 11/22/2013 01:21 AM' 
 二月 06, 2015 4:33:34 下午 org.glassfish.grizzly.http.server.NetworkListener start 
 INFO: Started listener bound to [127.0.0.1:10000] 
 二月 06, 2015 4:33:34 下午 org.glassfish.grizzly.http.server.HttpServer start 
 INFO: [HttpServer] Started. 
 
 ****** HTTP request ****** 
 GET http://127.0.0.1:10000/service/sean?desc =description HTTP/1.1[/r/n] 
 accept:[text/plain][/r/n] 
 content-type:[text/plain][/r/n] 
 user-agent:[Java/1.7.0_65][/r/n] 
 host:[127.0.0.1:10000][/r/n] 
 connection:[keep-alive][/r/n] 
 [/r/n] 
 
 ****** HTTP request ****** 
 GET http://127.0.0.1:10000/service/sean?desc =description HTTP/1.1[/r/n] 
 auth:[123456][/r/n] 
 accept:[text/plain][/r/n] 
 content-type:[text/plain][/r/n] 
 user-agent:[Java/1.7.0_65][/r/n] 
 host:[127.0.0.1:10000][/r/n] 
 connection:[keep-alive][/r/n] 
 [/r/n]

客户端日志如下:

 String:sean[description] 
 ****** HTTP response ****** 
 HTTP/1.1 200 OK[/r/n] 
 Transfer-Encoding:[chunked][/r/n] 
 Date:[Fri, 06 Feb 2015 08:33:38 GMT][/r/n] 
 Content-Type:[text/plain][/r/n] 
 [/r/n] 
 sean[description][/r/n]

Jersey对JSON的支持

Jersey提供3种基本方式来使用JSON格式

无论使用何种方式,在原有包的基础上,都需要在客户端和服务端Maven配置文件中添加jersey-json包以支持JSON格式

 <dependency> 
 <groupId>com.sun.jersey</groupId> 
 <artifactId>jersey-json</artifactId> 
 <version>1.18</version> 
 </dependency>

一,基于POJO

Request类和Response类(服务端和客户端都需要)都是基本的POJO:

 package com.sean; 
 
 public class Request { 
 private String query; 
 
 public String getQuery() { 
 return query; 
 } 
 
 public void setQuery(String query) { 
 this.query = query; 
 } 
 }
 package com.sean; 
 
 public class Response { 
 private int respCode; 
 private String respDesc; 
 
 public int getRespCode() { 
 return respCode; 
 } 
 
 public void setRespCode(int respCode) { 
 this.respCode = respCode; 
 } 
 
 public String getRespDesc() { 
 return respDesc; 
 } 
 
 public void setRespDesc(String respDesc) { 
 this.respDesc = respDesc; 
 } 
 }

服务端代码:

 package com.sean; 
 
 import java.io.IOException; 
 import java.net.URI; 
 
 import javax.ws.rs.Consumes; 
 import javax.ws.rs.POST; 
 import javax.ws.rs.Path; 
 import javax.ws.rs.Produces; 
 import javax.ws.rs.core.MediaType; 
 import javax.ws.rs.core.UriBuilder; 
 
 import org.glassfish.grizzly.http.server.HttpServer; 
 
 import com.sun.jersey.api.container .grizzly2.GrizzlyServerFactory; 
 import com.sun.jersey.api.core .PackagesResourceConfig; 
 import com.sun.jersey.api.core.ResourceConfig; 
 import com.sun.jersey .api.json.JSONConfiguration; 
 
 @Path("query") 
 public class MyResource { 
 
 @POST 
 @Consumes(MediaType.APPLICATION_JSON) 
 @Produces(MediaType.APPLICATION_JSON) 
 public Response query(Request req) { 
 System.out.println(req.getQuery()); 
 
 Response resp = new Response(); 
 resp.setRespCode(0); 
 resp.setRespDesc(req.getQuery()); 
 return resp; 
 } 
 
 public static void main (String[] args) { 
 URI uri = UriBuilder.fromUri ("http://127.0.0.1" ).port(10000).build(); 
 ResourceConfig rc = new PackagesResourceConfig ("com.sean"); 
 //使用Jersey对POJO的支持,必须设置为true 
 rc.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, true); 
 try { 
 HttpServer server = GrizzlyServerFactory.createHttpServer(uri, rc); 
 server.start(); 
 } catch (IllegalArgumentException e) { 
 e.printStackTrace(); 
 } catch (NullPointerException e) { 
 e.printStackTrace(); 
 } catch (IOException e) { 
 e.printStackTrace(); 
 } 
 try { 
 Thread.sleep(1000*1000); 
 } catch (InterruptedException e) { 
 e.printStackTrace(); 
 } 
 } 
 }

客户端代码:

 package com.sean; 
 
 import javax.ws.rs.core.MediaType; 
 
 import com.sun.jersey.api.client.Client; 
 import com.sun.jersey.api.client.ClientResponse; 
 import com.sun.jersey.api.client.WebResource; 
 import com.sun.jersey.api.client.config.ClientConfig; 
 import com.sun.jersey.api.client.config.DefaultClientConfig; 
 import com.sun.jersey.api.json.JSONConfiguration; 
 
 public class JerseyClient { 
 
 public static void main (String[] args) { 
 ClientConfig cc = new DefaultClientConfig(); 
 //使用Jersey对POJO的支持,必须设置为true 
 cc.getFeatures().put (JSONConfiguration.FEATURE _POJO _MAPPING, Boolean.TRUE); 
 Client client = Client.create(cc); 
 
 WebResource resource = client.resource ("http://127.0.0.1:10000/query"); 
 
 Request req = new Request(); 
 req.setQuery ("name"); 
 
 ClientResponse response = resource 
 .accept (MediaType.APPLICATION_JSON) 
 .type (MediaType.APPLICATION_JSON) 
 .post (ClientResponse.class, req); 
 
 Response resp = response.getEntity(Response.class); 
 System.out.println (resp.getRespCode() + " " + resp.getRespDesc()); 
 } 
 }

二,基于JAXB

使用JAXB的优点在于,无论使用XML格式还是JSON格式数据,都可以使用统一的Java模型

缺点很难找到一个合适的方式来生成特殊的JSON格式,这也是Jersey提供很多控制选项的原因

将Request类和Response类进行修改:

 package com.sean; 
 
 import javax.xml.bind.annotation.XmlRootElement; 
 
 @XmlRootElement 
 public class Request { 
 private String query; 
 
 public String getQuery() { 
 return query; 
 } 
 
 public void setQuery(String query) { 
 this.query = query; 
 } 
 }
 package com.sean; 
 
 import javax.xml.bind.annotation.XmlRootElement; 
 
 @XmlRootElement 
 public class Response { 
 private int respCode; 
 private String respDesc; 
 
 public int getRespCode() { 
 return respCode; 
 } 
 
 public void setRespCode(int respCode) { 
 this.respCode = respCode; 
 } 
 
 public String getRespDesc() { 
 return respDesc; 
 } 
 
 public void setRespDesc(String respDesc) { 
 this.respDesc = respDesc; 
 } 
 }

服务端代码去掉下面的配置

// rc.getFeatures().put (JSONConfiguration.FEATURE _POJO_MAPPING, true);

客户端代码去掉下面的配置

// cc.getFeatures().put (JSONConfiguration.FEATURE _POJO_MAPPING, Boolean.TRUE);

Jersey提供很多控制选项以便更精细的控制JSON的解析、组装过程,但是就我个人来看,JAXB提供的标签足够使用了

三,基于底层JSONObject/JSONArray

最大的优势在于可以完全控制JSON的解析、组装过程,相应的,在处理数据对象时也要更复杂

服务端代码如下:

 package com.sean; 
 
 import java.io.IOException; 
 import java.net.URI; 
 
 import javax.ws.rs.Consumes; 
 import javax.ws.rs.POST; 
 import javax.ws.rs.Path; 
 import javax.ws.rs.Produces; 
 import javax.ws.rs.core.MediaType; 
 import javax.ws.rs.core.UriBuilder; 
 
 import org.codehaus.jettison.json.JSONException; 
 import org.codehaus.jettison.json.JSONObject; 
 import org.glassfish.grizzly.http.server.HttpServer; 
 
 import com.sun.jersey.api.container.grizzly2 .GrizzlyServerFactory; 
 import com.sun.jersey.api.core .PackagesResourceConfig; 
 import com.sun.jersey.api.core.ResourceConfig; 
 
 @Path("query") 
 public class MyResource { 
 
 @POST 
 @Consumes (MediaType.APPLICATION_JSON) 
 @Produce s(MediaType.APPLICATION_JSON) 
 public JSONObject query (JSONObject query) { 
 //{"query":"name"} 
 System.out.println (query.toString()); 
 
 JSONObject resp = new JSONObject(); 
 try { 
 resp.put("respCode", 0); 
 resp.put("respDesc", query.get("query")); 
 } catch (JSONException e) { 
 e.printStackTrace(); 
 } 
 return resp; 
 } 
 
 public static void main (String[] args) { 
 URI uri = UriBuilder.fromUri ("http://127.0.0.1" ).port(10000).build(); 
 ResourceConfig rc = new PackagesResourceConfig ("com.sean"); 
 try { 
 HttpServer server = GrizzlyServerFactory.createHttpServer (uri, rc); 
 server.start(); 
 } catch (IllegalArgumentException e) { 
 e.printStackTrace(); 
 } catch (NullPointerException e) { 
 e.printStackTrace(); 
 } catch (IOException e) { 
 e.printStackTrace(); 
 } 
 try { 
 Thread.sleep(1000*1000); 
 } catch (InterruptedException e) { 
 e.printStackTrace(); 
 } 
 } 
 }

客户端代码如下:

 package com.sean; 
 
 import javax.ws.rs.core.MediaType; 
 
 import org.codehaus.jettison.json.JSONException; 
 import org.codehaus.jettison.json.JSONObject; 
 
 import com.sun.jersey.api.client.Client; 
 import com.sun.jersey.api.client.ClientResponse; 
 import com.sun.jersey.api.client.WebResource; 
 import com.sun.jersey.api.client.config.ClientConfig; 
 import com.sun.jersey.api. client.config.DefaultClientConfig; 
 
 public class JerseyClient { 
 
 public static void main(String[] args) { 
 ClientConfig cc = new DefaultClientConfig(); 
 Client client = Client.create(cc); 
 
 WebResource resource = client.resource ("http://127.0.0.1:10000/query"); 
 
 JSONObject req = new JSONObject(); 
 try { 
 req.put ("query", "name"); 
 } catch (JSONException e) { 
 e.printStackTrace(); 
 } 
 
 ClientResponse response = resource 
 .accept (MediaType.APPLICATION_JSON) 
 .type (MediaType.APPLICATION_JSON) 
 .post (ClientResponse.class, req); 
 
 JSONObject resp = response.getEntity(JSONObject.class); 
 //{"respCode":0, "respDesc": "name"} 
 System.out.println(resp.toString()); 
 } 
 }

与JAXB相比,结果是相同的,但是处理过程(主要是组装JSON对象)要复杂

对于上面3种方式,均可使用String类代替Request类、Response类或JSONObject类,Jersey会自动将对象转换为JSON串

当然,如果客户端修改为String,服务端也要相应的修改为String类型

修改客户端代码:

 public class JerseyClient { 
 
 public static void main(String[] args) { 
 ClientConfig cc = new DefaultClientConfig(); 
 Client client = Client.create(cc); 
 
 WebResource resource = client.resource ("http://127.0.0.1:10000/query"); 
 
 JSONObject req = new JSONObject(); 
 try { 
 req.put("query", "name"); 
 } catch (JSONException e) { 
 e.printStackTrace(); 
 } 
 
 String response = resource 
 .accept(MediaType.APPLICATION_JSON) 
 .type(MediaType.APPLICATION_JSON) 
 .post(String.class, req.toString()); 
 } 
 }

Jersey对HTTPS的支持

证书的生成过程这里就不介绍了

代码结构如下:

Jersey框架

Maven配置文件:

 <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> JERSEY</groupId> 
 <artifactId> JERSEY</artifactId> 
 <version>1.0< /version> 
 <dependencies> 
 <dependency> 
 <groupId>com.sun.jersey</groupId> 
 <artifactId> jersey-client</artifactId> 
 <version> 1.18</version> 
 </dependency> 
 <dependency> 
 <groupId> com.sun.jersey</groupId> 
 <artifactId> jersey-grizzly2</artifactId> 
 <version>1.18</version> 
 </dependency> 
 <dependency> 
 <groupId>com.sun.jersey</groupId> 
 <artifactId>jersey-json</artifactId> 
 <version>1.18</version> 
 </dependency> 
 <dependency> 
 <groupId>org.glassfish.jersey.media </groupId> 
 <artifactId> jersey-media-json-jackson</artifactId> 
 <version> 2.15</version> 
 </dependency> 
 </dependencies> 
 </project>

Person类是基本的JAXB:

 package com.sean; 
 
 import java.util.List; 
 
 import javax.xml.bind.annotation.XmlRootElement; 
 
 @XmlRootElement 
 public class Person { 
 private String name; 
 private List<String> addresses; 
 
 public Person(){} 
 
 public String getName() { 
 return name; 
 } 
 
 public void setName(String name) { 
 this.name = name; 
 } 
 
 public List<String> getAddresses() { 
 return addresses; 
 } 
 
 public void setAddresses(List<String> addresses) { 
 this.addresses = addresses; 
 } 
 }

客户端代码:

 package com.sean; 
 
 import java.net.URI; 
 
 import javax.net.ssl.SSLContext; 
 import javax.ws.rs.core.MediaType; 
 import javax.ws.rs.core.UriBuilder; 
 
 import org.glassfish.jersey.SslConfigurator; 
 
 import com.sun.jersey.api.client.Client; 
 import com.sun.jersey.api.client.ClientResponse; 
 import com.sun.jersey.api.client.WebResource; 
 import com.sun.jersey.api.client .config.ClientConfig; 
 import com.sun.jersey.api.client .config.DefaultClientConfig; 
 import com.sun.jersey.client .urlconnection.HTTPSProperties; 
 
 public class SSLClient { 
 public static void main(String[] args) { 
 int authType = 
 Integer.valueOf (Config.getConfig().getProperty ("authority")).intValue(); 
 
 SslConfigurator sslConfig = SslConfigurator.newInstance(); 
 if(authType == 1){ 
 sslConfig.trustStoreFile (Config.getConfig().getProperty ("clientTrustCer")) 
 .trustStorePassword (Config.getConfig().getProperty ("clientTrustCerPwd")); 
 }else if(authType == 2){ 
 sslConfig.keyStoreFile (Config.getConfig().getProperty ("clientCer")) 
 .keyStorePassword (Config.getConfig().getProperty ("clientCerPwd")) 
 .keyPassword (Config.getConfig().getProperty ("clientKeyPwd")) 
 .trustStoreFile (Config.getConfig().getProperty ("clientTrustCer")) 
 .trustStorePassword (Config.getConfig().getProperty ("clientTrustCerPwd")); 
 } 
 sslConfig.securityProtocol (Config.getConfig().getProperty ("protocol")); 
 SSLContext sslContext = sslConfig.createSSLContext(); 
 
 ClientConfig cc = new DefaultClientConfig(); 
 cc.getProperties().put (HTTPSProperties.PROPERTY _HTTPS_PROPERTIES, 
 new HTTPSProperties (new MyHostnameVerifier(), sslContext)); 
 Client client = Client.create(cc); 
 
 URI uri = UriBuilder.fromUri ("https://127.0.0.1/ queryAddress").port(10000).build(); 
 WebResource resource = client.resource(uri); 
 
 Person person = new Person(); 
 person.setName ("sean"); 
 
 ClientResponse response = resource 
 .accept (MediaType.APPLICATION_XML) 
 .type (MediaType.APPLICATION_XML) 
 .post (ClientResponse.class, person); 
 
 String addresses = response.getEntity(String.class); 
 System.out.println (addresses); 
 } 
 }

SSL握手过程中,会对请求IP或请求域名进行校验,如果在证书信息中无法找到相关请求IP或请求域名则会报错(javax.NET.ssl.SSLHandshakeException: Java.security.cert.CertificateException: No subject alternative names present)

这里实现自己的校验逻辑(如果请求的IP为127.0.0.1或请求的域名为localhost,则直接通过校验)以覆盖默认逻辑

 package com.sean; 
 
 import javax.net.ssl.HostnameVerifier; 
 import javax.net.ssl.SSLSession; 
 
 public class MyHostnameVerifier implements HostnameVerifier { 
 
 @Override 
 public boolean verify(String hostname, SSLSession session) { 
 if("127.0.0.1".equals(hostname) || "localhost".equals(hostname) ) 
 return true; 
 else 
 return false; 
 } 
 }

服务端代码:

 package com.sean; 
 
 import java.io.IOException; 
 import java.net.URI; 
 import java.util.ArrayList; 
 import java.util.List; 
 
 import javax.net.ssl.SSLContext; 
 import javax.ws.rs.Consumes; 
 import javax.ws.rs.POST; 
 import javax.ws.rs.Path; 
 import javax.ws.rs.Produces; 
 import javax.ws.rs.core.MediaType; 
 import javax.ws.rs.core.UriBuilder; 
 
 import org.glassfish.grizzly.http.server.HttpHandler; 
 import org.glassfish.grizzly.http.server.HttpServer; 
 import org.glassfish.grizzly.ssl.SSLEngineConfigurator; 
 import org.glassfish.jersey.SslConfigurator; 
 
 import com.sun.jersey.api.container .ContainerFactory; 
 import com.sun.jersey.api.container.grizzly2 .GrizzlyServerFactory; 
 import com.sun.jersey.api.core.PackagesResourceConfig; 
 import com.sun.jersey.api.core.ResourceConfig; 
 
 @Path("queryAddress") 
 public class SSLServer { 
 
 @POST 
 @Consumes(MediaType.APPLICATION_XML) 
 @Produces(MediaType.APPLICATION_XML) 
 public Person queryAddress(String name) { 
 System.out.println(name); 
 
 Person person = new Person(); 
 List<String> addresses = new ArrayList<String>(); 
 addresses.add("address1"); 
 addresses.add("address2"); 
 person.setAddresses(addresses); 
 return person; 
 } 
 
 public static void main(String[] args) { 
 Integer authType = 
 Integer.valueOf (Config.getConfig().getProperty ("authority")).intValue(); 
 
 SslConfigurator sslConfig = SslConfigurator.newInstance(); 
 if(authType == 1){ 
 sslConfig.keyStoreFile (Config.getConfig().getProperty ("serverCer")) 
 .keyStorePassword (Config.getConfig().getProperty ("serverCerPwd")) 
 .keyPassword (Config.getConfig().getProperty ("serverKeyPwd")); 
 }else if(authType == 2){ 
 sslConfig.keyStoreFil e (Config.getConfig().getProperty ("serverCer")) 
 .keyStorePassword (Config.getConfig().getProperty ("serverCerPwd")) 
 .keyPassword (Config.getConfig().getProperty ("serverKeyPwd")) 
 .trustStoreFile (Config.getConfig().getProperty ("serverTrustCer")) 
 .trustStorePassword (Config.getConfig().getProperty ("serverTrustCerPwd")); 
 } 
 sslConfig.securityProtocol (Config.getConfig().getProperty ("protocol")); 
 SSLContext sslContext = sslConfig.createSSLContext(); 
 
 SSLEngineConfigurator sslEngineConfig = new SSLEngineConfigurator (sslContext); 
 //默认情况下是客户端模式,如果忘记修改模式 
 //会抛出异常 
 //javax.net.ssl.SSLProtocolException: Handshake message sequence violation, 1] 
 sslEngineConfig.setClientMode (false); 
 if(authType == 1) 
 sslEngineConfig .setWantClientAuth(true); 
 else if (authType == 2) 
 sslEngineConfig.setNeedClientAuth (true); 
 
 ResourceConfig rc = new PackagesResourceConfig ("com.sean"); 
 HttpHandler handler = ContainerFactory.createContainer( 
 HttpHandler.class, rc); 
 
 URI uri = UriBuilder.fromUri ("https://127.0.0.1/") .port(10000).build(); 
 try { 
 HttpServer server = GrizzlyServerFactory.createHttpServer (uri, handler, true, 
 sslEngineConfig); 
 server.start(); 
 } catch (IllegalArgumentException e) { 
 e.printStackTrace(); 
 } catch (NullPointerException e) { 
 e.printStackTrace(); 
 } catch (IOException e) { 
 e.printStackTrace(); 
 } 
 try { 
 Thread.sleep(1000*1000); 
 } catch (InterruptedException e) { 
 e.printStackTrace(); 
 } 
 } 
 }

配置文件类:

 package com.sean; 
 
 import java.io.File; 
 import java.io.FileInputStream; 
 import java.io.InputStream; 
 import java.util.Properties; 
 
 public class Config{ 
 private static Properties config; 
 
 public static Properties getConfig(){ 
 try{ 
 if(null == config){ 
 File configFile = 
 new File ("src/main/resources/config/config.properties"); 
 if(configFile.exists() && configFile.isFile() 
 && configFile.canRead()){ 
 InputStream input = new FileInputStream(configFile); 
 config = new Properties(); 
 config.load(input); 
 } 
 } 
 }catch(Exception e){ 
 //default set 
 config = new Properties(); 
 config.setProperty ("authority", String.valueOf(1)); 
 config.setProperty ("protocol", "SSL"); 
 config.setProperty ("serverCer", "src/main/resources/certificate/server.jks"); 
 config.setProperty ("serverCerPwd", "1234sp"); 
 config.setProperty ("serverKeyPwd", "1234kp"); 
 config.setProperty ("serverTrustCer", "src/main/resources/certificate/serverTrust.jks"); 
 config.setProperty ("serverTrustCerPwd", "1234sp"); 
 config.setProperty ("clientCer", "src/main/resources/certificate/client.jks"); 
 config.setProperty ("clientCerPwd", "1234sp"); 
 config.setProperty ("clientKeyPwd", "1234kp"); 
 config.setProperty ("clientTrustCer", "src/main/resources/certificate/clientTrust.jks"); 
 config.setProperty ("clientTrustCerPwd", "1234sp"); 
 } 
 return config; 
 } 
 }

配置文件config.properties:

 #1:单向认证,只有服务器端需证明其身份 
 #2:双向认证,服务器端和客户端都需证明其身份 
 authority=2 
 #通信协议 
 protocol=SSL 
 #服务端证书信息 
 serverCer= src/main/resources/certificate/server.jks 
 #keystore的storepass 
 serverCerPwd=1234sp 
 #keystore的keypass 
 serverKeyPwd= 1234kp 
 #服务端证书信息 
 serverTrustCer= src/main/resources/certificate/serverTrust.jks 
 serverTrustCerPwd= 1234sp 
 #客户端证书信息 
 clientCer= src/main/resources/certificate/client.jks 
 clientCerPwd= 1234sp 
 clientKeyPwd= 1234kp 
 clientTrustCer= src/main/resources/certificate/clientTrust.jks 
 clientTrustCerPwd=1234sp

服务端运行结果:

 三月 03, 2015 3:30:54 下午 com.sun.jersey.api.core.PackagesResourceConfig init 
 INFO: Scanning for root resource and provider classes in the packages: 
 com.sean 
 三月 03, 2015 3:30:54 下午 com.sun.jersey.api.core.ScanningResourceConfig logClasses 
 INFO: Root resource classes found: 
 class com.sean.SSLServer 
 三月 03, 2015 3:30:54 下午 com.sun.jersey.api.core.ScanningResourceConfig init 
 INFO: No provider classes found. 
 三月 03, 2015 3:30:54 下午 com.sun.jersey.server.impl.application.WebApplicationImpl _initiate 
 INFO: Initiating Jersey application, version 'Jersey: 1.18 11/22/2013 01:21 AM' 
 三月 03, 2015 3:30:55 下午 org.glassfish.grizzly.http.server.NetworkListener start 
 INFO: Started listener bound to [127.0.0.1:10000] 
 三月 03, 2015 3:30:55 下午 org.glassfish.grizzly.http.server.HttpServer start 
 INFO: [HttpServer] Started. 
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?><person><name>sean</name></person>

客户端运行结果

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <person> <addresses>address1</addresses> <addresses>address2 </addresses></person>
原文  http://www.uml.org.cn/j2ee/201904122.asp
正文到此结束
Loading...