OkHttp调用HTTPS遇到的问题之:SSLPeerUnverifiedException

最近在用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 的方法···

于是就只能分析源码来找突破口了:

  1. 我们看到OkHttpClient 的hostnameVerifier 是通过Builder 的hostnameVerifier 来赋值的
  2. 关键是,OkHttpClient 有个public 的产生Builder 的方法:newBuilder
  3. 更关键是,Builder 这个内部类还有个可以获得他的hostnameVerifier 的公共方法!
  4. 我们就可以新建一个HostnameVerifier 类重写verify!
  5. 再用Builder 的build 方法新建一个OkHttpClient 对象!

完美,就这么干!

OkHttpClient client = new OkHttpClient().newBuilder().hostnameVerifier(new HostnameVerifier() {

    @Override
    public boolean verify(String hostname, SSLSession session) {
        //强行返回true 即验证成功
        return true;
    }
}).build();

OK!成功调通!

猜你喜欢

转载自blog.csdn.net/weixin_37569048/article/details/81201834