编辑推荐: |
本文来自于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的支持
证书的生成过程这里就不介绍了
代码结构如下:
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>
|
|