Web支持HTTPS的client(HTTP&XML-RPC)
生成Web自签名的证书(在命令行执行以下命令)
keytool -genkey -keysize 2048 -validity 3650 -keyalg RSA -dname "CN=Hanshow, OU=Hanshow, O=Hanshow, L=Jiaxing, ST=Zhejiang, C=CN" -alias shopweb -keypass password_of_key -storepass password_of_store -keystore shopweb.jks
-keysize 2048 指定生成2048位的密钥
-validity 3650 指定证书有效期天数(3650=10年)
-keyalg RSA 指定用RSA算法生成密钥
-dname 设置签发者的信息
-alias 设置别名
-keypass 设定访问key的password
-storepass 设定访问这个KeyStore的password
web.jks指定生成的KeyStore文件名叫web.jks
- 把生成的web.jks存放到classpath路径中。
- 以下代码依赖Jackson JSON,OkHttp3,Apache XML-RPC Client。
- 以下的实现全部是基于trustAll,即信任任何服务器
基础SSL工具类SSLUtils
import javax.net.ssl.HostnameVerifier; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import java.io.FileNotFoundException; import java.io.InputStream; import java.security.KeyStore; import java.security.SecureRandom; import java.security.cert.X509Certificate; public class SSLUtils { public static KeyStore loadKeyStore(String type, String fileName, String password) throws Exception { try (InputStream input = SSLUtils.class.getClassLoader().getResourceAsStream(fileName)) { if (input == null) { throw new FileNotFoundException(String.format("cannot find KeyStore file \"%s\" in classpath", fileName)); } KeyStore ks = KeyStore.getInstance(type); ks.load(input, password.toCharArray()); return ks; } } /** * 创建SSLSocketFactory * * @param protocol SSL协议,默认:TLS * @param algorithm KeyManager算法,默认:SunX509 * @param provider KeyManager提供者,默认:SunJSSE * @param keyPassword Key password * @param keyStoreType KeyStore类型,默认:JKS * @param keyStoreFileName KeyStore文件名,应在classpath中能找到。 * @param storePassword KeyStore的password * @return SSLSocketFactory实例 * @throws Exception */ public static SSLSocketFactory createSSLSocketFactory(String protocol, String algorithm, String provider, String keyPassword, String keyStoreType, String keyStoreFileName, String storePassword) throws Exception { KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(algorithm, provider); KeyStore keyStore = loadKeyStore(keyStoreType, keyStoreFileName, storePassword); keyManagerFactory.init(keyStore, keyPassword.toCharArray()); TrustManager[] trustManagers = new TrustManager[]{ new X509TrustManager() { public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } public void checkClientTrusted(X509Certificate[] certs, String authType) { // Trust always } public void checkServerTrusted(X509Certificate[] certs, String authType) { // Trust always } } }; SSLContext sslContext = SSLContext.getInstance(protocol); sslContext.init(keyManagerFactory.getKeyManagers(), trustManagers, new SecureRandom()); return sslContext.getSocketFactory(); } public static SSLSocketFactory createSSLSocketFactory(String keyPassword, String keyStoreFileName, String storePassword) throws Exception { return createSSLSocketFactory("TLS", "SunX509", "SunJSSE", keyPassword, "JKS", keyStoreFileName, storePassword); } public static HostnameVerifier createHostnameVerifier() { return (hostname, session) -> true; } }
XML-RPC Client
import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.xmlrpc.client.XmlRpcClient; import org.apache.xmlrpc.client.XmlRpcClientConfigImpl; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLSocketFactory; import java.net.URL; public class DemoXmlRpcClient { private static boolean SSLContextInitialized = false; private URL url; private XmlRpcClient xmlRpcClient; public DemoXmlRpcClient(URL url) { this.url = url; if ("https".equalsIgnoreCase(url.getProtocol())) { initSSLContext(); } XmlRpcClientConfigImpl rpcConfig = new XmlRpcClientConfigImpl(); rpcConfig.setServerURL(this.url); // 设置RPC连接超时时间为60秒 rpcConfig.setConnectionTimeout(60 * 1000); // 设置RPC等待响应时间为60秒 rpcConfig.setReplyTimeout(60 * 1000); this.xmlRpcClient = new XmlRpcClient(); this.xmlRpcClient.setConfig(rpcConfig); } private synchronized void initSSLContext() { if (!SSLContextInitialized) { // 只需要初始化一次 try { SSLSocketFactory sslSocketFactory = SSLUtils.createSSLSocketFactory( "password_of_key", "shopweb.jks", "password_of_store"); HttpsURLConnection.setDefaultSSLSocketFactory(sslSocketFactory); HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> true); } catch (Throwable t) { throw new RuntimeException("initialize SSLContext for XML-RPC error", t); } SSLContextInitialized = true; } } public Object execute(String command, Object[] params) throws Exception { return xmlRpcClient.execute(command, params); } public URL getUrl() { return url; } public static void main(String[] args) throws Exception { ObjectMapper objectMapper = new ObjectMapper(); Object response; // 测试通过HTTPS向ESL-Working发送XML-RPC请求 DemoXmlRpcClient sslClient = new DemoXmlRpcClient(new URL("https://127.0.0.1:9443/RPC2")); response = sslClient.execute("send_cmd", new Object[]{"API_VERSION", new Object[]{}}); System.out.println(objectMapper.writeValueAsString(response)); // 测试通过HTTP向ESL-Working发送XML-RPC请求 DemoXmlRpcClient normalClient = new DemoXmlRpcClient(new URL("http://127.0.0.1:9000/RPC2")); response = normalClient.execute("send_cmd", new Object[]{"API_VERSION", new Object[]{}}); System.out.println(objectMapper.writeValueAsString(response)); } }
HTTP Client
import com.fasterxml.jackson.databind.ObjectMapper; import okhttp3.MediaType; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; import okhttp3.Response; import okhttp3.ResponseBody; import okhttp3.internal.platform.Platform; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.X509TrustManager; import java.io.IOException; import java.net.URL; import java.util.concurrent.TimeUnit; public class DemoHttpClient { private final static String JSON_MEDIA_TYPE_PATTERN = "application/json; charset=%s"; private final static String DEFAULT_CHARSET = "utf-8"; private final static String DEFAULT_CONTENT_TYPE = String.format(JSON_MEDIA_TYPE_PATTERN, DEFAULT_CHARSET); private final static ObjectMapper objectMapper = new ObjectMapper(); private OkHttpClient httpClient; private OkHttpClient httpsClient; public DemoHttpClient(String keyPassword, String fileName, String storePassword) throws Exception { httpClient = new OkHttpClient.Builder() .readTimeout(60, TimeUnit.SECONDS) .connectTimeout(60, TimeUnit.SECONDS) .writeTimeout(60, TimeUnit.SECONDS).build(); SSLSocketFactory sslSocketFactory = SSLUtils.createSSLSocketFactory(keyPassword, fileName, storePassword); X509TrustManager x509TrustManager = Platform.get().trustManager(sslSocketFactory); httpsClient = new OkHttpClient.Builder() .readTimeout(60, TimeUnit.SECONDS) .connectTimeout(60, TimeUnit.SECONDS) .writeTimeout(60, TimeUnit.SECONDS) .sslSocketFactory(sslSocketFactory, x509TrustManager) .hostnameVerifier(SSLUtils.createHostnameVerifier()) .build(); } public String get(String url) throws IOException { return get(new URL(url)); } public String get(URL url) throws IOException { return httpRequest(url, "GET", DEFAULT_CONTENT_TYPE, null); } public String post(String url, Object data) throws IOException { return post(new URL(url), data); } public String post(URL url, Object object) throws IOException { byte[] data = objectMapper.writeValueAsString(object).getBytes(DEFAULT_CHARSET); return httpRequest(url, "POST", DEFAULT_CONTENT_TYPE, data); } public String put(String url, Object data) throws IOException { return put(new URL(url), data); } public String put(URL url, Object object) throws IOException { byte[] data = objectMapper.writeValueAsString(object).getBytes(DEFAULT_CHARSET); return httpRequest(url, "PUT", DEFAULT_CONTENT_TYPE, data); } public String httpRequest(URL url, String method, String contentType, byte[] data) throws IOException { OkHttpClient client; String protocol = url.getProtocol(); if ("http".equalsIgnoreCase(protocol)) { client = httpClient; } else if ("https".equalsIgnoreCase(protocol)) { client = httpsClient; } else { throw new UnsupportedOperationException("unsupported protocol: " + protocol); } Request.Builder builder = new Request.Builder().url(url); MediaType mediaType = MediaType.parse(contentType); if ("GET".equalsIgnoreCase(method)) { builder.get(); } else { RequestBody requestBody = RequestBody.create(mediaType, data == null ? new byte[0] : data); builder.method(method, requestBody); } Request request = builder.build(); try (Response response = client.newCall(request).execute()) { if (response.isSuccessful()) { ResponseBody responseBody = response.body(); return responseBody == null ? null : responseBody.string(); } else { throw new IOException(String.format( "%s/%s %s got unexpected response code %d", protocol.toUpperCase(), method, url, response.code())); } } } public static void main(String[] args) throws Exception { DemoHttpClient httpClient = new DemoHttpClient("password_of_key", "shopweb.jks", "password_of_store"); // 通过HTTP访问ESL-Working RESTful接口 System.out.println(httpClient.get("http://127.0.0.1:9000/api2/runinfo")); // 通过HTTPS访问ESL-Working RESTful接口 System.out.println(httpClient.get("https://127.0.0.1:9443/api2/runinfo")); } }