爲何要使用ip直連這種方式去請求咱們的服務器呢?這其實和國內運營傷有關,運營商有時爲了利益會將你的域名劫持換成他人的域名,爲了防止這種狀況的發生通用的解決辦法要麼聯繫運營商要麼就只能使用ip直連了。廣泛你們目前使用的都是okHttp,這裏就以okHttp爲例子。其實很是簡單隻須要設置一下兩個方法就行:java
OkHttpClient.Builder builder = new OkHttpClient.Builder(); .... String domain = ....; builder.sslSocketFactory(new TlsSniSocketFactory(domain), new SSLUtil.TrustAllManager()) .hostnameVerifier(new TrueHostnameVerifier(domain));
經過調用sslSocketFactory()方法傳入兩個參數一個是:SSLSocket,還有一個x509TrustManager。咱們來看看第一個參數是如何實現的:後端
public class TlsSniSocketFactory extends SSLSocketFactory { private final String TAG = TlsSniSocketFactory.class.getSimpleName(); HostnameVerifier hostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier(); String peerHost; public TlsSniSocketFactory(String peerHost) { this.peerHost = peerHost; } public TlsSniSocketFactory() { } @Override public Socket createSocket() { return null; } @Override public Socket createSocket(String host, int port) { return null; } @Override public Socket createSocket(String host, int port, InetAddress localHost, int localPort) { return null; } @Override public Socket createSocket(InetAddress host, int port) { return null; } @Override public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) { return null; } // TLS layer @Override public String[] getDefaultCipherSuites() { return new String[0]; } @Override public String[] getSupportedCipherSuites() { return new String[0]; } @Override public Socket createSocket(Socket plainSocket, String host, int port, boolean autoClose) throws IOException { if (TextUtils.isEmpty(peerHost)) { peerHost = host; peerHost = ....; } Log.i(TAG, "customized createSocket. host: " + peerHost); InetAddress address = plainSocket.getInetAddress(); if (autoClose) { // we don't need the plainSocket plainSocket.close(); } // create and connect SSL socket, but don't do hostname/certificate verification yet SSLCertificateSocketFactory sslSocketFactory = (SSLCertificateSocketFactory) SSLCertificateSocketFactory.getDefault(0); SSLSocket ssl = (SSLSocket) sslSocketFactory.createSocket(address, port); // enable TLSv1.1/1.2 if available ssl.setEnabledProtocols(ssl.getSupportedProtocols()); // set up SNI before the handshake if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { Log.i(TAG, "Setting SNI hostname"); sslSocketFactory.setHostname(ssl, peerHost); } else { Log.d(TAG, "No documented SNI support on Android <4.2, trying with reflection"); try { java.lang.reflect.Method setHostnameMethod = ssl.getClass().getMethod("setHostname", String.class); setHostnameMethod.invoke(ssl, peerHost); } catch (Exception e) { Log.w(TAG, "SNI not useable", e); } } // verify hostname and certificate SSLSession session = ssl.getSession(); if (!hostnameVerifier.verify(peerHost, session)) throw new SSLPeerUnverifiedException("Cannot verify hostname: " + peerHost); Log.i(TAG, "Established " + session.getProtocol() + " connection with " + session.getPeerHost() + " using " + session.getCipherSuite()); return ssl; } }
爲了防止獲取不到domain將外圍的domain塞入,將這個domain塞入返回咱們的ssl。x509TrustManagerx信任了全部的證書,固然正常狀況下應該使用和後端約定好的證書,代碼以下:服務器
public class SSLUtil { /** * 默認信任全部的證書 * TODO 最好加上證書認證,主流App都有本身的證書 * * @return */ @SuppressLint("TrulyRandom") public static SSLSocketFactory createSSLSocketFactory() { SSLSocketFactory sSLSocketFactory = null; try { SSLContext sc = SSLContext.getInstance("TLS"); sc.init(null, new TrustManager[]{new TrustAllManager()}, new SecureRandom()); sSLSocketFactory = sc.getSocketFactory(); } catch (Exception e) { } return sSLSocketFactory; } public static class TrustAllManager implements X509TrustManager { @Override public void checkServerTrusted(X509Certificate[] chain, String authType) { } @Override public void checkClientTrusted(X509Certificate[] chain, String authType) { } @Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } } public static class TrustAllHostnameVerifier implements HostnameVerifier { @Override public boolean verify(String hostname, SSLSession session) { return true; } } }
最後https須要校驗個人domain是不是服務器提供的domain:session
public class TrueHostnameVerifier implements HostnameVerifier { public String domain; public TrueHostnameVerifier(String domain) { this.domain = domain; } public TrueHostnameVerifier() { } @Override public boolean verify(String hostname, SSLSession session) { if(TextUtils.isEmpty(domain)) { domain = ...; } return HttpsURLConnection.getDefaultHostnameVerifier().verify(domain, session); } }
以上代碼能夠直接拷貝,省略號代碼具體代碼可能須要你本身去實現。但願對你有所幫助。dom