禁用 WebView 中的缓存、cookie 和其他所有内容

2023-11-21

我有一个网络服务,我试图使用网络视图在后台进行身份验证。当我最初发送请求时,它将正常工作(基于凭据的失败/成功),但之后我似乎收到了缓存的响应。

这是我的网络视图设置代码:

WebView browser = new WebView(this);
WebSettings settings = browser.getSettings();
settings.setJavaScriptEnabled(true);
settings.setSavePassword(false);
settings.setCacheMode(WebSettings.LOAD_NO_CACHE);
settings.setAppCacheEnabled(false);
browser.setWebChromeClient(new WebChromeClient() {
    public void onProgressChanged(WebView view, int progress) {
    Log.d("BROWSERPROGRESS", Integer.toString(progress));
}
});
jsInterface = new AddAccountJSInterface();
browser.addJavascriptInterface(jsInterface, "ADDACCOUNTJSINTERFACE");
browser.setWebViewClient(new AddAccountClient(this));

正如您所看到的,我有两个额外的类控制我的 webView:

  1. 为 javascript 提供接口的对象 (AddAccountJSInterface)
  2. WebView客户端

另外,我确实有一个 WebChromeClient,但它只是用于调试,我很确定它不会干扰任何事情。

JS 接口只是提供了一种获取正文 HTML 来执行分析的简单方法,因此我相信这也不是问题。

WebViewClient 中包含以下代码,它根据来自 Web 服务的各种响应完成路由的大部分“自定义”工作。

    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        if(url.contains(INSTALL_PREFIX)) {
            HashMap<String, String> params = extractParameters(url);
            verificationComplete(params);
            return true;
        }
        return false;
    }

    @Override
    public void onPageFinished(WebView view, String url){
        if(invalidShop(view)) {
            Toast.makeText(context, context.getString(R.string.no_find_shop), Toast.LENGTH_SHORT).show();
            shopAddressField.requestFocus();
            replaceUiElements(loadingBar, addAccountButton);
        } else if(url.contains(ADMIN_AUTH_LOGIN)) {
            if(invalidLogin(view)) {
                Toast.makeText(context, context.getString(R.string.invalid_login),Toast.LENGTH_SHORT).show();
                emailField.requestFocus();
                replaceUiElements(loadingBar, addAccountButton);
            } else {
                String email = emailField.getText().toString();
                String password = passwordField.getText().toString();
                String submitJS = String.format(FORM_SUBMISSION_JS, email, password);

                jsInterface.setInnerHTML("");

                browser.loadUrl(submitJS);
            }
        }
    }

在我的活动中,我需要填写 3 个文本字段,然后单击按钮提交。然后,该活动从 3 个文本字段(shopAddressField、usernameField、passwordField)获取数据,然后执行一些 javascript 来填充一些表单数据(在不可见的 webView 中加载),然后单击提交按钮。

最后一部分是混乱的,它似乎正在缓存来自服务器的响应(也许使用cookie?)并返回该响应,而不是询问服务器数据是否正确。

澄清一下:

JSInterface 只是一个 Java 对象,它允许我在 webview 上执行 javascript,该 webview 与该对象中的函数绑定。就我而言,我的 JSInterface 有一个函数,即 setInnerHtml(String html)。

这是在 webview 上执行的 javascript:

javascript:window.ADDACOUNTJSINTERFACE.setInnerHTML(document.body.innerHTML)

这是 setInnerHtml 函数:

public void setInnerHtml(String innerHtml) {
    this.innerHtml = innerHtml;
}

因此,当我实际执行 jsInterface.setInnerHtml("") 时,我只是覆盖了拉入的 HTML(以确保我不会因某种原因从那里获取旧数据)。

至于我的submitJS,它又是在我的webView上执行的一些Javascript,如下所示:

// submitJS will be something like this once all the credentials have been set
// Note: I know that the server will make jQuery available
// Note: Much of the Java string formatting has been removed to help clarify
// the code.
String submitJS = 
    "javascript:(function() {
        $('login-input').value='username';
        $('password').value='password';
        $('sign-in-form').up().submit();
    })()"
// I then simply get the webview to execute the javascript above
webView.loadData(submitJS);

所以事实证明问题不是基于缓存,也可能不是 cookie。

当在 webView 上执行 javascript 时,它会在单独的线程中执行此操作,并且速度可能会很慢。这会导致竞争条件,导致代码以错误的顺序执行。

我通过使用信号量作为互斥体解决了这个问题。这允许我阻止 getter 在 webView 上的 Javascript 能够执行之前返回。

我现在创建的界面如下所示:

private class AddAccountJSInterface {
    private final String TAG = getClass().getName().toUpperCase();
    private Semaphore mutex = new Semaphore(1, false);
    private String innerHTML;

    public void aquireSemaphore() {
        Log.d(TAG, "Attempting to lock semaphore");
        try {
            mutex.acquire();
        } catch(InterruptedException e) {
            Log.d(TAG, "Oh snap, we got interrupted.  Just going to abort.");
            return;
        }
        Log.d(TAG, "Semaphore has been aquired");
    }

    @SuppressWarnings("unused")
    public void setInnerHTML(String html) {
            this.innerHTML = html;
            Log.d(TAG, "setInnerHTML is now releasing semaphore.");
            mutex.release();
            Log.d(TAG, "setInnerHTML has successfully released the semaphore.");
    }

    public synchronized String getInnerHTML() {
        Log.d(TAG, "getInnerHTML attempting to aquire semaphore, may block...");
        String innerHTML = "";
        try {
            mutex.acquire();

            Log.d(TAG, "getInnerHTML has aquired the semaphore, grabbing data.");
            innerHTML = this.innerHTML;

            Log.d(TAG, "getInnerHTML no longer needs semaphore, releasing");
            mutex.release();
        } catch (InterruptedException e) {
            Log.d(TAG, "Something has gone wrong while attempting to aquire semaphore, aborting");
        }

        return innerHTML;
    }
}

现在我在代码中使用它的方式如下:

// I have access to the jsInterface object which is an instance of the class above as well as a webView which I will be executing the javascript on.
String getInnerHtmlJS = "javascript:window.MYJSINTERFACE.setInnerHTML(document.body.innerHTML);"
jsInterface.aquireSemaphore()
// Execute my JS on the webview
jsInterface.loadUrl(getInnerHtmlJS)
// Now we get our inner HTML
// Note: getInnerHTML will block since it must wait for the setInnerHTML (executed via the JS) function to release the semaphore
String theInnerHTML = jsInterface.getInnerHTML();
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

禁用 WebView 中的缓存、cookie 和其他所有内容 的相关文章

随机推荐