1.引入包
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.6</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>4.3.2</version>
</dependency>
2.對於單向https,客戶端通過配置truststore來驗證服務器證書合法性即可,而服務器則無需驗證客戶端身份。
對於雙向https則服務器端需要驗證客戶端身份,如此客戶端需要配置對應keystore,其中keystore存儲着客戶端密鑰、公鑰證書等信息。
實際完整代碼如下:
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.InterruptedIOException;
import java.net.URL;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.Enumeration;
import java.util.concurrent.TimeUnit;
import java.util.zip.GZIPInputStream;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.CookieStore;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.config.SocketConfig;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.AllowAllHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.util.EntityUtils;
import org.json.JSONObject;
public class ClientUtil {
private static PoolingHttpClientConnectionManager cm;
private CloseableHttpClient httpClient;
private HttpClientContext context;
public boolean ishttps;
private String prefix;
//雙向驗證ssl、https
public ClientUtil(String prefix,String CLIENT_CERT_FILE,String CLIENT_PWD,String TRUST_STRORE_FILE,String TRUST_STORE_PWD) throws Exception {
ishttps = "https".equals(new URL(prefix).getProtocol());
if(!ishttps){
throw new Exception("請選擇http初始化類");
}
// 初始化密鑰庫
KeyManagerFactory keyManagerFactory = KeyManagerFactory
.getInstance("SunX509");
KeyStore keyStore = getKeyStore(CLIENT_CERT_FILE, CLIENT_PWD, "PKCS12");
keyManagerFactory.init(keyStore, CLIENT_PWD.toCharArray());
//獲取一些證書信息,添加到請求頭中
/*Enumeration<String> aliases = keyStore.aliases();
String keyAlias = null;
while(aliases.hasMoreElements())
{
keyAlias = aliases.nextElement();
if(keyStore.isKeyEntry(keyAlias))
break;
}
PrivateKey signKey = (PrivateKey) keyStore.getKey(keyAlias, "1".toCharArray());
X509Certificate signCert = (X509Certificate) keyStore.getCertificate(keyAlias);
System.out.println("私鑰是:"+Base64.encode(signKey.getEncoded()));
System.out.println("公鑰證書是:"+Base64.encode(signCert.getEncoded()));
System.out.println("公鑰證書序列號是:"+signCert.getSerialNumber().toString(16).toUpperCase());
System.out.println("頒發者是:"+signCert.getIssuerDN().getName()); */
// 初始化信任庫
TrustManagerFactory trustManagerFactory = TrustManagerFactory
.getInstance("SunX509");
KeyStore trustkeyStore = getKeyStore(TRUST_STRORE_FILE, TRUST_STORE_PWD,"JKS");
trustManagerFactory.init(trustkeyStore);
// 初始化SSL上下文
SSLContext sslcontext = SSLContext.getInstance("SSL");
sslcontext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory
.getTrustManagers(), null);
//設置規則
SSLConnectionSocketFactory ssf = new SSLConnectionSocketFactory(sslcontext,
new String[]{"TLSv1","TLSv1.1","TLSv1.2"},null,
new HttpsHostnameVerifier(prefix));
//new AllowAllHostnameVerifier()不驗證服務器證書與服務器ip之間關聯
//註冊
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory> create()
.register("http", PlainConnectionSocketFactory.INSTANCE)
.register("https", ssf).build();
if (cm == null) {
cm = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
cm.setMaxTotal(200);
cm.setDefaultMaxPerRoute(5);
cm.setDefaultSocketConfig(SocketConfig.custom().setTcpNoDelay(true).build());
cm.closeExpiredConnections();
cm.closeIdleConnections(60, TimeUnit.SECONDS);
}
CookieStore cookieStore = new BasicCookieStore();
context = HttpClientContext.create();
context.setCookieStore(cookieStore);
RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(10000)
.setConnectTimeout(10000).setSocketTimeout(30000).build();
this.prefix = prefix;
httpClient = HttpClients.custom().setConnectionManager(cm).setDefaultRequestConfig(requestConfig)
.setKeepAliveStrategy(new DefaultConnectionKeepAliveStrategy()).build();
}
//單向驗證https
public ClientUtil(String prefix,String TRUST_STRORE_FILE,String TRUST_STORE_PWD) throws Exception {
ishttps = "https".equals(new URL(prefix).getProtocol());
if(!ishttps){
throw new Exception("請選擇http初始化該類");
}
// 初始化信任庫
TrustManagerFactory trustManagerFactory = TrustManagerFactory
.getInstance("SunX509");
KeyStore trustkeyStore = getKeyStore(TRUST_STRORE_FILE, TRUST_STORE_PWD,"JKS");
trustManagerFactory.init(trustkeyStore);
SSLContext sslcontext = SSLContexts.custom().loadTrustMaterial(trustkeyStore, new TrustSelfSignedStrategy()).build();
//設置規則
SSLConnectionSocketFactory ssf = new SSLConnectionSocketFactory(sslcontext,
new String[]{"TLSv1","TLSv1.1","TLSv1.2"},null,
new AllowAllHostnameVerifier());
//註冊
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory> create()
.register("http", PlainConnectionSocketFactory.INSTANCE)
.register("https", ssf).build();
if (cm == null) {
cm = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
cm.setMaxTotal(200);
cm.setDefaultMaxPerRoute(5);
cm.setDefaultSocketConfig(SocketConfig.custom().setTcpNoDelay(true).build());
cm.closeExpiredConnections();
cm.closeIdleConnections(60, TimeUnit.SECONDS);
}
CookieStore cookieStore = new BasicCookieStore();
context = HttpClientContext.create();
context.setCookieStore(cookieStore);
RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(10000)
.setConnectTimeout(10000).setSocketTimeout(30000).build();
this.prefix = prefix;
httpClient = HttpClients.custom().setConnectionManager(cm).setDefaultRequestConfig(requestConfig)
.setKeepAliveStrategy(new DefaultConnectionKeepAliveStrategy()).build();
}
public String Post(String urlEnd, String param) {
HttpPost httpPost = new HttpPost(prefix + urlEnd);
try {
httpPost.setHeader("Connection", "keep-alive");
if (param != null) {
httpPost.setEntity(new StringEntity(param, ContentType.APPLICATION_JSON));
}
HttpResponse httpResponse = null;
while (httpResponse == null) {
try {
httpResponse = httpClient.execute(httpPost, context);
} catch (InterruptedIOException ex) {
if (!ex.getMessage().equals("Connection has been shut down")) {
throw ex;
}
}
}
int code = httpResponse.getStatusLine().getStatusCode();
switch (code)
{
case 200:
{
HttpEntity entity = httpResponse.getEntity();
byte[] bs = EntityUtils.toByteArray(entity);
String jsonStr;
if (bs[0] != '{') {
if (bs[0] == 0x1F && bs[1] == 0x8B && bs[2] == 0x08) {
GZIPInputStream gzin = new GZIPInputStream(new ByteArrayInputStream(bs));
InputStreamReader isr = new InputStreamReader(gzin, "utf-8");
BufferedReader br = new BufferedReader(isr);
StringBuilder sb = new StringBuilder();
String tempbf;
while ((tempbf = br.readLine()) != null) {
sb.append(tempbf);
sb.append("\r\n");
}
isr.close();
gzin.close();
jsonStr = sb.toString();
} else {
jsonStr = new String(bs);
//System.out.println(jsonStr);
//jsonStr = "{\"data\":\"" + jsonStr + "\"}";
}
} else {
jsonStr = new String(bs, "utf-8");
}
return jsonStr;
}
default:
return "http code is "+code;
}
} catch (Exception ex) {
return ex.toString();
} finally {
httpPost.releaseConnection();
}
}
/**
* 獲得KeyStore
*
* @param keyStorePath
* @param password
* @return
* @throws Exception
*/
private static KeyStore getKeyStore(String keyStorePath, String password,String type)
throws Exception {
FileInputStream is = new FileInputStream(keyStorePath);
KeyStore ks = KeyStore.getInstance(type);
ks.load(is, password.toCharArray());
is.close();
return ks;
}
public static void main(String[] args) throws Exception {
String prefix="https://192.168.3.111:443";
String CLIENT_CERT_FILE="C:/rsa1.pfx";// 客戶端證書路徑,用了本地絕對路徑,需要修改
String CLIENT_PWD="1";// 客戶端證書密碼
String TRUST_STRORE_FILE="C:/trustStore";// 信任庫路徑
String TRUST_STORE_PWD="changeit";// 信任庫密碼
ClientUtil client=new ClientUtil(prefix,CLIENT_CERT_FILE,CLIENT_PWD,TRUST_STRORE_FILE,TRUST_STORE_PWD);
//ClientUtil client1=new ClientUtil(prefix,TRUST_STRORE_FILE,TRUST_STORE_PWD);
String url = "/test_args";
String url1="/redis";
String httpOrgCreateTestRtn = client.Post(url, null);
System.out.println(httpOrgCreateTestRtn);
}
}
這裏CLIENT_CERT_FILE採用pdf證書,這裏使用標準ca頒發證書,trustStore生成可以參照keytool配置ssl策略。
其中new HttpsHostnameVerifier(prefix)、new AllowAllHostnameVerifier()分別爲手動編寫的主機域名驗證策略與採取不驗證允許所有。這裏HttpsHostnameVerifier主要防止重定向。
import java.security.cert.X509Certificate;
import java.util.List;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSession;
import org.apache.http.conn.util.PublicSuffixMatcher;
public class HttpsHostnameVerifier implements HostnameVerifier {
private String host;
public HttpsHostnameVerifier(String host){
String hostString=host.replace("https://", "");
this.host = hostString.substring(0, hostString.indexOf(":"));
}
/**
* 驗證對方主機名稱 ,防止服務器證書上的Hostname和實際的URL不匹配,這個目前證書不具備和ip主題相同
* 防止鏈接被重定向到其他的不安全的地址
*/
@Override
public boolean verify(String hostname, SSLSession session) {
System.out.println("hostname = [" + hostname + "],session = [" + session.getPeerHost() + "]");
if (hostname.equals(host) || session.equals(host))
return true;
else
return false;
}
}
經驗證,設置成雙向時只能初始化爲雙向進行訪問,但當nginx設置成單向時,使用雙向亦可訪問。