🐿️

ComposeのWebViewでBasic認証を行いWebアプリを表示する(Android)

2022/06/05に公開

作ったWebアプリのデモを見せる機会があり、ネイティブアプリにしたときに、どのような感じなのか聞かれたら、見せられるようにしたいなと思いました。手っ取り早くBasic認証がかかったWebアプリをネイティブアプリとして動かしたいと思って、WebViewで出来ないかなと思いました。
そのときに、Basic認証でいくつか躓いたので、躓いたポイントと、その解決策を共有したいと思います。
ポイントは以下の2点です。

  1. アクセス先のウェブページへのBasic認証
  2. アクセスしたウェブページで読み込むリソース(画像など)の認証

やりたいこと

Basic認証でロックが掛かっているWebアプリをWebViewで表示、操作できるようにしたい。
Webアプリで読み込むリソース(画像など)にもBasic認証がかかっている。
WebViewの導入方法は、katzさんの記事[1]を参考にしました。

onReceivedHttpAuthRequestをOverrideする方法

この記事を書いた当初は出来なかったのですが、ちょっと頑張れば出来ました。参考にしたのはstackoverflowの記事[2]です。
コードを示します。

MainActivity.kt
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent{ MyWebClient(url = "https://hogehoge.hoge")}
    }
}
//https://developer.android.com/guide/webapps/webview?hl=ja
@SuppressLint("SetJavaScriptEnabled")
@Composable
fun MyWebClient(url: String) {

    AndroidView(factory = ::WebView,
    update = { webView ->
        webView.webViewClient = MyWebViewClient()
        webView.settings.javaScriptEnabled = true
        webView.loadUrl(url)

    })
}

// WebViewClientを継承してonReceivedHttpAuthRequestをOverrideする。
private class MyWebViewClient: WebViewClient() {
    @Override
    override fun onReceivedHttpAuthRequest(
        view: WebView?,
        handler: HttpAuthHandler?,
        host: String?,
        realm: String?
    ) {
        if (handler != null) {
            handler.proceed("Basic認証のユーザー名", "Basic認証のパスワード")
        }
    }
}

実行結果は、同様ですが以下のようになります。

Success to load all contents

アクセス先のウェブページのBasic認証

まずは、アクセス先のウェブページのBasic認証を実現します。
ネットで調べると、onReceivedHttpAuthRequestを使用する方法[2:1]が見つかりましたが、うまく実装できませんでした。そこで、stackoverflowの記事[3]を参考に、WebViewのloadUrlにヘッダー情報でAuthenticationの情報を入れることにしました。
入れ方は、loadUrlの第2引数にヘッダー情報を入れます。
入れる認証情報は、ID:passwordをBase64へエンコードした文字列です。例えば「ID:password」をBase64へエンコードすると「SUQ6cGFzc3dvcmQ=」になります。これを、mapOfを使って、ヘッダー情報としてloadUrlの第2引数へ入れます。例えばこんな感じです。

webView.loadUrl(url, mapOf("Authorization" to "Basic SUQ6cGFzc3dvcmQ="))

これを使って、コードを書くと、以下のようになります。

MainActivity.kt
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent{ MyWebClient(url = "https://hogehoge.hoge")}
    }
}

@SuppressLint("SetJavaScriptEnabled")
@Composable
fun MyWebClient(url: String) {

    AndroidView(factory = ::WebView,
    update = { webView ->
        webView.webViewClient = WebViewClient()
        webView.settings.javaScriptEnabled = true
        webView.loadUrl(url, mapOf("Authorization" to "Basic {Base64でエンコードしたID:password}"))
    })
}

これを実行すると、以下のような実行結果になります。

Failed to load images

このように、画像が読み込めていません。原因は、画像にもBasic認証でロックが掛かっているにもかかわらず、認証情報が画像の読み込み時に反映されていないことです。

アクセスしたウェブページで読み込むリソース(画像など)の認証

これを実現するのに、とても苦労しました。
やり方は、以下のようにWebViewのキャッシュの設定を書き加えることです。
これは、キャッシュを保存しない方法を書いた記事[4]を参考にしました。

webView.settings.cacheMode = WebSettings.LOAD_CACHE_ONLY

これを追加したコードが以下です。

MainActivity.kt
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent{ MyWebClient(url = "https://hogehoge.hoge")}
    }
}

@SuppressLint("SetJavaScriptEnabled")
@Composable
fun MyWebClient(url: String) {

    AndroidView(factory = ::WebView,
    update = { webView ->
        webView.webViewClient = WebViewClient()
        webView.settings.javaScriptEnabled = true
        webView.settings.cacheMode = WebSettings.LOAD_CACHE_ONLY
        webView.loadUrl(url, mapOf("Authorization" to "Basic {Base64でエンコードしたID:password}"))
    })
}

これを実行すると、以下のような実行結果になります。
ちょっと、コンテンツは見せられないので、ぼかしになりますが、画像も読み込まれています。

Success to load all contents

脚注
  1. Jetpack Compose の AndroidView で WebView を利用する / katz https://zenn.dev/kaleidot725/articles/2021-11-13-jc-webview-detaiils (2022-06-05閲覧) ↩︎

  2. Using WebView setHttpAuthUsernamePassword? / dparnas https://stackoverflow.com/questions/2585055/using-webview-sethttpauthusernamepassword (2022-06-05閲覧) ↩︎ ↩︎

  3. Android WebView - JWT Authentication / Stonz2 [https://stackoverflow.com/questions/48588918/android-webview-jwt-authentication] (2022-06-05閲覧) ↩︎

  4. 【Android】WebView にキャッシュをさせない方法 / awacleberry https://awacleberry.hatenablog.com/entry/2017/06/28/223524 (2022-06-05閲覧) ↩︎

Discussion