研究了有一天了,最后一博讲解https,https接口也是以后接口开发中必定会碰到的,这里只是简单介绍一下简单的java工程中的实现,在更复杂的项目中必定有更优秀的框架来实现。
第一步先创建两个秘钥库,服务的库和客户端库,我这里都用keystore文件保存。
jdk自带的keytool命令生成各自的秘钥库
keytoo -****** -alias server -keypass 123456 -keysize 1024 -keyalg RSA -validity 720 -keystore "e:/keystore/server.keystore" -storeypass 123456 -dname "CN=localhost,OU=sf,O=cib,L=sz,ST=gd,C=cn"
keytoo -****** -alias client -keypass 123456 -keysize 1024 -keyalg RSA -validity 720 -keystore "e:/keystore/client.keystore" -storeypass 123456 -dname "CN=localhost,OU=sf,O=cib,L=sz,ST=gd,C=cn"
导出各自秘钥并导入到对方的秘钥库中
keytool -export -alias server -keystore "e:/keystore/server.keystore" -file "e:/keystore/cer/server.cer" -storeypass 123456
keytool -export -alias client-keystore "e:/keystore/client.keystore" -file "e:/keystore/cer/client.cer" -storeypass 123456
keytool -import -alias client -file "e:/keystore/cer/client.cer" -keystore "e:/keystore/server.keystore" -storeypass 123456
keytool -import -alias server -file "e:/keystore/cer/server .cer" -keystore "e:/keystore/client.keystore" -storeypass 123456
查看秘钥库内容
keytool -list -v -keystore -keystore "e:/keystore/server.keystore" -storepass 123456
配置tomcat server.xml服务器SSL请求 clientAuth="true" 为双向认证
<Connector SSLEnabled="true"
keystoreFile="E:\keystore\httpsweb.keystore"
keystorePass="changeit"
truststoreFile="E:\keystore\httpsweb.keystore"
truststorePass="changeit" scheme="https" secure="true"
clientAuth="true" sslProtocol="SSL"
maxThreads="150" port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"/>
配置web.xml
<servlet>
<servlet-name>httpsweb</servlet-name>
<servlet-class>com.wm.core.HttpswebServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>httpsweb</servlet-name>
<url-pattern>/ssl</url-pattern>
</servlet-mapping>
<security-constraint>
<web-resource-collection>
<web-resource-name>SSL</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
<!-- CONFIDENTIAL: 要保证服务器和客户端之间传输的数据不能够被修改,且不能被第三方查看到 -->
<!-- INTEGRAL: 要保证服务器和client之间传输的数据不能够被修改 -->
<!-- NONE: 指示容器必须能够在任一的连接上提供数据。(即用HTTP或HTTPS,由客户端来决定) -->
</user-data-constraint>
</security-constraint>
服务端代码
客户端代码 http://hc.apache.org/downloads.cgi httpClient包下载地址
private static final String KEY_STORE_TYPE_JKS = "jks";
private static final String SCHEME_HTTPS = "https";
private static final int HTTPS_PORT = 8443;
private static final String HTTPS_URL = "https://localhost:8443/httpsWeb/ssl";
private static final String KEYSTORE_PATH = "E:\\keystore\\client.keystore";
private static final String KEY_STORE_PASSWORD = "123456";
private static final String CHARSET = "utf-8";
public static String doPost(String url, Map<String, String> map) throws Exception {
HttpClient httpClient = new DefaultHttpClient();
String result = "";
try {
InputStream ksin = null;
InputStream tsin = null;
KeyStore keyStore = KeyStore.getInstance(KEY_STORE_TYPE_JKS);
KeyStore trustKeyStore = KeyStore.getInstance(KEY_STORE_TYPE_JKS);
try {
ksin = new FileInputStream(KEYSTORE_PATH);
tsin = new FileInputStream(KEYSTORE_PATH);
keyStore.load(ksin, KEY_STORE_PASSWORD.toCharArray());
trustKeyStore.load(tsin, KEY_STORE_PASSWORD.toCharArray());
} catch (Exception e) {
throw e;
} finally {
if (ksin != null) {
ksin.close();
}
if (tsin != null) {
tsin.close();
}
}
SSLSocketFactory socketFactory = new SSLSocketFactory(keyStore, KEY_STORE_PASSWORD, trustKeyStore);
Scheme sch = new Scheme(SCHEME_HTTPS, HTTPS_PORT, socketFactory);
httpClient.getConnectionManager().getSchemeRegistry().register(sch);
HttpPost httpPost = new HttpPost(url);
// 设置参数
if (map != null) {
List<NameValuePair> list = new ArrayList<NameValuePair>();
Iterator<Entry<String, String>> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, String> elem = (Map.Entry<String, String>) iterator.next();
list.add(new BasicNameValuePair(elem.getKey(), elem.getValue()));
}
httpPost.setEntity(new UrlEncodedFormEntity(list, CHARSET));
}
HttpResponse response = httpClient.execute(httpPost);
if (response != null) {
HttpEntity resEntity = response.getEntity();
if (resEntity != null) {
result = EntityUtils.toString(resEntity, CHARSET);
}
}
} finally {
httpClient.getConnectionManager().shutdown();
}
return result;
}
public static void main(String[] args) throws Exception {
Map<String,String> map = new HashMap<String,String>();
map.put("xml", "https request data");
String result = httpClientUtil.doPost(HTTPS_URL, map);
System.out.println(result);
}
另一种客户端实现 纯jdk实现 但是取值的方式这里有限制
private static final String KEYSTORE_PATH = "E:\\keystore\\client.keystore";
private static final String KEYSTORE_PASS = "123456";
private static final String LOCAL_URL = "https://localhost:8443/httpsWeb/ssl";
private static SSLContext sslContext;
public static void initSSL() throws Exception{
synchronized(SkipAuthReq.class) {
if(sslContext != null) {
return;
}
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
FileInputStream ks = new FileInputStream(new File(KEYSTORE_PATH));
KeyStore mk = KeyStore.getInstance("jks");
mk.load(ks, KEYSTORE_PASS.toCharArray());
kmf.init(mk, KEYSTORE_PASS.toCharArray());
KeyManager[] keymanager = kmf.getKeyManagers();
TrustManagerFactory trustmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
FileInputStream trustks = new FileInputStream(new File(KEYSTORE_PATH));
KeyStore trustmk = KeyStore.getInstance("jks");
trustmk.load(trustks, KEYSTORE_PASS.toCharArray());
trustmf.init(trustmk);
sslContext = SSLContext.getInstance("TLS");
//sslContext.init(keymanager, trustmf.getTrustManagers(), null); //验证服务器验证
sslContext.init(keymanager, new TrustManager[] {new defaultTrustManage()}, null); //跳过验证
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
});
}
}
private static class defaultTrustManage implements X509TrustManager{
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
}
public static String doPost(String param) throws Exception {
String rsp = "";
BufferedReader read = null;
OutputStream out = null;
byte[] content = param.getBytes();
try {
URL url = new URL(LOCAL_URL);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
if(conn instanceof HttpsURLConnection) {
initSSL();
((HttpsURLConnection) conn).setSSLSocketFactory(sslContext.getSocketFactory());
}
conn.setRequestProperty("accept", "*/*"); //请求报文可通过一个“Accept”报文头属性告诉服务端 客户端接受什么类型的响应
conn.setRequestProperty("connection", "Keep-Alive");//表示是否需要持久连接。(HTTP 1.1默认进行持久连接)Keep-Alive为持久连接
conn.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 7.0; WOW64)");//(用户代理),简称 UA,它是一个特殊字符串头,使得服务器能够识别客户端使用的操作系统及版本、CPU 类型、浏览器及版本、浏览器渲染引擎、浏览器语言、浏览器插件等。
conn.setRequestProperty("Content-type", "application/json;charset=utf-8"); //返回的响应MIME类型与编码–告诉浏览器它发送的数据属于什么文件类型
conn.setRequestMethod("POST");
conn.setDoOutput(true);
conn.setDoInput(true);
out = conn.getOutputStream();
out.write(content);
out.flush();
if(200 == conn.getResponseCode()) {
read = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"));
}else {
read = new BufferedReader(new InputStreamReader(conn.getErrorStream()));
}
String line = "";
while ((line = read.readLine()) != null) {
rsp += line;
}
}finally{
if(read != null) {
read.close();
}
if(out != null) {
out.close();
}
}
return rsp;
}
这里给了几种实现,可以根据具体需求跟场景搭配组合使用。