编辑推荐: |
本文来自于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
表明被注解的方法响应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; } }
服务端代码去掉下面的配置
客户端代码去掉下面的配置
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的支持
证书的生成过程这里就不介绍了
代码结构如下:
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>
客户端运行结果