Android WebView控件捕獲用戶輸入的信息
WebView可所謂是Android中最強(qiáng)大的控件之一,無(wú)所不能。
于是有這么一個(gè)需求,用戶在app之中內(nèi)嵌的WebView中輸入帳號(hào)密碼的時(shí)候,App需要捕獲已經(jīng)輸入的帳號(hào)密碼。
當(dāng)用戶輸入帳號(hào)密碼,一般情況下會(huì)進(jìn)行頁(yè)面轉(zhuǎn)跳,在頁(yè)面轉(zhuǎn)跳之前執(zhí)行js腳本,通過(guò)js腳本來(lái)獲取這個(gè)帳號(hào)密碼的value值。要先獲取各個(gè)元素的class值,需要解析整個(gè)html頁(yè)面,那么我們可以重寫 onLoadResource 這個(gè)方法,代碼如下:
webview.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return false;
}
@Override
public void onLoadResource(WebView view, String url) {
getHtml();
Log.e("log-->", "onLoadResource-->>" + url);
}
@Override
public void onPageFinished(WebView view, String url) {
}
});
上面這個(gè)方法在加載頁(yè)面資源時(shí)會(huì)調(diào)用,每一個(gè)資源(比如圖片)的加載都會(huì)調(diào)用一次。那么我們可以在這個(gè)方法里面執(zhí)行注入的js腳本
先執(zhí)行addJavascriptInterface方法,將一個(gè)java對(duì)象綁定到一個(gè)js對(duì)象中,代碼如下:
public class JavaScriptInterface {
String mPasswrod;
String mUsername;
@JavascriptInterface
public void getHTML(final String html) {
if (!TextUtils.isEmpty(html)) {
saveWebViewUserData.saveUserDataWebView(webview, html);
}
}
@JavascriptInterface
public void save_password(final String password) {
if (!TextUtils.isEmpty(password)){
LogUtils.e("received from js. password = " + password);
this.mPasswrod = password;
checkData(mUsername, mPasswrod);
}
}
@JavascriptInterface
public void save_username(final String username) {
if (!TextUtils.isEmpty(username)) {
LogUtils.e("received from js. username = " + username);
this.mUsername = username;
checkData(mUsername, mPasswrod);
}
}
}
webview.addJavascriptInterface(new JavaScriptInterface(), "android");
private void getHtml() {
webview.loadUrl("javascript:window.android.getHTML('<html>'+document.body.innerHTML+'</html>');");
}
那么下面這句話執(zhí)行完的結(jié)果將會(huì)返回到JavaScriptInterface中g(shù)etHTML方法里面。也就是說(shuō)通過(guò)綁定,js代碼調(diào)用了java代碼,并將整個(gè)html作為返回值返回,執(zhí)行的是saveWebViewUserData.saveUserDataWebView(webview, html);
得到了包含class的html之后,就需要依次分析了,通常來(lái)說(shuō),一般輸入帳號(hào)密碼的頁(yè)面都含有 type=”password” 字樣。先判斷這個(gè)html頁(yè)面是否含有這個(gè)字樣,如果有,那么可能就是登錄頁(yè)面。
再判斷這個(gè)頁(yè)面的id,或者是classname是否包含password啦,pwd啦,或者什么其他和密碼有關(guān)的了,這個(gè)元素肯定就是密碼框了,再過(guò)濾掉頁(yè)面中其他的button,hidden,submit,checkbox等等,剩下的那一個(gè)肯定就是用戶名了;過(guò)濾代碼如下:(這里使用jsoup解析html獲取各個(gè)document,循環(huán)遍歷剔除不需要的元素)
public void saveUserDataWebView(WebView webView, String html) {
Document document = Jsoup.parse(html);
Elements elements = document.select("input");
boolean isContainsPassword = false;
for (Element element : elements) {
String type = element.attr("type");
if ("password".equals(type)) {
isContainsPassword = true;
break;
}
}
if (!isContainsPassword) {
return;
}
for (Element element : elements) {
String className = element.className();
String type = element.attr("type");
webView.post(new Runnable() {
@Override
public void run() {
LogUtils.e("this element id is = " + element.attr("id") + " type = " + type);
String id = element.attr("id");
if (filterData(type, id)) {
int handType = handleType(type);
if (handType == NONE) {
handType = handleId(id);
if (handType == NONE) {
handleClassName(className);
}
}
switch (handType) {
case PASSWORD:
if (id==null){
}else {
savePasswordById(id, webView);
}
break;
case USERNAME:
if (id==null){
}else {
saveUsernameById(id, webView);
}
break;
case NONE:
break;
}
}
}
});
}
}
private int handleClassName(String className) {
if (className == null) {
return ERROR;
}
if (className.contains("password")) {
return PASSWORD;
}
if (className.contains("captcha")) {
return ERROR;
}
return USERNAME;
}
private boolean filterData(String type, String id) {
if ("captcha".equals(type)) {
return false;
} else if ("login_vcode".equals(type)) {
return false;
} else if ("button".equals(type)) {
return false;
} else if ("hidden".equals(type)) {
return false;
} else if ("submit".equals(type)) {
return false;
} else if ("checkbox".equals(type)) {
return false;
} else if ("captcha".equals(id)) {
return false;
} else if ("inp_ChkCode".equals(id)) {
return false;
} else {
return true;
}
}
private int handleId(String id) {
if (id == null) {
return NONE;
}
if (id.contains("captcha")) {
return ERROR;
}
if (id.contains("password")) {
return PASSWORD;
}
if (id.contains("Phone")) {
return USERNAME;
}
if (id.contains("username")) {
return USERNAME;
}
if (id.contains("code")) {
return ERROR;
}
return USERNAME;
}
private int handleType(String type) {
if (type == null) {
return NONE;
}
if (type.contains("tel")) {
return ERROR;
}
if (type.contains("pwd")) {
return PASSWORD;
}
if (type.contains("password")) {
return PASSWORD;
}
return NONE;
}
將他們倆的class id記錄下來(lái),再次通過(guò)js代碼獲取到頁(yè)面的value值,調(diào)用java代碼保存下來(lái)。代碼如下:
private void saveUsernameById(String id, WebView webView) {
webView.loadUrl("javascript:window.android.save_username(document.getElementById('" + id + "').value)");
}
private void savePasswordById(String id, WebView webView) {
webView.loadUrl("javascript:window.android.save_password(document.getElementById('" + id + "').value)");
}
經(jīng)過(guò)上面簡(jiǎn)單的處理,已經(jīng)大致可以獲取到用戶輸入的帳號(hào)密碼了,經(jīng)過(guò)測(cè)試,簡(jiǎn)單的頁(yè)面中的帳號(hào)密碼是可以獲取到的,其他復(fù)雜的(如密碼在轉(zhuǎn)跳時(shí)清空了,又傳值到其他地方進(jìn)行運(yùn)算的)需要再根據(jù)不同的方案來(lái)對(duì)付了。
轉(zhuǎn)跳前先獲取整個(gè)頁(yè)面的html,用jsoup獲取頁(yè)面的所有class name,遍歷各個(gè)節(jié)點(diǎn),剔除無(wú)用內(nèi)容(驗(yàn)證碼按鈕等),判斷密碼框在哪,剩下的可能就是帳號(hào)了,執(zhí)行js代碼獲取value值。
以上就是本文的全部?jī)?nèi)容,希望大家喜歡。
- Android高仿微信支付密碼輸入控件
- Android自定義控件通用驗(yàn)證碼輸入框的實(shí)現(xiàn)
- Android如何禁止向EditText控件中輸入內(nèi)容詳解
- Android用戶輸入自動(dòng)提示控件AutoCompleteTextView使用方法
- Android 帶清除功能的輸入框控件實(shí)例詳解
- Android開(kāi)發(fā)中給EditText控件添加TextWatcher監(jiān)聽(tīng)實(shí)現(xiàn)對(duì)輸入字?jǐn)?shù)的限制(推薦)
- Android帶清除功能的輸入框控件EditTextWithDel
- Android輸入框控件ClearEditText實(shí)現(xiàn)清除功能
- Android實(shí)現(xiàn)EditText控件禁止輸入內(nèi)容的方法(附測(cè)試demo)
- Android自定義view實(shí)現(xiàn)輸入控件
相關(guān)文章
Android LaunchMode四種啟動(dòng)模式詳細(xì)介紹
這篇文章主要介紹了Android LaunchMode四種啟動(dòng)模式詳細(xì)介紹的相關(guān)資料,這里對(duì)launchmode的使用方法進(jìn)行了詳解及啟動(dòng)模式的比較,需要的朋友可以參考下2016-12-12
Android列表實(shí)現(xiàn)(1)_數(shù)組列表實(shí)例介紹
最近開(kāi)始學(xué)習(xí)android的ui,先上幾個(gè)相關(guān)的例子,后續(xù)還會(huì)有更新,感興趣的朋友可以研究下2012-12-12
Android底部導(dǎo)航欄的動(dòng)態(tài)替換方案
這篇文章主要為大家詳細(xì)介紹了Android底部導(dǎo)航欄的動(dòng)態(tài)替換方案,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-12-12
Android應(yīng)用創(chuàng)建桌面快捷方式代碼
這篇文章主要為大家詳細(xì)介紹了Android應(yīng)用創(chuàng)建桌面快捷方式代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-09-09
AndroidStudio插件GsonFormat之Json快速轉(zhuǎn)換JavaBean教程
這篇文章主要介紹了AndroidStudio插件GsonFormat之Json快速轉(zhuǎn)換JavaBean教程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-04-04
Android 兩種啟動(dòng)模式的實(shí)例詳解
這篇文章主要介紹了Android 兩種啟動(dòng)模式的實(shí)例詳解的相關(guān)資料,Activity的兩種啟動(dòng)模式:FLAG_ACTIVITY_CLEAR_TOP和FLAG_ACTIVITY_REORDER_TO_FRONT ,需要的朋友可以參考下2017-08-08
Android貝塞爾曲線實(shí)現(xiàn)加入購(gòu)物車拋物線動(dòng)畫(huà)
這篇文章主要為大家詳細(xì)介紹了Android貝塞爾曲線實(shí)現(xiàn)加入購(gòu)物車拋物線動(dòng)畫(huà),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-06-06
Android消息通知Notification常用方法(發(fā)送消息和接收消息)
最近在做消息通知類Notification的相關(guān)業(yè)務(wù),利用閑暇時(shí)間總結(jié)一下,主要分為兩部分來(lái)記錄:發(fā)送消息和接收消息,對(duì)Android消息通知相關(guān)知識(shí)感興趣的朋友一起看看吧2024-02-02
Android自定義相機(jī)Camera實(shí)現(xiàn)手動(dòng)對(duì)焦的方法示例
這篇文章主要介紹了Android自定義相機(jī)Camera實(shí)現(xiàn)手動(dòng)對(duì)焦的方法示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-06-06

