How to add a JavaScript function in WebView and call it later from HTML upon submitting reCAPTCHA

核能气质少年 提交于 2019-12-09 15:46:48

问题


I am adding a JavaScript function in WebView like this (Kotlin):

val webView = findViewById(R.id.webview) as WebView
webView.getSettings().setJavaScriptEnabled(true)
webView.addJavascriptInterface(this, "android")
webView.getSettings().setBuiltInZoomControls(false)
webView.loadUrl(url)

webView.webViewClient = object : WebViewClient() {
    override fun onPageFinished(view: WebView, url: String) {
        super.onPageFinished(view, url)
        webView.loadUrl("javascript:(function captchaResponse (token){" +
                        "      android.reCaptchaCallbackInAndroid(token);" +
                        "    })()")
    }
}

The function works fine, but the problem is that it runs immediately, when I add it in WebView. I only want to include it as a JavaScript function and it should be called only from the HTML, when the user will fill the reCAPTCHA. How can I do that?


回答1:


Try injecting the script like this,

function addCode(code){
var addedScript= document.createElement('script');
addedScript.text= code;
document.body.appendChild(addedScript);}

now call the function like,

val codeToExec = "function captchaResponse (token){" +
                    "android.reCaptchaCallbackInAndroid(token);" +
                    "}";

now exec loadurl like,

webview.loadUrl("javascript:(function addCode(code){
var addedScript= document.createElement('script');
addedScript.text= code;
document.body.appendChild(addedScript);})(codeToExec));



回答2:


In order to run your reCaptchaCallbackInAndroid exposed method from JavaScript, when the user submitted a successful reCAPTCHA response, first make sure, to actually listen to the reCAPTCHA callback via g-recaptcha tag attributes:

<div class="g-recaptcha"
     data-sitekey="{{your site key}}"
     data-callback="myCustomJavaScriptCallback"
></div>

or via the reCAPTCHA JavaScript API:

grecaptcha.render(
  'g-recaptcha-element-id', {
    sitekey: '{{your site key}}',
    callback: 'myCustomJavaScriptCallback'
  }
)

then, when the page finished loading in the WebView, add your JavaScript callback function to the window object using webView.loadUrl:

webView.loadUrl("""
    javascript:(function() {
        window.myCustomJavaScriptCallback = function(token) {                
            android.reCaptchaCallbackInAndroid(token);
            /* also add your additional JavaScript functions
               and additional code in this function */
        }
    })()
""".trimIndent())

and finally, when the user submits a successful reCAPTCHA response, your myCustomJavaScriptCallback will be called and through that, your exposed reCaptchaCallbackInAndroid method too with the reCAPTCHA token.

  • Since you're using Kotlin, in this case, you can just simply use multiline string literals.

  • Since you're exposing a method to JavaScript, make sure to know the security concerns.

  • In case you'll need additional JavaScript injection in the future (more method exposure, DOM manipulation, etc.), check out this post.


In your case:

Set reCAPTCHA to call your captchaResponse JavaScript function via tag attribute:

<div class="g-recaptcha"
     ...
     data-callback="captchaResponse"
     ...
></div>

or via its API:

grecaptcha.render(
  '...', {
    ...
    callback: 'captchaResponse'
    ...
  }
)

and add your captchaResponse callback function to window:

webView.loadUrl("""
    javascript:(function() {
        window.captchaResponse = function(token) {
            android.reCaptchaCallbackInAndroid(token);
            /* also you can add further JavaScript functions
               and additional code in this function */
        }
    })()
""".trimIndent())

Test:

Here's a simple, Empty Activity in Android Studio with a basic LinearLayout ( an EditText and a Button within the layout) and the MainActivity.kt:

package com.richardszkcs.injectjsintowebview

import android.net.Uri
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.webkit.JavascriptInterface
import kotlinx.android.synthetic.main.activity_main.*
import android.webkit.WebView
import android.webkit.WebViewClient
import android.widget.Toast

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        sendButton.setOnClickListener { loadWebpage() }
    }

    @Throws(UnsupportedOperationException::class)
    fun buildUri(authority: String) : Uri {
        val builder = Uri.Builder()
        builder.scheme("https")
                .authority(authority)
        return builder.build()
    }

    @JavascriptInterface
    fun reCaptchaCallbackInAndroid(token: String) {
        val tok = token.substring(0, token.length / 2) + "..."
        Toast.makeText(this.applicationContext, tok, Toast.LENGTH_LONG).show()
    }

    fun loadWebpage() {
        webView.getSettings().setJavaScriptEnabled(true)
        webView.addJavascriptInterface(this, "android")
        webView.getSettings().setBuiltInZoomControls(false)
        webView.loadUrl("https://richardszkcs.github.io/recaptcha-test/")

        webView.webViewClient = object : WebViewClient() {
            override fun onPageFinished(view: WebView, url: String) {
                super.onPageFinished(view, url)
                webView.loadUrl("""
                    javascript:(function() {
                        window.onCaptchaSuccess = function(token) {
                            android.reCaptchaCallbackInAndroid(token);
                        }
                    })()
                """.trimIndent())
            }
        }
    }
}

then using a simple reCAPTCHA test website, the window.onCaptchaSuccess function is called upon a successful reCAPTCHA submission and the reCAPTCHA token is partially displayed in a Toast using an Android Emulator:


Full disclosure: I made the reCAPTCHA test website to prepare/test/debug similar situations.



来源:https://stackoverflow.com/questions/51570906/how-to-add-a-javascript-function-in-webview-and-call-it-later-from-html-upon-sub

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!