React Native 请求与Https中SSL认证

221900330_詹鹏翔 学生 2022-06-22 07:20:02

目录

  • 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服务器端会有一个证书,在交互过程中客户端需要去验证证书的合法性,大多数证书是由权威机构颁发的证书,可是还有一部分网站是自签名的网站,对于自签名证书,那么我们就需要去校验合法性了,也就是说我们只需要让OkhttpClient去信任这个证书就可以畅通的进行通信了

技术的难点在于:该框架是使用React语言实现跨平台app开发,却需要去修改底层的android代码(这个情况并不少见),没有一定的android原生开发基础和java基础,将会非常懵逼

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验证的那一刻,我激动得快疯了,终于找到真正问题了!!!!)

image-20220622064627290

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

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

RN-android端原生层源码使用的网络模块是NetworkingModule.java
NetworkingModule使用的是OkHttpClient来实现HTTP通信交互的,NetworkingModule.java文件就是我们android最底层的网络请求工具类,每次创建NetworkingModule对象的时候,我们会传入一个okhttpclient的对象

okhttp默认情况下是支持https协议的网站的,但是面对自签名的网站似乎无法支持,此时就会报错:
Possible unhandled promise rejection (id:0: Network request failed)
这句报错很难发现根本的错误原因是SSL,很容易以为是网络的问题
因此要实现okhttp正常访问,必须重写okhttpclient类

流程图如下:

3、解决思路

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

  • 步骤一:自定义OkHttpClient工厂类(代码可看最下方)

    其中忽略HTTPS验证的关键代码:

        .sslSocketFactory(sslSocketFactory,trustManager)
        .hostnameVerifier(new HostnameVerifier() {
            @Override
            public boolean verify(String s, SSLSession sslSession) {
                // 忽略所有的HTTPS认证,直接返回了true
                return true;
            }
        });
    
  • 步骤二:将自定义OkHttpClient工厂类放入OkHttpClientProvider中

    在项目的MainApplication类的onCreate()生命周期方法加入如下代码:

    @Override
    public void onCreate() {
      super.onCreate();
        //......
       // 更改创建OkHttpClient的工厂类,解决无法连接自建HTTPS证书网站的问题
      OkHttpClientProvider.setOkHttpClientFactory(new CustomOkHttpClientFactory());
    }
    
  • 步骤三:重新运行项目

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

  • Android10以前跳过SSL证书和Android10以后是不一样的,具体的解决方案是把X509TrustManager的实现类换成X509ExtendedTrustManager实现类即可(需要在sslSocketFactory后面增加一个trustManager参数)

  •     clientBuilder
        //.sslSocketFactory(sslSocketFactory)(android10及以下)
        .sslSocketFactory(sslSocketFactory,trustManager)
    
  • 网络上给的代码大多都没有import,然后webstorm又没有java代码补全,最后我想了一个办法,把每一句未知变量都用百度查找代码,总会有使用过他们的代码,这样就可以凑齐他们的import命名了,别忘记写包名。

    package com.fzustudyroom;
    /**
     * 自定义OkHttpClient工厂类
     */
    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;
    
  • 代码的位置:我一开始被网上教程耍了,以为需要修改node_modules包里面的代码,结果找了半天,差点修改底层源码去了,最后只需要把该文件放到和MainActivity.java同级的文件夹下面就可以了

以下附上完整代码(全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、参考资料

...全文
767 回复 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

142

社区成员

发帖
与我相关
我的任务
社区描述
2022年福大-软件工程;软件工程实践-W班
软件工程 高校
社区管理员
  • FZU_SE_teacherW
  • 丝雨_xrc
  • Lyu-
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧