如何在WebView中添加JavaScript函数,稍后在提交reCAPTCHA时从HTML调用它

Zoh*_*Ali 10 javascript android recaptcha webview android-webview

我在WebView中添加了一个JavaScript函数(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);" +
                        "    })()")
    }
}
Run Code Online (Sandbox Code Playgroud)

该函数工作正常,但问题是它在WebView中添加它时会立即运行.我只想将它作为JavaScript函数包含在内,只有当用户填写reCAPTCHA时才能从HTML调用它.我怎样才能做到这一点?

Ric*_*ick 9

为了reCaptchaCallbackInAndroid从JavaScript 运行您的公开方法,当用户提交成功的 reCAPTCHA响应时,首先要确保通过标记属性实际监听 reCAPTCHA :callbackg-recaptcha

<div class="g-recaptcha"
     data-sitekey="{{your site key}}"
     data-callback="myCustomJavaScriptCallback"
></div>
Run Code Online (Sandbox Code Playgroud)

或通过reCAPTCHA JavaScript API:

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

然后,当页面完成了在加载WebView,添加您的JavaScript回调函数的window对象使用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())
Run Code Online (Sandbox Code Playgroud)

最后,当用户提交成功的reCAPTCHA响应时,您myCustomJavaScriptCallback将通过reCAPTCHA调用您的公开reCaptchaCallbackInAndroid方法token.

  • 由于你使用的是Kotlin,在这种情况下,你可以简单地使用多行字符串文字.

  • 由于您正在向JavaScript公开方法,因此请务必了解安全问题.

  • 如果您将来需要额外的JavaScript注入(更多方法曝光,DOM操作等),请查看此文章.


在你的情况下:

设置reCAPTCHA以captchaResponse通过tag attribute以下方式调用JavaScript函数:

<div class="g-recaptcha"
     ...
     data-callback="captchaResponse"
     ...
></div>
Run Code Online (Sandbox Code Playgroud)

或通过其API:

grecaptcha.render(
  '...', {
    ...
    callback: 'captchaResponse'
    ...
  }
)
Run Code Online (Sandbox Code Playgroud)

并将您的captchaResponse回调函数添加到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())
Run Code Online (Sandbox Code Playgroud)

测试:

这是一个简单的,Empty Activity在Android Studio中有一个基本的LinearLayout (一个EditText和一个Button布局内)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())
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

然后使用简单的reCAPTCHA测试网站,在window.onCaptchaSuccess成功提交reCAPTCHA时调用该函数,并Toast使用Android模拟器部分显示reCAPTCHA令牌:

成功reCAPTCHA的虚拟机


完全披露:我做了reCAPTCHA测试网站准备/测试/调试类似的情况.


Sai*_*man 0

尝试像这样注入脚本,

function addCode(code){
var addedScript= document.createElement('script');
addedScript.text= code;
document.body.appendChild(addedScript);}
Run Code Online (Sandbox Code Playgroud)

现在调用该函数,例如

val codeToExec = "function captchaResponse (token){" +
                    "android.reCaptchaCallbackInAndroid(token);" +
                    "}";
Run Code Online (Sandbox Code Playgroud)

现在执行 loadurl 像,

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