當React Native遇到https(Android)

當React Native遇到https(Android),公司來瞭幾套測試環境,然後都是需要加證書訪問的,這就尷尬瞭,我們默認的網絡請求用的是fetch,所以肯定是需要去修改native源碼的,這裡就以android為例子瞭(ios我也不懂~~嘿嘿)。

這裡我們以rn的”0.43.4“版本為例子,
其實rn已經為我們提供瞭一個修改默認okhttpclient的方法,
我們找到這個一個文件:

這裡寫圖片描述

NetworkingModule.java文件就是我們android最底層的網絡請求工具類,每次創建NetworkingModule對象的時候,我們會傳入一個okhttpclient的對象:

 public NetworkingModule(ReactApplicationContext context) {
        this(context, (String)null, OkHttpClientProvider.createClient(), (List)null);
    }

    public NetworkingModule(ReactApplicationContext context, List networkInterceptorCreators) {
        this(context, (String)null, OkHttpClientProvider.createClient(), networkInterceptorCreators);
    }

    public NetworkingModule(ReactApplicationContext context, String defaultUserAgent) {
        this(context, defaultUserAgent, OkHttpClientProvider.createClient(), (List)null);
    }

可以看到,每次都是通過:

OkHttpClientProvider.createClient()

的方式創建瞭一個okhttpclient對象,最後發送請求為:

 @ReactMethod
    public void sendRequest(ExecutorToken executorToken, String method, String url, final int requestId, ReadableArray headers, ReadableMap data, final String responseType, final boolean useIncrementalUpdates, int timeout) {
        okhttp3.Request.Builder requestBuilder = (new okhttp3.Request.Builder()).url(url);
        if(requestId != 0) {
            requestBuilder.tag(Integer.valueOf(requestId));
        }

        final RCTDeviceEventEmitter eventEmitter = this.getEventEmitter(executorToken);
        Builder clientBuilder = this.mClient.newBuilder();
        ....

所以我們隻需要替換掉默認的httpclient,然後為其添加上證書認證就可以瞭,我們打開 OkHttpClientProvider.createClient()方法:

   public static OkHttpClient createClient() {
        Builder client = (new Builder()).connectTimeout(0L, TimeUnit.MILLISECONDS).readTimeout(0L, TimeUnit.MILLISECONDS).writeTimeout(0L, TimeUnit.MILLISECONDS).cookieJar(new ReactCookieJarContainer());
        return enableTls12OnPreLollipop(client).build();
    }

OkHttpClientProvider這個工具類就是為我們提供瞭一個修改默認okhttpclient的方法,我們看到其中有一個方法:

 public static void replaceOkHttpClient(OkHttpClient client) {
        sClient = client;
    }

這個方法就是替換掉rn中網絡請求默認的okhttpclient方法。

所以我們在程序初始化的時候改掉默認的okhttpclient。。

1、首先找到你項目的application文件,在oncreate方法中提供其方法:

 @Override
    public void onCreate() {
        。。。
        //RN OKHTTP添加https證書
        OkHttpClientProvider.replaceOkHttpClient(initCustomOkHttpClient());
        。。。
    }
public OkHttpClient initCustomOkHttpClient() {
        OkHttpClient.Builder client = new OkHttpClient.Builder()
                .connectTimeout(30 * 1000, TimeUnit.MILLISECONDS)
                .readTimeout(30 * 1000, TimeUnit.MILLISECONDS)
                .writeTimeout(30 * 1000, TimeUnit.MILLISECONDS)
                .cookieJar(new ReactCookieJarContainer());
        client.addNetworkInterceptor(new StethoInterceptor());
        client.addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY));
        try {
        //你的證書文件,放在android的assets文件夾下
            setCertificates(client, getAssets().open("CA.crt"));
            client.hostnameVerifier(new HostnameVerifier() {
                @Override
                public boolean verify(String hostname, SSLSession session) {
                    return true;
                }
            });
        } catch (IOException e) {
            e.printStackTrace();
        }
        OkHttpClient.Builder builder = OkHttpClientProvider.enableTls12OnPreLollipop(client);
        return builder.build();
    }
public void setCertificates(OkHttpClient.Builder client, InputStream... certificates) {
        try {
            CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
            KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
            keyStore.load(null);
            int index = 0;
            for (InputStream certificate : certificates) {
                String certificateAlias = Integer.toString(index++);
                keyStore.setCertificateEntry(certificateAlias, certificateFactory.generateCertificate(certificate));

                try {
                    if (certificate != null)
                        certificate.close();
                } catch (IOException e) {
                }
            }

            SSLContext sslContext = SSLContext.getInstance("TLS");

            TrustManagerFactory trustManagerFactory =
                    TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());

            trustManagerFactory.init(keyStore);
            sslContext.init(
                    null,
                    trustManagerFactory.getTrustManagers(),
                    new SecureRandom()
            );
            client.sslSocketFactory(sslContext.getSocketFactory());

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

2、最後一步

修改NetworkingModule.java文件

public NetworkingModule(ReactApplicationContext context) {
        this(context, (String)null, OkHttpClientProvider.createClient(), (List)null);
    }

    public NetworkingModule(ReactApplicationContext context, List networkInterceptorCreators) {
        this(context, (String)null, OkHttpClientProvider.createClient(), networkInterceptorCreators);
    }

    public NetworkingModule(ReactApplicationContext context, String defaultUserAgent) {
        this(context, defaultUserAgent, OkHttpClientProvider.createClient(), (List)null);
    }

把默認創建okhttpclient的方式:

OkHttpClientProvider.createClient()

全部改為:

OkHttpClientProvider.getOkHttpClient()
  /**
   * @param context the ReactContext of the application
   */
  public NetworkingModule(final ReactApplicationContext context) {
    this(context, null, OkHttpClientProvider.getOkHttpClient(), null);
  }

  /**
   * @param context the ReactContext of the application
   * @param networkInterceptorCreators list of {@link NetworkInterceptorCreator}'s whose create()
   * methods would be called to attach the interceptors to the client.
   */
  public NetworkingModule(
    ReactApplicationContext context,
    List networkInterceptorCreators) {
    this(context, null, OkHttpClientProvider.getOkHttpClient(), networkInterceptorCreators);
  }

  /**
   * @param context the ReactContext of the application
   * @param defaultUserAgent the User-Agent header that will be set for all requests where the
   * caller does not provide one explicitly
   */
  public NetworkingModule(ReactApplicationContext context, String defaultUserAgent) {
    this(context, defaultUserAgent, OkHttpClientProvider.getOkHttpClient(), null);
  }

好啦~到這裡小夥伴疑問瞭,我是導的react-native的第三方依賴庫,改不瞭源碼咋辦呢?

哈哈~~ 我們直接copy一份NetworkingModule跟mainreactpackage的代碼,然後在application中把mainreactpackage替換成我們copy出去的那一份,

 private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
        @Override
        public boolean getUseDeveloperSupport() {
            return BuildConfig.DEBUG;
        }

        @Override
        protected List getPackages() {
            return Arrays.asList(
            //替換成我們copy的那個文件
                    new MainReactPackage(),
                    new VectorIconsPackage(),
                    new RCTSplashScreenPackage(),
                    new RNDeviceInfo(),
                    new AndroidModulePackage(),
                    new RCTSwipeRefreshLayoutPackage()
            );
        }
    };

然後點進我們copy的那個MainReactPackage文件,再把MainReactPackage中的:

new ModuleSpec(NetworkingModule.class, new Provider() {
        @Override
        public NativeModule get() {
        //替換成我們自己copy的那個文件
          return new NetworkingModule(context);
        }
      }),

好啦~~ 我不知道其它rn版本中有沒有這個bug,如果NetworkingModule創建okhttpclient的方式之久就是:

OkHttpClientProvider.getOkHttpClient()

這種方式的話,我們就沒必要這麼麻煩瞭~~

好啦!! 也沒有啥技術含量的東西,就先到這裡瞭,小夥伴如果改瞭還是不行歡迎聯系我~~

You May Also Like