关于数据安全通信设计方案
背景:
数据通信时需要保证数据安全,除了敏感字加密,也可以使用https服务方式传送。
技术点:
分布式 tomcat cxf restful服务 jdk keytool安全密钥。
架构:
实现:
1.提供数据服务 (restful get/post)
AgdRestfulServiceImpl
package com.agd.service.impl;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
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.UriInfo;
import com.agd.service.AgdRestfulService;
import com.alibaba.fastjson.JSONObject;
/**
* restful 服务
* 启动成功后 可用路径测试
* https://localhost:8443/agdRestFul/services/restful/init/123456
* @author kuanghongjiang
*
*/
@Path(value = "/")
public class AgdRestfulServiceImpl implements AgdRestfulService {
@Context
private UriInfo uriInfo;
@Context
private HttpServletRequest request;
@POST
@Path("/sendData")
@Produces(MediaType.APPLICATION_JSON)
public String sendData() throws IOException
{
System.out.println("sendData::"+ request.getAttributeNames());
return "success";
}
@GET
@Path("/init/{id}")
@Produces(MediaType.APPLICATION_JSON)
public String init(@PathParam("id")String id)
{
JSONObject obj = new JSONObject();
System.out.println("my first restful");
obj.put("id", id);
return obj.toString();
}
}
package com.agd.service;
import java.io.IOException;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
@Path(value = "/AgdRestfulService")
public interface AgdRestfulService {
@POST
@Path("/sendData")
@Produces(MediaType.APPLICATION_JSON)
public String sendData() throws IOException;
@GET
@Path("/init/{id}")
@Produces(MediaType.APPLICATION_JSON)
public String init(@PathParam("id")String id);
}
<s<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:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:jaxws="http://cxf.apache.org/jaxws" xmlns:jaxrs="http://cxf.apache.org/jaxrs"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd ">
<!-- 为cxf 的配置 -->
<import resource="classpath:META-INF/cxf/cxf.xml" />
<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
<bean id="agdRestfulService" class="com.agd.service.impl.AgdRestfulServiceImpl" />
<!-- 这里的地址很重要,客户端需要通过这个地址来访问WebService -->
<!-- 访问地址为 http://localhost:8080/zzMyRestFul/services/restful/ 方法类path -->
<jaxrs:server id="restServiceContainer" address="/restful">
<jaxrs:serviceBeans>
<ref bean="agdRestfulService" />
</jaxrs:serviceBeans>
<jaxrs:extensionMappings>
<entry key="json" value="application/json" />
<entry key="xml" value="application/xml" />
</jaxrs:extensionMappings>
<jaxrs:languageMappings>
<entry key="en" value="en-gb" />
</jaxrs:languageMappings>
</jaxrs:server>
</beans>
<span style="font-family: Arial, Helvetica, sans-serif;">web.xml</span>
<?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_2_5.xsd" version="2.5">
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring_restful.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>/services/*</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
以上已完成restful 服务开发
2.发布成https方式
tomcat 里server.xml 需要修改:
<!--
<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
maxThreads="150" SSLEnabled="true" scheme="https" secure="true"
clientAuth="false" sslProtocol="TLS" />
-->
<span style="white-space: pre;"> </span>
<span style="white-space: pre;"> </span><Connector
protocol="org.apache.coyote.http11.Http11Protocol"
port="8443" maxThreads="200"
scheme="https" secure="true" SSLEnabled="true"
<span style="color:#ff6666;">keystoreFile="d:\\tomcat.keystore" keystorePass="password"</span>
clientAuth="false" sslProtocol="TLS"/>
keystoreFile为是生成密钥文件
keystorePass安全认证密码
以上tomcat 就可以起动了,正常情况 https服务就可以访问了。当然这里会报错,上面红色部分还没有完成.
3. jdk keytool 生成密钥和导入实例化信任库
keytool -genkey -alias tomcat -keyalg RSA -keypass password -storepass password -keystore d:\tomcat.keystore -validity 3600
password 为密码。
d:\tomcat.keystore 生成的文件。
导入过程分2步,第一步是导出证书,
命令如下:keytool -export -trustcacerts -alias tomcat -file d:\tomcat.keystore.cer -keystore tomcat.keystore -storepass password
第二步是导入到证书信任库,
命令如下:keytool -import -trustcacerts -alias tomcat -file d:\tomcat.keystore.cer -keystore d:\tomcat.trustcacerts -storepass password
这样就完成 发布https 服务了。
4. 客户端应用 远程消费https 服务
package com.agd.https;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;
/**
*
* 实现用于主机名验证的基接口。 在握手期间,如果 URL 的主机名和服务器的标识主机名不匹配,则验证机制可以回调此接口的实现程序来确定是否应该允许此连接。
*
* @author kuanghongjiang
*/
public class MyHostnameVerifier implements HostnameVerifier
{
@Override
public boolean verify(String hostname, SSLSession session)
{
if ("localhost".equals(hostname))
{
return true;
}
else
{
return false;
}
}
}
package com.agd.client;
import com.agd.https.HttpsPostUtils;
/**
* <一句话功能简述>
* <功能详细描述>
*
* @author kuanghongjiang
* @version [版本号, 2015年11月1日]
* @see [相关类/方法]
* @since [产品/模块版本]
*/
public class HttpsClient
{
public static void main(String[] args)
{
// 取得认证
HttpsPostUtils.initHTTPS();
// 本地起的https服务
String restgetTest = "https://localhost:8443/agdRestFul/services/restful/init/2222";
String restpostTest = "https://localhost:8443/agdRestFul/services/restful/sendData";
// 传输文本
String xmlStr = "my https request";
// 发起请求
HttpsPostUtils.post(restpostTest, xmlStr);
HttpsPostUtils.get(restgetTest, xmlStr);
}
}
package com.agd.https;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
/**
* HTTPS 请求工具类 <一句话功能简述> <功能详细描述>
*
* @author kuanghongjiang
* @version [版本号, 2015年11月1日]
* @see [相关类/方法]
* @since [产品/模块版本]
*/
public class HttpsPostUtils
{
/**
* 获得KeyStore.
*
* @param keyStorePath 密钥库路径
* @param password 密码
* @return 密钥库
* @throws Exception
*/
public static KeyStore getKeyStore(String password, String keyStorePath)
throws Exception
{
// 实例化密钥库
KeyStore ks = KeyStore.getInstance("JKS");
// 获得密钥库文件流
FileInputStream is = new FileInputStream(keyStorePath);
// 加载密钥库
ks.load(is, password.toCharArray());
// 关闭密钥库文件流
is.close();
return ks;
}
/**
* 获得SSLSocketFactory.
*
* @param password 密码
* @param keyStorePath 密钥库路径
* @param trustStorePath 信任库路径
* @return SSLSocketFactory
* @throws Exception
*/
public static SSLContext getSSLContext(String password, String keyStorePath, String trustStorePath)
throws Exception
{
// 实例化密钥库
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
// 获得密钥库
KeyStore keyStore = getKeyStore(password, keyStorePath);
// 初始化密钥工厂
keyManagerFactory.init(keyStore, password.toCharArray());
// 实例化信任库
TrustManagerFactory trustManagerFactory =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
// 获得信任库
KeyStore trustStore = getKeyStore(password, trustStorePath);
// 初始化信任库
trustManagerFactory.init(trustStore);
// 实例化SSL上下文
SSLContext ctx = SSLContext.getInstance("TLS");
// 初始化SSL上下文
ctx.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);
// 获得SSLSocketFactory
return ctx;
}
/**
* 初始化HttpsURLConnection.
*
* @param password 密码
* @param keyStorePath 密钥库路径
* @param trustStorePath 信任库路径
* @throws Exception
*/
public static void initHttpsURLConnection(String password, String keyStorePath, String trustStorePath)
throws Exception
{
// 声明SSL上下文
SSLContext sslContext = null;
// 实例化主机名验证接口
HostnameVerifier hnv = new MyHostnameVerifier();
try
{
sslContext = getSSLContext(password, keyStorePath, trustStorePath);
}
catch (GeneralSecurityException e)
{
e.printStackTrace();
}
if (sslContext != null)
{
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
}
HttpsURLConnection.setDefaultHostnameVerifier(hnv);
}
/**
* 发送请求.
*
* @param httpsUrl 请求的地址
* @param xmlStr 请求的数据
*/
public static void post(String httpsUrl, String xmlStr)
{
HttpsURLConnection urlCon = null;
try
{
urlCon = (HttpsURLConnection)(new URL(httpsUrl)).openConnection();
urlCon.setDoInput(true);
urlCon.setDoOutput(true);
urlCon.setRequestMethod("POST");
urlCon.setRequestProperty("Content-Length", "application/json");
String param = "{\"username\":kuanghongjiang,\"password\":\"123456\"}";
// urlCon.setUseCaches(false);
// 设置为gbk可以解决服务器接收时读取的数据中文乱码问题
OutputStream out = urlCon.getOutputStream();
out.write(param.getBytes("gbk"));
out.flush();
if (urlCon.getResponseCode() != 200)
{
throw new RuntimeException("HTTPS POST Request Failed with Error code : " + urlCon.getResponseCode());
}
BufferedReader in = new BufferedReader(new InputStreamReader(urlCon.getInputStream()));
String line;
while ((line = in.readLine()) != null)
{
System.out.println(line);
}
// out.close();
}
catch (MalformedURLException e)
{
e.printStackTrace();
}
catch (IOException e)
{
e.printStackTrace();
}
catch (Exception e)
{
e.printStackTrace();
}
}
/**
* 发送请求. GET方式
*
* @param httpsUrl 请求的地址
* @param xmlStr 请求的数据
*/
public static void get(String httpsUrl, String xmlStr)
{
HttpsURLConnection urlCon = null;
try
{
urlCon = (HttpsURLConnection)(new URL(httpsUrl)).openConnection();
urlCon.setDoInput(true);
urlCon.setDoOutput(true);
urlCon.setRequestMethod("GET");
urlCon.setRequestProperty("Content-Length", "application/json");
urlCon.setUseCaches(false);
if (urlCon.getResponseCode() != 200)
{
throw new RuntimeException("HTTPS GET Request Failed with Error code : " + urlCon.getResponseCode());
}
BufferedReader in = new BufferedReader(new InputStreamReader(urlCon.getInputStream()));
String line;
while ((line = in.readLine()) != null)
{
System.out.println(line);
}
}
catch (MalformedURLException e)
{
e.printStackTrace();
}
catch (IOException e)
{
e.printStackTrace();
}
catch (Exception e)
{
e.printStackTrace();
}
}
public static void initHTTPS()
{
// 密码
String password = "password";
// 密钥库
String keyStorePath = "F:\\project\\other\\tools\\tomcat\\apache-tomcat-8.0.21\\conf\\tomcat.keystore";
// 信任库
String trustStorePath = "F:\\project\\other\\tools\\tomcat\\apache-tomcat-8.0.21\\conf\\tomcat.trustcacerts";
// 发起https 必要条件
try
{
HttpsPostUtils.initHttpsURLConnection(password, keyStorePath, trustStorePath);
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
使用客户端就能完成数据通信。
分布式这里就不说了,用ningx代理多台tomcat,之前有一片博文参考