最近在用OkHttp调https接口的时候遇到一个问题
javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated.
这是由于https请求证书验证问题,直接请求一般都会失败。一般是做证书验证处理或者忽略证书验证。
可是给我https接口的小伙伴说,他们都没使用证书加密。我:(黑人???)
那就只能忽略证书验证咯。
于是我就打断点一步步看是哪里验证证书,于是找到一个属于OkHttpClient的一个对象hostnameVerifier的一个叫verify的方法,关于HostnameVerifier是一个接口,具体可以看:Interface HostnameVerifier OkHttpClient是使用OkHostnameVerifier这个实现类的verify方法,如下:
@Override
public boolean verify(String host, SSLSession session) {
try {
Certificate[] certificates = session.getPeerCertificates();
return verify(host, (X509Certificate) certificates[0]);
} catch (SSLException e) {
return false;
}
}
public boolean verify(String host, X509Certificate certificate) {
return verifyAsIpAddress(host)
? verifyIpAddress(host, certificate)
: verifyHostname(host, certificate);
}
具体就是验证证书,然后成功返回true,失败返回false。就是这里返回了false到导致我们出错。
那咋办?
前面说到hostnameVerifier是属于OkHttpClient的一个对象,那我们新建一个HostnameVerifier实现类,并重写verify的方法,强制让他返回true,不就可以验证通过了么?OK,stackoverflow上的答案也是这么说的,看看人家给出的答案:
okHttpClient.setHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
于是我也这么干呗,结果发现我的okHttpClient没有setHostnameVerifier这个方法!我脑海里就黑人???
便进去OkHttpClient去看到底是咋回事
public class OkHttpClient implements Cloneable, Call.Factory {
/**
* final的 不能更改
*/
final HostnameVerifier hostnameVerifier;
/**
* 私有的构造函数,即不能通过构造函数去给实例化hostnameVerifier
*/
private OkHttpClient(Builder builder) {
this.hostnameVerifier = builder.hostnameVerifier;
}
public HostnameVerifier hostnameVerifier() {
return hostnameVerifier;
}
/**
* 这个是关键,下面会用到
*/
public Builder newBuilder() {
return new Builder(this);
}
/**
* 内部类
*/
public static final class Builder {
HostnameVerifier hostnameVerifier;
/**
* 这里就是前面说到的OkHostnameVerifier
*/
public Builder() {
hostnameVerifier = OkHostnameVerifier.INSTANCE;
}
public Builder hostnameVerifier(HostnameVerifier hostnameVerifier) {
if (hostnameVerifier == null) throw new NullPointerException("hostnameVerifier == null");
this.hostnameVerifier = hostnameVerifier;
return this;
}
/**
* 关键 通过Builder 新建一个OkHttpClient对象
*/
public OkHttpClient build() {
return new OkHttpClient(this);
}
}
}
以上的代码我只贴出和hostnameVerifier 有关的,真没有setHostnameVerifier 的方法···
于是就只能分析源码来找突破口了:
- 我们看到OkHttpClient 的hostnameVerifier 是通过Builder 的hostnameVerifier 来赋值的
- 关键是,OkHttpClient 有个public 的产生Builder 的方法:newBuilder
- 更关键是,Builder 这个内部类还有个可以获得他的hostnameVerifier 的公共方法!
- 我们就可以新建一个HostnameVerifier 类重写verify!
- 再用Builder 的build 方法新建一个OkHttpClient 对象!
完美,就这么干!
OkHttpClient client = new OkHttpClient().newBuilder().hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
//强行返回true 即验证成功
return true;
}
}).build();
OK!成功调通!