React Native 请求与Https中SSL认证

文章目录

  • 1、概述
  • 2、问题概述
  • 3、解决思路
  • 4、总结体会
  • 5、参考资料

1、概述

首先先简单介绍一下SSL认证,HTTPS相当于HTTP的安全版本,而安全的基础就靠SSL认证实现,它的作用包括:

  • 认证用户和服务器,确保数据发送到正确的客户机和服务器;(验证证书)
  • 加密数据以防止数据中途被窃取;(加密)
  • 维护数据的完整性,确保数据在传输过程中不被改变。(摘要算法)

HTTPS的工作原理:

HTTPS在传输数据之前需要客户端(浏览器)与服务端(网站)之间进行一次握手,在握手过程中将确立双方加密传输数据的密码信息。握手过程的简单描述如下:

浏览器将自己支持的一套加密算法、HASH算法发送给网站。
网站从中选出一组加密算法与HASH算法,并将自己的身份信息以证书的形式发回给浏览器。证书里面包含了网站地址,加密公钥,以及证书的颁发机构等信息。
浏览器获得网站证书之后,开始验证证书的合法性,如果证书信任,则生成一串随机数字作为通讯过程中对称加密的秘钥。然后取出证书中的公钥,将这串数字以及HASH的结果进行加密,然后发给网站。
网站接收浏览器发来的数据之后,通过私钥进行解密,然后HASH校验,如果一致,则使用浏览器发来的数字串使加密一段握手消息发给浏览器。
浏览器解密,并HASH校验,没有问题,则握手结束。接下来的传输过程将由之前浏览器生成的随机密码并利用对称加密算法进行加密。

握手过程中如果有任何错误,都会使加密连接断开,从而阻止了隐私信息的传输

原文链接:https://blog.csdn.net/lmj623565791/article/details/48129405

简单来说,https服务器端会有一个证书,在交互过程中客户端需要去验证证书的合法性,大多数证书是由权威机构颁发的证书,okhttp默认情况下是支持https协议的网站的。可是还有一部分网站是自签名的网站,对于自签名证书,那么我们就需要去校验合法性了,也就是说我们只需要让OkhttpClient去信任这个证书就可以畅通的进行通信了

2、问题概述

在Beta冲刺的,我打开alpha冲刺的app,发现福大教务处的二维码突然间不显示了,我打开控制台运行程序,发现所有有关福大的接口都没法显示,全部请求都报一个错误:

Possible unhandled promise rejection (id:0: Network request failed)

我疯狂谷歌百度,有说ios的(我明明在写android),什么在js里加上了catch就好了(完全不明所以),最后网络上的主流方法我都试了一遍,都不对劲,于是我打算用抓包工具看看,请求究竟是怎么样的。结果意外发现,模拟器使用抓包代理之后,请求居然正常了,我开始怀疑是手机本身的某种限制,之后我经过多次测试终于发现,浏览器等都能正常访问福大接口,但是只有手机不行,使用POSTman这个问题会更加明显。

这是福大的验证码地址:https://jwcjwxt1.fzu.edu.cn/plus/verifycode.asp

将这个地址用postman请求,会发现postman报错(看到SSL验证的那一刻,我激动得快疯了,终于找到真正问题了!!!!)

React Native 请求与Https中SSL认证_第1张图片

关闭postman的SSL验证,会发现请求正常

React Native 请求与Https中SSL认证_第2张图片

因此确定了,只要搞定了手机的SSL请求问题,一切都了(谁知道后面跟下了地狱一样)

3、解决思路

我一开始是先使用python来进行福大教务处模拟爬取的,python的http库想要关闭SSL认证贼容易,加一句verify=False就行了,鬼知道想要在前端js代码上跳过SSL认证基本没可能(或许是我太菜了没看懂),因此我开始针对React Native 方向研究该问题。果然这个方面有很多人有需求,网上有很多种方法,基本方法都是去改写底层的okhttp机制,实现方法有很多,甚至还有改写依赖包里面的java代码重构整个底层项目来实现的,我全部都试验了但是最后全失败了,终于最后让我试出来一种:

步骤一:自定义OkHttpClient工厂类
步骤二:将自定义OkHttpClient工厂类放入OkHttpClientProvider中
步骤三:重新运行项目

此外还需要注意以下几点:

  • Android10以前跳过SSL证书和Android10以后是不一样的,具体的解决方案是把X509TrustManager的实现类换成X509ExtendedTrustManager实现类即可

  • 网络上给的代码大多都没有import,然后webstorm又没有java代码补全,最后我想了一个办法,把每一句未知变量都用百度查找代码,总会有使用过他们的代码,这样就可以凑齐他们的import命名了。

  • 代码的位置:我一开始被网上教程耍了,以为需要修改node_modules包里面的代码,结果找了半天,最后只需要把该文件放到和MainActivity.java同级的文件夹下面就可以了

    React Native 请求与Https中SSL认证_第3张图片

以下附上完整代码(全Android版本通用):

import com.facebook.react.modules.network.OkHttpClientFactory;
import com.facebook.react.modules.network.ReactCookieJarContainer;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.security.SecureRandom;
import okhttp3.OkHttpClient;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import javax.net.ssl.KeyManager;
public class CustomOkHttpClientFactory implements OkHttpClientFactory {

    static SSLSocketFactory sslSocketFactory;
    static X509TrustManager trustManager;
    static {
        trustManager = new X509TrustManager() {
            public void checkClientTrusted(X509Certificate[] chain, String authType) throws
                    CertificateException {
            }

            public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }

            public X509Certificate[] getAcceptedIssuers() {
                return new X509Certificate[0];
            }
        };
        TrustManager[] trustAllCerts = new TrustManager[]{trustManager};

        try {
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init((KeyManager[]) null, trustAllCerts, new SecureRandom());
            sslSocketFactory = sslContext.getSocketFactory();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public OkHttpClient createNewNetworkModuleClient() {
        OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder()
                .connectTimeout(0, TimeUnit.MILLISECONDS)
                .readTimeout(0, TimeUnit.MILLISECONDS)
                .writeTimeout(0, TimeUnit.MILLISECONDS)
                .cookieJar(new ReactCookieJarContainer());
        clientBuilder
        //.sslSocketFactory(sslSocketFactory)(android10及以下)
        .sslSocketFactory(sslSocketFactory,trustManager)
        .hostnameVerifier(new HostnameVerifier() {
            @Override
            public boolean verify(String s, SSLSession sslSession) {
                // 忽略所有的HTTPS认证,直接返回了true
                return true;
            }
        });
        return clientBuilder.build();
    }

}

4、总结体会

React Native小众不是没有理由的呀,太完蛋了,资料都贼难找,看上去就那么短的博客,我居然花了整整两天才完成,大部分时间都花在尝试解决方案,解决失败,再找个一个,再失败。。。。一直在半死不活之间死去活来。

5、参考资料

  • ReactNative如何忽略Https的证书验证
  • 当REACT NATIVE遇到HTTPS(ANDROID)
  • React-Native,Android端解决无法访问不受信任HTTPS证书服务器的问题
  • SSL 证书和 React Native Axios
  • Android Https相关完全解析 当OkHttp遇到https(Android)
  • Android 10及以上0khttp跳过SSL证书校验

你可能感兴趣的:(https,react,native,ssl)