https和httpDNS的优缺点我就不说了,网上一搜一大把,但是总体来说还是优点大于缺点的,否则也不用非那么大功夫折腾这儿玩意儿了。
下边我就主要说一下我们最近引入https+httpDNS遇到的一些坑。

###方案探索:

####1.Request执行前替换域名为ip地址,并设置请求投的host
此方案具体实现起来可以自己替换Request,如果使用okhttp也可以直接通过拦截器来处理。
这种方案实现起来很简单,没有任何技术性的问题,只需要发送前做一个处理就行。 这种方案单独使用https或者单独使用httpDNS没有任何问题,但是一旦两者结合起来就产生了问题。主要的问题是SNI的问题。
SNI(Server Name Indication)是为了解决一个服务器使用多个域名和证书的SSL/TLS扩展。 一句话简述它的工作原理就是,在连接到服务器建立SSL连接之前先发送要访问站点的域名(Hostname), 这样服务器根据这个域名返回一个合适的证书。
由于是通过IP进行访问的,服务如果IP服务器上设置了多个域名多个证书,那么简历ssl连接时服务器不知道你需要哪个证书,所有有可能就会返回的是域名的根证书或者错误的证书,这时候我们客户端或者前端就会发生域名证书不匹配的问题,直接导致了连接失败。
SNI的解决方案: ssl连接时通过域名获取证书,已保证获取有效的证书。详细解决方案可以看一下阿里提供的方案:单IP多HTTPS域名场景下的解决方案
但是这种方案对okhttp3来说实现起来可能不太有好有,不太容易实现。而悲催的是我们正好实用的是okhttp的网络库,一次不得不寻求其他的方案了。

####2.okhttp 自建dns解析方案
OkHttp其实暴露了一个Dns接口,默认的实现是使用系统的方法发送udp请求进行dns解析。于是,我们就可以实现一个Dns接口,解析的方式使用httpdns,将解析结果返回,接口实现之后将系统默认的Dns接口替换成我们的Dns接口。
优点:
a.这样做相当于还是用域名进行访问,只不过底层的dns解析换成了http协议。也就是和之前系统的dns解析没有差别,但是得保证httpdns返回的ip是正确的。
b. https下不会存在任何问题,证书校验依然使用域名进行校验cookie的问题也自然不存在。
缺点:
a.容灾不好做,除非强制关闭Httpdns。服务器返回的ip如果不正确,这次请求就挂了,甚至下次也可能挂了。OkHttp默认对解析结果有一定时间的缓存,万一ttl过期了, okhttp可能依然会去使用,这时候也是有风险的。
b.代理失效

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class HttpDns implements Dns {
private static final Dns SYSTEM = Dns.SYSTEM;

@Override
public List<InetAddress> lookup(String hostname) throws UnknownHostException {
Log.e("HttpDns", "lookup:" + hostname);
String ip = DNSHelper.getIpByHost(hostname);
if (ip != null && !ip.equals("")) {
List<InetAddress> inetAddresses = Arrays.asList(InetAddress.getAllByName(ip));
Log.e("HttpDns", "inetAddresses:" + inetAddresses);
return inetAddresses; //返回自己解析的地址列表
}
return SYSTEM.lookup(hostname); //走系统的dns列表
}
}
//使用
OkHttpClient client = new OkHttpClient.Builder()
.dns(new HttpDns())
.build();