[Android] WebView without HTTPS

在開發 Android 程式的時候,有需要用到 WebView 直接連結特定的網頁,但是很多時候網頁並沒有 HTTP SSL 的保護, 基本上 Android 9 以後 WebView 在顯示沒有 SSL 的網頁會直接顯示設成 Disable,詳情可以參考。本篇想要紀錄如何利用 Android WebView 連結 without HTTPS 的網站。

Note: The guidance in this section applies only to apps that target Android 8.1 (API level 27) or lower. Starting with Android 9 (API level 28), cleartext support is disabled by default.

嘗試一:增加 network_security_config.xml 到 res/xml 裡面

我們首先嘗試參考連結的解決方法,在 res/xml 裡面增加一個 network_security_config.xml 檔案如以下的內容,在 <domain includeSubdomains=”true”> </domain> 中間加入可以接受 ClearTextTraffic 的 URL。

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config cleartextTrafficPermitted="true">
        //  Add host of your download URL in below line. 
        //  ie. if url is  "https://www.google.com/search?source=...."
        //  then just add "www.google.com"
        <domain includeSubdomains="true">www.smarterasp.net</domain>
    </domain-config>
</network-security-config>

然後在 AndroidManifest.xml 裡面增加以下的設定:

<application
        ...
        android:networkSecurityConfig="@xml/network_security_config"
        ...>
</application>

但是測試的結果是這個方法並不有效,之後嘗試方法二。其他參考資訊:連結

嘗試二:改動 WebViewClient 增加 onReceivedSslError()

在設定 webView 的時候需要設定 WebViewClient,這個方法是重新調整當 SslError 時的反應方法,在 Android 9.0 之後 onReceivedSslError 的預設方法是 handler.cancel() 此會將 WebView 變成空白頁,將 handler.cancel() 改成 handler.proceed() 之後就可以成功顯示沒有 SSL 的網頁。

webView.setWebViewClient(new WebViewClient(){
          @Override
            public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error){
                handler.proceed();
            }
        });
        WebSettings webSettings = webView.getSettings();
        webSettings.setJavaScriptEnabled(true);
        webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
}

以上的方法雖然可以解決 SSL 的問題,但是在發佈到 Google Play Store 的時候發生以下錯誤訊息,Google 方面提供相關資訊連結:https://support.google.com/faqs/answer/7071387

我們將 onReceivedSslError 近一步改進如以下,還是無法通過 Google Play 審核:

public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error){
    if (error.getPrimaryError() == SslError.SSL_IDMISMATCH ){
        handler.proceed();
    } else {
        handler.cancel();
    }
}

我們將 SslError 錯誤的情況限制在 SSL_IDMISMATCH 則可以被接受,這邊還是需要看不同的需求去改動。其他的錯誤情況可以參考以下:

public static final int SSL_NOTYETVALID = 0;
public static final int SSL_EXPIRED = 1;
public static final int SSL_IDMISMATCH = 2;
public static final int SSL_UNTRUSTED = 3;
public static final int SSL_DATE_INVALID = 4;
public static final int SSL_INVALID = 5;
@Deprecated
public static final int SSL_MAX_ERROR = 6;
最後解決方法:

在搜尋其他的解決方法之後,找到以下的方法可以通過 Google 的審核,但是並不是完美的解決方法,如果 WebView 連結沒有 SSL 的網站的話,Google 還是希望可以詢問用戶的意願來近一步保護用戶!

SslCertificate sslCertificate = error.getCertificate();
final AlertDialog.Builder builder = new AlertDialog.Builder(act);
builder.setTitle("Website Loading, Continue?");
builder.setPositiveButton(R.string.NSLS_YES, new DialogInterface.OnClickListener() {
    @Override
    public void onClick(DialogInterface dialog, int which) {
        handler.proceed();
    }
});
builder.setNegativeButton(R.string.NSLS_CANCEL, new DialogInterface.OnClickListener() {
    @Override
    public void onClick(DialogInterface dialog, int which) {
        handler.cancel();
    }
});
final AlertDialog dialog = builder.create();
dialog.show();

 

其他參考連結:

https://codertw.com/android-%E9%96%8B%E7%99%BC/334517/

https://lipeng1667.github.io/2017/01/03/android-webview-https-and-taobaoke/

https://stackoverflow.com/questions/58003523/how-can-i-fix-this-android-webview-problem