wake-up-neo.com

So fügen Sie eine JavaScript-Funktion in WebView hinzu und rufen sie später beim Senden von reCAPTCHA aus HTML auf

Ich füge eine JavaScript-Funktion in WebView hinzu (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);" +
                        "    })()")
    }
}

Die Funktion funktioniert gut, aber das Problem ist, dass sie sofort ausgeführt wird, wenn ich sie in WebView hinzufüge. Ich möchte es nur als JavaScript-Funktion einschließen und sollte nur vom HTML-Code aus aufgerufen werden, wenn der Benutzer reCAPTCHA ausfüllt. Wie kann ich das machen?

10
Zohab Ali

Versuchen Sie, das Skript so zu injizieren,

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

rufen Sie jetzt die Funktion auf wie

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

jetzt exec loadurl wie,

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

Wenn der Benutzer eine erfolgreiche reCAPTCHA-Antwort gesendet hat, um Ihre reCaptchaCallbackInAndroid-Methode in JavaScript auszuführen, stellen Sie zunächst sicher, dass Sie tatsächlich die reCAPTCHA-callback-Antwort über g-recaptcha Tag-Attribute :

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

oder über die reCAPTCHA JavaScript API :

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

fügen Sie nach dem Laden der Seite in WebView Ihre JavaScript-Rückruffunktion zum window-Objekt hinzu, indem Sie webView.loadUrl verwenden:

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

und schließlich, wenn der Benutzer eine erfolgreiche reCAPTCHA-Antwort einreicht, wird Ihre myCustomJavaScriptCallback-Methode aufgerufen, und damit auch Ihre exponierte reCaptchaCallbackInAndroid-Methode mit der reCAPTCHA-Methode token.

  • Da Sie Kotlin verwenden, können Sie in diesem Fall einfach mehrzeilige String-Literale verwenden.

  • Da Sie eine Methode für JavaScript verfügbar machen, müssen Sie das Sicherheitsbedenken kennen.

  • Falls Sie in Zukunft zusätzliche JavaScript-Injection benötigen (mehr Methodenexposition, DOM-Manipulation usw.), siehe diesen Beitrag .


In deinem Fall:

Stellen Sie reCAPTCHA so ein, dass Ihre JavaScript-Funktion captchaResponse über tag attribute aufgerufen wird:

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

oder über seine API:

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

und fügen Sie Ihre captchaResponse-Rückruffunktion zu window hinzu:

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())

Prüfung:

Hier ist ein einfaches Empty Activity in Android Studio mit einem grundlegenden LinearLayout(ein EditText und ein Button innerhalb des Layouts)und dem 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())
            }
        }
    }
}

wenn dann eine einfache reCAPTCHA-Testwebsite verwendet wird, wird die window.onCaptchaSuccess -Funktion bei erfolgreicher reCAPTCHA-Übermittlung aufgerufen und das reCAPTCHA-Token wird teilweise in einem Toast mit einem Android Emulator:

virtual machine with successful reCAPTCHA


Vollständige Offenlegung: Ich habe die reCAPTCHA-Test-Website erstellt, um ähnliche Situationen vorzubereiten/zu testen/zu debuggen.

9
Rick

IN Java

MyApplication.Java

    public class MyApplication  extends Application {

        public static final String TAG = MyApplication.class
                .getSimpleName();

        private RequestQueue mRequestQueue;

        private static MyApplication mInstance;

        @Override
        public void onCreate() {
            super.onCreate();
            mInstance = this;
        }

        public static synchronized MyApplication getInstance() {
            return mInstance;
        }

        public RequestQueue getRequestQueue() {
            if (mRequestQueue == null) {
                mRequestQueue = Volley.newRequestQueue(getApplicationContext());
            }

            return mRequestQueue;
        }

        public <T> void addToRequestQueue(Request<T> req, String tag) {
            // set the default tag if tag is empty
            req.setTag(TextUtils.isEmpty(tag) ? TAG : tag);
            getRequestQueue().add(req);
        }

        public <T> void addToRequestQueue(Request<T> req) {
            req.setTag(TAG);
            getRequestQueue().add(req);
        }

        public void cancelPendingRequests(Object tag) {
            if (mRequestQueue != null) {
                mRequestQueue.cancelAll(tag);
            }
        }
    }

**MainActivity.Java**

public class MainActivity extends AppCompatActivity {

    private static final String TAG = MainActivity.class.getSimpleName();

    // TODO - replace the SITE KEY with yours
    private static final String SAFETY_NET_API_SITE_KEY = " YOUR SITE KEY HEAR";

    // TODO - replace the SERVER URL with yours
    private static final String URL_VERIFY_ON_SERVER = "https://example.com/index.php";

    @BindView(R.id.input_feedback)
    EditText inputFeedback;

    @BindView(R.id.layout_feedback_form)
    LinearLayout layoutFeedbackForm;

    @BindView(R.id.message_feedback_done)
    TextView messageFeedbackDone;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);

        Toolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        getSupportActionBar().setTitle(getString(R.string.feedback));
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);

        Toast.makeText(getApplicationContext(), "Always check Android Studio `LogCat` for errors!", Toast.LENGTH_LONG).show();
    }

    @OnClick(R.id.btn_send)
    public void validateCaptcha() {

        String feedback = inputFeedback.getText().toString().trim();
        // checking for empty feedback message
        if (TextUtils.isEmpty(feedback)) {
            Toast.makeText(getApplicationContext(), "Enter feedback!", Toast.LENGTH_LONG).show();
            return;
        }

        // Showing reCAPTCHA dialog
        SafetyNet.getClient(this).verifyWithRecaptcha(SAFETY_NET_API_SITE_KEY)
                .addOnSuccessListener(this, new OnSuccessListener<SafetyNetApi.RecaptchaTokenResponse>() {
                    @Override
                    public void onSuccess(SafetyNetApi.RecaptchaTokenResponse response) {
                        Log.d(TAG, "onSuccess");

                        if (!response.getTokenResult().isEmpty()) {

                            // Received captcha token
                            // This token still needs to be validated on the server
                            // using the SECRET key
                            verifyTokenOnServer(response.getTokenResult());
                        }
                    }
                })
                .addOnFailureListener(this, new OnFailureListener() {
                    @Override
                    public void onFailure(@NonNull Exception e) {
                        if (e instanceof ApiException) {
                            ApiException apiException = (ApiException) e;
                            Log.d(TAG, "Error message: " +
                                    CommonStatusCodes.getStatusCodeString(apiException.getStatusCode()));
                        } else {
                            Log.d(TAG, "Unknown type of error: " + e.getMessage());
                        }
                    }
                });
    }

    /**
     * Verifying the captcha token on the server
     * Post param: recaptcha-response
     * Server makes call to https://www.google.com/recaptcha/api/siteverify
     * with SECRET Key and Captcha token
     */
    public void verifyTokenOnServer(final String token) {
        Log.d(TAG, "Captcha Token" + token);

        StringRequest strReq = new StringRequest(Request.Method.POST,
                URL_VERIFY_ON_SERVER, new Response.Listener<String>() {

            @Override
            public void onResponse(String response) {
                Log.d(TAG, response.toString());

                try {
                    JSONObject jsonObject = new JSONObject(response);
                    boolean success = jsonObject.getBoolean("success");
                    String message = jsonObject.getString("message");

                    if (success) {
                        // Congrats! captcha verified successfully on server
                        // TODO - submit the feedback to your server

                        layoutFeedbackForm.setVisibility(View.GONE);
                        messageFeedbackDone.setVisibility(View.VISIBLE);
                    } else {
                        Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();
                    }
                } catch (JSONException e) {
                    e.printStackTrace();
                    Toast.makeText(getApplicationContext(), "Json Error: " + e.getMessage(), Toast.LENGTH_LONG).show();
                }

            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                Log.e(TAG, "Error: " + error.getMessage());
            }
        }) {
            @Override
            protected Map<String, String> getParams() {
                Map<String, String> params = new HashMap<>();
                params.put("recaptcha-response", token);

                return params;
            }
        };

        MyApplication.getInstance().addToRequestQueue(strReq);
    }
}

build.gradle

 // SafetyNet reCAPTCHA
    implementation 'com.google.Android.gms:play-services-safetynet:11.8.0'

    // ButterKnife
    implementation 'com.jakewharton:butterknife:8.8.1'
    annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<Android.support.design.widget.CoordinatorLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:app="http://schemas.Android.com/apk/res-auto"
    xmlns:tools="http://schemas.Android.com/tools"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    tools:context=".MainActivity">

    <Android.support.design.widget.AppBarLayout
        Android:layout_width="match_parent"
        Android:layout_height="wrap_content"
        Android:theme="@style/AppTheme.AppBarOverlay">

        <Android.support.v7.widget.Toolbar
            Android:id="@+id/toolbar"
            Android:layout_width="match_parent"
            Android:layout_height="?attr/actionBarSize"
            Android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.PopupOverlay" />

    </Android.support.design.widget.AppBarLayout>

    <include layout="@layout/content_main" />

</Android.support.design.widget.CoordinatorLayout>

content_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:app="http://schemas.Android.com/apk/res-auto"
    xmlns:tools="http://schemas.Android.com/tools"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    Android:orientation="vertical"
    Android:padding="@dimen/activity_margin"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context="info.androidhive.recaptcha.MainActivity"
    tools:showIn="@layout/activity_main">

    <LinearLayout
        Android:id="@+id/layout_feedback_form"
        Android:layout_width="match_parent"
        Android:layout_height="wrap_content"
        Android:orientation="vertical">

        <TextView
            Android:layout_width="match_parent"
            Android:layout_height="wrap_content"
            Android:text="@string/title_form"
            Android:textColor="#666666"
            Android:textSize="20dp"
            Android:textStyle="bold" />

        <TextView
            Android:layout_width="match_parent"
            Android:layout_height="wrap_content"
            Android:text="@string/desc_form" />

        <EditText
            Android:id="@+id/input_feedback"
            Android:layout_width="match_parent"
            Android:layout_height="wrap_content"
            Android:layout_marginTop="@dimen/activity_margin"
            Android:gravity="top"
            Android:hint="@string/hint_feedback"
            Android:lines="5" />

        <Button
            Android:id="@+id/btn_send"
            Android:layout_width="wrap_content"
            Android:layout_height="wrap_content"
            Android:layout_marginTop="@dimen/activity_margin"
            style="@style/Widget.AppCompat.Button.Colored"
            Android:text="@string/btn_send"
            Android:textColor="@Android:color/white" />

    </LinearLayout>

    <TextView
        Android:id="@+id/message_feedback_done"
        Android:layout_width="match_parent"
        Android:layout_height="wrap_content"
        Android:layout_marginTop="40dp"
        Android:gravity="center"
        Android:padding="@dimen/activity_margin"
        Android:text="@string/message_feedback_done"
        Android:textSize="22dp"
        Android:visibility="gone" />

</LinearLayout>
0
Viral Patel