RN集成自签名https及双向认证-android(2)

上一篇文章讲了ios的这里接着讲RN-android-自签名https的配置,首先android比较复杂,首先用AS打开android工程

android有4个地方得改

  1. fetch (.jar包里的代码不能直接改)
  2. webview (.jar包里的代码不能直接改)
  3. Image (.jar包里的代码不能直接改--)
  4. react-native-fetch-blob(可以直接改源码-RNFetchBlobReq
    RNFetchBlob 这两个类)
  5. 如果使用了react-native-navigation时,在我们copy出MainReactPackage使用时会报错,注释掉react-native-navigation报错的地方就行

注意:RN的不同版本,会有不同的依赖问题,得根据报错一个一个改,很麻烦

fetch

  • 修改路径和原理
    安卓三方的依赖库在这可以看到


    RN集成自签名https及双向认证-android(2)_第1张图片
    AADFC784-0EDE-4B65-8633-7577568F4294.png
RN集成自签名https及双向认证-android(2)_第2张图片
17A28174-6D4C-4C0C-9773-FA77D93D2C0F.png

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());
      ...
    }

//自定义初始化client方法-
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();
        }
    }

  1. 修改NetworkingModule.java文件的以下方法,把默认创建okhttpclient的方式:OkHttpClientProvider.createClient(),全部改为:OkHttpClientProvider.getOkHttpClient()
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);
    }

如下:

  /**
   * @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);
        }
      }),

MainReactPackage路径.png
  • Image部分


    RN集成自签名https及双向认证-android(2)_第3张图片
    android_Image路径.png
RN集成自签名https及双向认证-android(2)_第4张图片
android图片https修改的地方.png

但是RN对应的那一部分.jar包是不可改的,,主要的问题还是怎么去改动代码

摘要 :

如何改动android.jar 包rn_androi_https

RN集成自签名https及双向认证-android(2)_第5张图片
https需要复制出来的文件.png

MainReactPackage路径.png

  • webView
    • ReactWebviewManager.java
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
* 

* This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. */ package petrochina.cplh.qqgl2.htttps; import android.content.ActivityNotFoundException; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.Picture; import android.net.Uri; import android.os.Build; import android.text.TextUtils; import android.util.Log; import android.view.ViewGroup.LayoutParams; import android.webkit.ConsoleMessage; import android.webkit.CookieManager; import android.webkit.GeolocationPermissions; import android.webkit.JavascriptInterface; import android.webkit.ValueCallback; import android.webkit.WebChromeClient; import android.webkit.WebResourceResponse; import android.webkit.WebSettings; import android.webkit.WebView; import android.webkit.WebViewClient; import com.facebook.common.logging.FLog; import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.LifecycleEventListener; import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.ReadableMapKeySetIterator; import com.facebook.react.bridge.WritableMap; import com.facebook.react.common.MapBuilder; import com.facebook.react.common.ReactConstants; import com.facebook.react.common.build.ReactBuildConfig; import com.facebook.react.module.annotations.ReactModule; import com.facebook.react.uimanager.SimpleViewManager; import com.facebook.react.uimanager.ThemedReactContext; import com.facebook.react.uimanager.UIManagerModule; import com.facebook.react.uimanager.annotations.ReactProp; import com.facebook.react.uimanager.events.ContentSizeChangeEvent; import com.facebook.react.uimanager.events.Event; import com.facebook.react.uimanager.events.EventDispatcher; import com.facebook.react.views.webview.WebViewConfig; import com.facebook.react.views.webview.events.TopLoadingErrorEvent; import com.facebook.react.views.webview.events.TopLoadingFinishEvent; import com.facebook.react.views.webview.events.TopLoadingStartEvent; import com.facebook.react.views.webview.events.TopMessageEvent; import org.json.JSONException; import org.json.JSONObject; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.net.URL; import java.security.KeyStore; import java.security.cert.CertificateFactory; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import javax.annotation.Nullable; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSession; import javax.net.ssl.TrustManagerFactory; /** * Manages instances of {@link WebView} *

* Can accept following commands: * - GO_BACK * - GO_FORWARD * - RELOAD *

* {@link WebView} instances could emit following direct events: * - topLoadingFinish * - topLoadingStart * - topLoadingError *

* Each event will carry the following properties: * - target - view's react tag * - url - url set for the webview * - loading - whether webview is in a loading state * - title - title of the current page * - canGoBack - boolean, whether there is anything on a history stack to go back * - canGoForward - boolean, whether it is possible to request GO_FORWARD command */ @ReactModule(name = ReactWebViewManager.REACT_CLASS) public class ReactWebViewManager extends SimpleViewManager { protected static final String REACT_CLASS = "RCTWebView"; protected static final String HTML_ENCODING = "UTF-8"; protected static final String HTML_MIME_TYPE = "text/html"; protected static final String BRIDGE_NAME = "__REACT_WEB_VIEW_BRIDGE"; protected static final String HTTP_METHOD_POST = "POST"; public static final int COMMAND_GO_BACK = 1; public static final int COMMAND_GO_FORWARD = 2; public static final int COMMAND_RELOAD = 3; public static final int COMMAND_STOP_LOADING = 4; public static final int COMMAND_POST_MESSAGE = 5; public static final int COMMAND_INJECT_JAVASCRIPT = 6; // Use `webView.loadUrl("about:blank")` to reliably reset the view // state and release page resources (including any running JavaScript). protected static final String BLANK_URL = "about:blank"; protected WebViewConfig mWebViewConfig; protected @Nullable WebView.PictureListener mPictureListener; private static ThemedReactContext reactContext; protected static class ReactWebViewClient extends WebViewClient { // ---https 双向认证------ private SSLContext sslContext; public ReactWebViewClient() { // 添加https证书 try { InputStream is = reactContext.getAssets().open("CNPCCA.cer"); NetConfig.addCertificate(is); // 这里将证书读取出来,,放在配置中byte[]里 } catch (IOException ioe) { ioe.printStackTrace(); } // 添加证书cer List certificates = new ArrayList<>(); List certs_data = NetConfig.getCertificatesData(); // 将字节数组转为数组输入流 if (certs_data != null && !certs_data.isEmpty()) { for (byte[] bytes : certs_data) { certificates.add(new ByteArrayInputStream(bytes)); } } try { prepareSslPinning(certificates); } catch (IOException e) { e.printStackTrace(); } } @Override public WebResourceResponse shouldInterceptRequest(final WebView view, String url) { Log.i("shouldInterceptRequest", "shouldInterceptRequest1"); if (url.indexOf("https") != -1) { return processRequest(url); } else { return null; } } private WebResourceResponse processRequest(String webUrl) { // LogUtils.i("SSL_PINNING_WEBVIEWS", "GET: " + webUrl.toString()); try { // Setup connection URL url = new URL(webUrl); HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection(); // Set SSL Socket Factory for this request urlConnection.setSSLSocketFactory(sslContext.getSocketFactory()); urlConnection.setHostnameVerifier(new HostnameVerifier() { @Override public boolean verify(String hostname, SSLSession session) { return true; } });//很重要,校验证书 // Get content, contentType and encoding InputStream is = urlConnection.getInputStream(); String contentType = urlConnection.getContentType(); String encoding = urlConnection.getContentEncoding(); // If got a contentType header if (contentType != null) { String mimeType = contentType; // Parse mime type from contenttype string if (contentType.contains(";")) { mimeType = contentType.split(";")[0].trim(); } // LogUtils.i("SSL_PINNING_WEBVIEWS", "Mime: " + mimeType); // Return the response return new WebResourceResponse(mimeType, encoding, is); } } catch (Exception e) { e.printStackTrace(); // LogUtils.i("SSL_PINNING_WEBVIEWS", e.getLocalizedMessage()); } // Return empty response for this request return new WebResourceResponse(null, null, null); } private void prepareSslPinning(List certificates) throws IOException { try { // 服务器端需要验证的客户端证书,其实就是客户端的keystore KeyManagerFactory keyManagerFactory = KeyManagerFactory .getInstance("X509"); TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("X509"); KeyStore keyStore = KeyStore.getInstance("PKCS12"); KeyStore keyStore2 = KeyStore.getInstance(KeyStore.getDefaultType()); //读取证书 InputStream ksIn = reactContext.getResources().getAssets().open("client.p12");//***:你的p12证书 //加载证书 keyStore2.load(null); keyStore.load(ksIn, "cplh123456".toCharArray());//***:p12证书的密码,必须 ksIn.close(); CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); try { for (int i = 0, size = certificates.size(); i < size; ) { InputStream certificate = certificates.get(i); String certificateAlias = Integer.toString(i++); keyStore2.setCertificateEntry(certificateAlias, certificateFactory.generateCertificate(certificate)); if (certificate != null) certificate.close(); } } catch (IOException e) { e.printStackTrace(); } sslContext = SSLContext.getInstance("TLS"); // TrustManagerFactory trustManagerFactory = // // TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); keyManagerFactory.init(keyStore, "cplh123456".toCharArray()); trustManagerFactory.init(keyStore2); sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null); } catch (Exception e) { e.printStackTrace(); } } // ---https 双向认证------ protected boolean mLastLoadFailed = false; protected @Nullable ReadableArray mUrlPrefixesForDefaultIntent; @Override public void onPageFinished(WebView webView, String url) { super.onPageFinished(webView, url); if (!mLastLoadFailed) { ReactWebView reactWebView = (ReactWebView) webView; reactWebView.callInjectedJavaScript(); reactWebView.linkBridge(); emitFinishEvent(webView, url); } } @Override public void onPageStarted(WebView webView, String url, Bitmap favicon) { super.onPageStarted(webView, url, favicon); mLastLoadFailed = false; dispatchEvent( webView, new TopLoadingStartEvent( webView.getId(), createWebViewEvent(webView, url))); } @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { boolean useDefaultIntent = false; if (mUrlPrefixesForDefaultIntent != null && mUrlPrefixesForDefaultIntent.size() > 0) { ArrayList urlPrefixesForDefaultIntent = mUrlPrefixesForDefaultIntent.toArrayList(); for (Object urlPrefix : urlPrefixesForDefaultIntent) { if (url.startsWith((String) urlPrefix)) { useDefaultIntent = true; break; } } } if (!useDefaultIntent && (url.startsWith("http://") || url.startsWith("https://") || url.startsWith("file://") || url.equals("about:blank"))) { return false; } else { try { Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); view.getContext().startActivity(intent); } catch (ActivityNotFoundException e) { FLog.w(ReactConstants.TAG, "activity not found to handle uri scheme for: " + url, e); } return true; } } @Override public void onReceivedError( WebView webView, int errorCode, String description, String failingUrl) { super.onReceivedError(webView, errorCode, description, failingUrl); mLastLoadFailed = true; // In case of an error JS side expect to get a finish event first, and then get an error event // Android WebView does it in the opposite way, so we need to simulate that behavior emitFinishEvent(webView, failingUrl); WritableMap eventData = createWebViewEvent(webView, failingUrl); eventData.putDouble("code", errorCode); eventData.putString("description", description); dispatchEvent( webView, new TopLoadingErrorEvent(webView.getId(), eventData)); } protected void emitFinishEvent(WebView webView, String url) { dispatchEvent( webView, new TopLoadingFinishEvent( webView.getId(), createWebViewEvent(webView, url))); } protected WritableMap createWebViewEvent(WebView webView, String url) { WritableMap event = Arguments.createMap(); event.putDouble("target", webView.getId()); // Don't use webView.getUrl() here, the URL isn't updated to the new value yet in callbacks // like onPageFinished event.putString("url", url); event.putBoolean("loading", !mLastLoadFailed && webView.getProgress() != 100); event.putString("title", webView.getTitle()); event.putBoolean("canGoBack", webView.canGoBack()); event.putBoolean("canGoForward", webView.canGoForward()); return event; } public void setUrlPrefixesForDefaultIntent(ReadableArray specialUrls) { mUrlPrefixesForDefaultIntent = specialUrls; } } /** * Subclass of {@link WebView} that implements {@link LifecycleEventListener} interface in order * to call {@link WebView#destroy} on activity destroy event and also to clear the client */ protected static class ReactWebView extends WebView implements LifecycleEventListener { protected @Nullable String injectedJS; protected boolean messagingEnabled = false; protected @Nullable ReactWebViewClient mReactWebViewClient; protected class ReactWebViewBridge { ReactWebView mContext; ReactWebViewBridge(ReactWebView c) { mContext = c; } @JavascriptInterface public void postMessage(String message) { mContext.onMessage(message); } } /** * WebView must be created with an context of the current activity *

* Activity Context is required for creation of dialogs internally by WebView * Reactive Native needed for access to ReactNative internal system functionality */ public ReactWebView(ThemedReactContext reactContext) { super(reactContext); } @Override public void onHostResume() { // do nothing } @Override public void onHostPause() { // do nothing } @Override public void onHostDestroy() { cleanupCallbacksAndDestroy(); } @Override public void setWebViewClient(WebViewClient client) { super.setWebViewClient(client); mReactWebViewClient = (ReactWebViewClient) client; } public @Nullable ReactWebViewClient getReactWebViewClient() { return mReactWebViewClient; } public void setInjectedJavaScript(@Nullable String js) { injectedJS = js; } protected ReactWebViewBridge createReactWebViewBridge(ReactWebView webView) { return new ReactWebViewBridge(webView); } public void setMessagingEnabled(boolean enabled) { if (messagingEnabled == enabled) { return; } messagingEnabled = enabled; if (enabled) { addJavascriptInterface(createReactWebViewBridge(this), BRIDGE_NAME); linkBridge(); } else { removeJavascriptInterface(BRIDGE_NAME); } } public void callInjectedJavaScript() { if (getSettings().getJavaScriptEnabled() && injectedJS != null && !TextUtils.isEmpty(injectedJS)) { loadUrl("javascript:(function() {\n" + injectedJS + ";\n})();"); } } public void linkBridge() { if (messagingEnabled) { if (ReactBuildConfig.DEBUG && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { // See isNative in lodash String testPostMessageNative = "String(window.postMessage) === String(Object.hasOwnProperty).replace('hasOwnProperty', 'postMessage')"; evaluateJavascript(testPostMessageNative, new ValueCallback() { @Override public void onReceiveValue(String value) { if (value.equals("true")) { FLog.w(ReactConstants.TAG, "Setting onMessage on a WebView overrides existing values of window.postMessage, but a previous value was defined"); } } }); } loadUrl("javascript:(" + "window.originalPostMessage = window.postMessage," + "window.postMessage = function(data) {" + BRIDGE_NAME + ".postMessage(String(data));" + "}" + ")"); } } public void onMessage(String message) { dispatchEvent(this, new TopMessageEvent(this.getId(), message)); } protected void cleanupCallbacksAndDestroy() { setWebViewClient(null); destroy(); } } public ReactWebViewManager() { mWebViewConfig = new WebViewConfig() { public void configWebView(WebView webView) { } }; } public ReactWebViewManager(WebViewConfig webViewConfig) { mWebViewConfig = webViewConfig; } @Override public String getName() { return REACT_CLASS; } protected ReactWebView createReactWebViewInstance(ThemedReactContext reactContext) { return new ReactWebView(reactContext); } @Override protected WebView createViewInstance(ThemedReactContext reactContext) { ReactWebView webView = createReactWebViewInstance(reactContext); webView.setWebChromeClient(new WebChromeClient() { @Override public boolean onConsoleMessage(ConsoleMessage message) { if (ReactBuildConfig.DEBUG) { return super.onConsoleMessage(message); } // Ignore console logs in non debug builds. return true; } @Override public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) { callback.invoke(origin, true, false); } }); reactContext.addLifecycleEventListener(webView); mWebViewConfig.configWebView(webView); webView.getSettings().setBuiltInZoomControls(true); webView.getSettings().setDisplayZoomControls(false); webView.getSettings().setDomStorageEnabled(true); webView.getSettings().setAllowUniversalAccessFromFileURLs(true); webView.getSettings().setAllowFileAccess(true); webView.getSettings().setAllowFileAccessFromFileURLs(true); webView.getSettings().setJavaScriptEnabled(true); webView.getSettings().setBlockNetworkImage(false); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { webView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); } // Fixes broken full-screen modals/galleries due to body height being 0. webView.setLayoutParams( new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); if (ReactBuildConfig.DEBUG && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { WebView.setWebContentsDebuggingEnabled(true); } return webView; } @ReactProp(name = "javaScriptEnabled") public void setJavaScriptEnabled(WebView view, boolean enabled) { view.getSettings().setJavaScriptEnabled(enabled); } @ReactProp(name = "thirdPartyCookiesEnabled") public void setThirdPartyCookiesEnabled(WebView view, boolean enabled) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { CookieManager.getInstance().setAcceptThirdPartyCookies(view, enabled); } } @ReactProp(name = "scalesPageToFit") public void setScalesPageToFit(WebView view, boolean enabled) { view.getSettings().setUseWideViewPort(!enabled); } @ReactProp(name = "domStorageEnabled") public void setDomStorageEnabled(WebView view, boolean enabled) { view.getSettings().setDomStorageEnabled(enabled); } @ReactProp(name = "userAgent") public void setUserAgent(WebView view, @Nullable String userAgent) { if (userAgent != null) { // TODO(8496850): Fix incorrect behavior when property is unset (uA == null) view.getSettings().setUserAgentString(userAgent); } } @ReactProp(name = "mediaPlaybackRequiresUserAction") public void setMediaPlaybackRequiresUserAction(WebView view, boolean requires) { view.getSettings().setMediaPlaybackRequiresUserGesture(requires); } @ReactProp(name = "allowUniversalAccessFromFileURLs") public void setAllowUniversalAccessFromFileURLs(WebView view, boolean allow) { view.getSettings().setAllowUniversalAccessFromFileURLs(allow); } @ReactProp(name = "saveFormDataDisabled") public void setSaveFormDataDisabled(WebView view, boolean disable) { view.getSettings().setSaveFormData(!disable); } @ReactProp(name = "injectedJavaScript") public void setInjectedJavaScript(WebView view, @Nullable String injectedJavaScript) { ((ReactWebView) view).setInjectedJavaScript(injectedJavaScript); } @ReactProp(name = "messagingEnabled") public void setMessagingEnabled(WebView view, boolean enabled) { ((ReactWebView) view).setMessagingEnabled(enabled); } @ReactProp(name = "source") public void setSource(WebView view, @Nullable ReadableMap source) { if (source != null) { if (source.hasKey("html")) { String html = source.getString("html"); if (source.hasKey("baseUrl")) { view.loadDataWithBaseURL( source.getString("baseUrl"), html, HTML_MIME_TYPE, HTML_ENCODING, null); } else { view.loadData(html, HTML_MIME_TYPE, HTML_ENCODING); } return; } if (source.hasKey("uri")) { String url = source.getString("uri"); String previousUrl = view.getUrl(); if (previousUrl != null && previousUrl.equals(url)) { return; } if (source.hasKey("method")) { String method = source.getString("method"); if (method.equals(HTTP_METHOD_POST)) { byte[] postData = null; if (source.hasKey("body")) { String body = source.getString("body"); try { postData = body.getBytes("UTF-8"); } catch (UnsupportedEncodingException e) { postData = body.getBytes(); } } if (postData == null) { postData = new byte[0]; } view.postUrl(url, postData); return; } } HashMap headerMap = new HashMap<>(); if (source.hasKey("headers")) { ReadableMap headers = source.getMap("headers"); ReadableMapKeySetIterator iter = headers.keySetIterator(); while (iter.hasNextKey()) { String key = iter.nextKey(); if ("user-agent".equals(key.toLowerCase(Locale.ENGLISH))) { if (view.getSettings() != null) { view.getSettings().setUserAgentString(headers.getString(key)); } } else { headerMap.put(key, headers.getString(key)); } } } view.loadUrl(url, headerMap); return; } } view.loadUrl(BLANK_URL); } @ReactProp(name = "onContentSizeChange") public void setOnContentSizeChange(WebView view, boolean sendContentSizeChangeEvents) { if (sendContentSizeChangeEvents) { view.setPictureListener(getPictureListener()); } else { view.setPictureListener(null); } } @ReactProp(name = "mixedContentMode") public void setMixedContentMode(WebView view, @Nullable String mixedContentMode) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { if (mixedContentMode == null || "never".equals(mixedContentMode)) { view.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_NEVER_ALLOW); } else if ("always".equals(mixedContentMode)) { view.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); } else if ("compatibility".equals(mixedContentMode)) { view.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE); } } } @ReactProp(name = "urlPrefixesForDefaultIntent") public void setUrlPrefixesForDefaultIntent( WebView view, @Nullable ReadableArray urlPrefixesForDefaultIntent) { ReactWebViewClient client = ((ReactWebView) view).getReactWebViewClient(); if (client != null && urlPrefixesForDefaultIntent != null) { client.setUrlPrefixesForDefaultIntent(urlPrefixesForDefaultIntent); } } @Override protected void addEventEmitters(ThemedReactContext reactContext, WebView view) { this.reactContext = reactContext; // Do not register default touch emitter and let WebView implementation handle touches view.setWebViewClient(new ReactWebViewClient()); } @Override public @Nullable Map getCommandsMap() { return MapBuilder.of( "goBack", COMMAND_GO_BACK, "goForward", COMMAND_GO_FORWARD, "reload", COMMAND_RELOAD, "stopLoading", COMMAND_STOP_LOADING, "postMessage", COMMAND_POST_MESSAGE, "injectJavaScript", COMMAND_INJECT_JAVASCRIPT ); } @Override public void receiveCommand(WebView root, int commandId, @Nullable ReadableArray args) { switch (commandId) { case COMMAND_GO_BACK: root.goBack(); break; case COMMAND_GO_FORWARD: root.goForward(); break; case COMMAND_RELOAD: root.reload(); break; case COMMAND_STOP_LOADING: root.stopLoading(); break; case COMMAND_POST_MESSAGE: try { JSONObject eventInitDict = new JSONObject(); eventInitDict.put("data", args.getString(0)); root.loadUrl("javascript:(function () {" + "var event;" + "var data = " + eventInitDict.toString() + ";" + "try {" + "event = new MessageEvent('message', data);" + "} catch (e) {" + "event = document.createEvent('MessageEvent');" + "event.initMessageEvent('message', true, true, data.data, data.origin, data.lastEventId, data.source);" + "}" + "document.dispatchEvent(event);" + "})();"); } catch (JSONException e) { throw new RuntimeException(e); } break; case COMMAND_INJECT_JAVASCRIPT: root.loadUrl("javascript:" + args.getString(0)); break; } } @Override public void onDropViewInstance(WebView webView) { super.onDropViewInstance(webView); ((ThemedReactContext) webView.getContext()).removeLifecycleEventListener((ReactWebView) webView); ((ReactWebView) webView).cleanupCallbacksAndDestroy(); } protected WebView.PictureListener getPictureListener() { if (mPictureListener == null) { mPictureListener = new WebView.PictureListener() { @Override public void onNewPicture(WebView webView, Picture picture) { dispatchEvent( webView, new ContentSizeChangeEvent( webView.getId(), webView.getWidth(), webView.getContentHeight())); } }; } return mPictureListener; } protected static void dispatchEvent(WebView webView, Event event) { ReactContext reactContext = (ReactContext) webView.getContext(); EventDispatcher eventDispatcher = reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher(); eventDispatcher.dispatchEvent(event); } }

  • react-native-fetch-blob
    • RNFetchBlobReq


      RN集成自签名https及双向认证-android(2)_第6张图片
      RNFetchBlobReq.png
    • RNFetchBlob


      RN集成自签名https及双向认证-android(2)_第7张图片
      RNFetchBlob.png
package com.RNFetchBlob;

import android.app.Activity;
import android.app.DownloadManager;
import android.content.Intent;
import android.net.Uri;

import com.facebook.react.bridge.ActivityEventListener;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.LifecycleEventListener;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;

// Cookies
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.network.ForwardingCookieHandler;
import com.facebook.react.modules.network.CookieJarContainer;
import com.facebook.react.modules.network.OkHttpClientProvider;
import com.facebook.react.modules.network.ReactCookieJarContainer;

import okhttp3.OkHttpClient;
import okhttp3.JavaNetCookieJar;

import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.cert.CertificateFactory;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManagerFactory;

import static android.app.Activity.RESULT_OK;
import static com.RNFetchBlob.RNFetchBlobConst.GET_CONTENT_INTENT;

public class RNFetchBlob extends ReactContextBaseJavaModule {

    // Cookies
    private final ForwardingCookieHandler mCookieHandler;
    private final CookieJarContainer mCookieJarContainer;
    private final OkHttpClient mClient;

    static ReactApplicationContext RCTContext;
    static LinkedBlockingQueue taskQueue = new LinkedBlockingQueue<>();
    static ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, 10, 5000, TimeUnit.MILLISECONDS, taskQueue);
    static LinkedBlockingQueue fsTaskQueue = new LinkedBlockingQueue<>();
    static ThreadPoolExecutor fsThreadPool = new ThreadPoolExecutor(2, 10, 5000, TimeUnit.MILLISECONDS, taskQueue);
    static public boolean ActionViewVisible = false;
    static HashMap promiseTable = new HashMap<>();

     //https --修改 --
    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, RCTContext.getAssets().open("CNPCCA.cer"));
            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);

            //初始化keystore
            KeyStore clientKeyStore = KeyStore.getInstance("BKS");
            clientKeyStore.load(RCTContext.getAssets().open("client.bks"), "mima".toCharArray());

            KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
            keyManagerFactory.init(clientKeyStore, "mima".toCharArray());

            sslContext.init(
                    keyManagerFactory.getKeyManagers(),
                    trustManagerFactory.getTrustManagers(),
                    new SecureRandom()
            );
            client.sslSocketFactory(sslContext.getSocketFactory());

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
            //https --修改 --
    public RNFetchBlob(ReactApplicationContext reactContext) {

        super(reactContext);

        RCTContext = reactContext;
        mClient = initCustomOkHttpClient();
        mCookieHandler = new ForwardingCookieHandler(reactContext);
        mCookieJarContainer = (CookieJarContainer) mClient.cookieJar();
        mCookieJarContainer.setCookieJar(new JavaNetCookieJar(mCookieHandler));


        reactContext.addActivityEventListener(new ActivityEventListener() {
            @Override
            public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
                if(requestCode == GET_CONTENT_INTENT && resultCode == RESULT_OK) {
                    Uri d = data.getData();
                    promiseTable.get(GET_CONTENT_INTENT).resolve(d.toString());
                    promiseTable.remove(GET_CONTENT_INTENT);
                }
            }

            @Override
            public void onNewIntent(Intent intent) {

            }
        });
    }

    @Override
    public String getName() {
        return "RNFetchBlob";
    }

    @Override
    public Map getConstants() {
        return RNFetchBlobFS.getSystemfolders(this.getReactApplicationContext());
    }

    @ReactMethod
    public void createFile(final String path, final String content, final String encode, final Callback callback) {
        threadPool.execute(new Runnable() {
            @Override
            public void run() {
                RNFetchBlobFS.createFile(path, content, encode, callback);
            }
        });

    }

    @ReactMethod
    public void actionViewIntent(String path, String mime, final Promise promise) {
        try {
            Intent intent= new Intent(Intent.ACTION_VIEW)
                    .setDataAndType(Uri.parse("file://" + path), mime);
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            this.getReactApplicationContext().startActivity(intent);
            ActionViewVisible = true;

            final LifecycleEventListener listener = new LifecycleEventListener() {
                @Override
                public void onHostResume() {
                    if(ActionViewVisible)
                        promise.resolve(null);
                    RCTContext.removeLifecycleEventListener(this);
                }

                @Override
                public void onHostPause() {

                }

                @Override
                public void onHostDestroy() {

                }
            };
            RCTContext.addLifecycleEventListener(listener);
        } catch(Exception ex) {
            promise.reject(ex.getLocalizedMessage());
        }
    }

    @ReactMethod
    public void createFileASCII(final String path, final ReadableArray dataArray, final Callback callback) {
        threadPool.execute(new Runnable() {
            @Override
            public void run() {
                RNFetchBlobFS.createFileASCII(path, dataArray, callback);
            }
        });

    }

    @ReactMethod
    public void writeArrayChunk(final String streamId, final ReadableArray dataArray, final Callback callback) {
        RNFetchBlobFS.writeArrayChunk(streamId, dataArray, callback);
    }

    @ReactMethod
    public void unlink(String path, Callback callback) {
        RNFetchBlobFS.unlink(path, callback);
    }

    @ReactMethod
    public void mkdir(String path, Callback callback) {
        RNFetchBlobFS.mkdir(path, callback);
    }

    @ReactMethod
    public void exists(String path, Callback callback) {
        RNFetchBlobFS.exists(path, callback);
    }

    @ReactMethod
    public void cp(final String path, final String dest, final Callback callback) {
        threadPool.execute(new Runnable() {
            @Override
            public void run() {
                RNFetchBlobFS.cp(path, dest, callback);
            }
        });

    }

    @ReactMethod
    public void mv(String path, String dest, Callback callback) {
        RNFetchBlobFS.mv(path, dest, callback);
    }

    @ReactMethod
    public void ls(String path, Callback callback) {
        RNFetchBlobFS.ls(path, callback);
    }

    @ReactMethod
    public void writeStream(String path, String encode, boolean append, Callback callback) {
        new RNFetchBlobFS(this.getReactApplicationContext()).writeStream(path, encode, append, callback);
    }

    @ReactMethod
    public void writeChunk(String streamId, String data, Callback callback) {
        RNFetchBlobFS.writeChunk(streamId, data, callback);
    }

    @ReactMethod
    public void closeStream(String streamId, Callback callback) {
        RNFetchBlobFS.closeStream(streamId, callback);
    }

    @ReactMethod
    public void removeSession(ReadableArray paths, Callback callback) {
        RNFetchBlobFS.removeSession(paths, callback);
    }

    @ReactMethod
    public void readFile(final String path, final String encoding, final Promise promise) {
        threadPool.execute(new Runnable() {
            @Override
            public void run() {
                RNFetchBlobFS.readFile(path, encoding, promise);
            }
        });
    }

    @ReactMethod
    public void writeFileArray(final String path, final ReadableArray data, final boolean append, final Promise promise) {
        threadPool.execute(new Runnable() {
            @Override
            public void run() {
                RNFetchBlobFS.writeFile(path, data, append, promise);
            }
        });
    }

    @ReactMethod
    public void writeFile(final String path, final String encoding, final String data, final boolean append, final Promise promise) {
        threadPool.execute(new Runnable() {
            @Override
            public void run() {
                RNFetchBlobFS.writeFile(path, encoding, data, append, promise);
            }
        });

    }

    @ReactMethod
    public void lstat(String path, Callback callback) {
        RNFetchBlobFS.lstat(path, callback);
    }

    @ReactMethod
    public void stat(String path, Callback callback) {
        RNFetchBlobFS.stat(path, callback);
    }

    @ReactMethod
    public void scanFile(final ReadableArray pairs, final Callback callback) {
        final ReactApplicationContext ctx = this.getReactApplicationContext();
        threadPool.execute(new Runnable() {
            @Override
            public void run() {
                int size = pairs.size();
                String [] p = new String[size];
                String [] m = new String[size];
                for(int i=0;i

原理就是,import我们copy改动的类,不再使用.jar包里的类,option+return自动import包路径
具体查看管知汇,前期管理

你可能感兴趣的:(RN集成自签名https及双向认证-android(2))