在WebView中对第三方H5页面的文本密码框添加自定义随机键盘

前言:

首先介绍一下这个需求的背景,由于公司是涉及到金融行业的需要与银行对接资金存管。出于保密性这里不直接列出公司名字和银行名字。从2018年国家对金融行业大整改以来,为了能够顺利通过备案,我们也跟着政府的脚步一步一步走向合规。

好了,大致就是因为要通过备案,必须把这个需求实现,否则将不会通过。

需求内容就是,当客户端有关资金交易的时候,会通过加密数据以及秘钥的方式把第三方银行的页面(充值、提现、投资...)等通过解析前面的加密数据获取到里面的链接用WebView把第三方银行的页面加载出来,并且此时必须对第三方页面的文本框密码框做拦截监听,而且要实现公司自己的随机键盘,屏蔽系统键盘。为什么要屏蔽系统键盘?因为怕手机被root,键盘被植入的木马监听!那为啥自定义键盘不会被监听,因为自定义键盘属于应用。要监听你的应用那就要破解反编译再签名打包等,如果做到这一点了,那说明你的应用本身就不安全了,要不要自定义键盘也没啥用。

起初,觉着挺简单,毕竟以前做过自定义的键盘。然后就直接开撸。

等到随机键盘做出来以后,我才发现,页面并不是我们公司自己的,而是第三方的,如果需要第三方页面调用我们的接口,双方公司不是必须要协商好接口协议吗,此时才能调用我们自定义的键盘。

关键的地方就是在于,银行不可能因为你一家的需求改自己的公共页面。

分析完之后,我考虑到几个问题:

1.首先我们要能够获取并且设置这个框的焦点事件。

2.我们的自定义键盘如何绑定到这个文本框,看过android源码的都知道EditText是如何与系统输入法绑定的。

这个两个问题解决了,应该基本就可以实现了。再仔细分析一下,这两个问题其实就是一个问题,那就是如何获取到当先密码框的Object,对这个Object设置监听,设置值等一系列操作只要获取的这个Object了,那就是几行代码就能解决的问题了。

思路:

获取到网页源码分析密码框的特性做出筛选(Input标签type=password就是密码框了)。

解析出来type=password的Input标签通过遍历的方式得到ID(input.getElementById(id)就可以得到这个密码框的Object了)。

回到上面1中,设置这个框的焦点事件,我们能够通过解析出这个Object了,那么该怎么把监听注入到这个Object中?为什么说要注入进去,因为页面是别人的,你总不能在别人页面中添加js代码吧,只能是注入js代码。

思路分析清楚了,那流程就好办了。

解决方案:

1.遍历网页input标签并且type=password的标签并且添加onFocus事件

webView.loadUrl("javascript:(function(){" +
				"var objs = document.getElementsByTagName(\"input\");" +
				"for(var i=0;i<objs.length;i++)" +
				"{"
				+"var type = objs[i].getAttribute(\"type\");"
				+"if(type==\"password\"){"
				+"objs[i].οnfοcus=function()" +
				"{"
				+ "window.androidWebView.openSoftInput(this.id,this.type);" +
				"}" +
				"}" +
				"}" +
				"})()");

这段代码逻辑应该都能看懂,就是WebView中加载js的方法loadUrl加载你自己写的js事件,这就把js代码注入到网页中并且执行。主要解释一下window.androidWebView.openSoftInput(this.id,this.type)这一句。androidWebView是

webView.addJavascriptInterface(new JavascriptInterface(),
				"androidWebView");

在这一句中给你的JavascriptInterface类相当于起的一个给网页调用的别名。后面openSoftInput(this.id,this.type)是你在JavascriptInterface这个类中定义的函数名括号里面是参数都是String类型。这样在党文本框获取到焦点的时候,会调用你在JavascriptInterface类中定义的openSoftInput函数,并且把当前密码框的id,type传过去(其实type后来我发现没用着,但是留着日后万一可以拓展)这时候就是java代码了,你在openSoftInput函数中写你如何调用自定义键盘的逻辑就可以了。

2.自定义键盘假装你已经写好了(网上很多代码这里也不提供了)

这个键盘可以通过dialog、popWindow等方式弹出,然后就是如何把输入的值设置到当前密码框。首先自定义的键盘要想跟系统键盘一样,可以对此input密码框增删查改的话,必须拿到一个类似EditTextView中一个Editable的对象,对这个对象进行操作。但是这个是网页,如果要实现那很繁琐,这里给大家提供一个简单的思路,就是在自定义的键盘上面提供一个android原生的EditTextView,把你的密码首先输入到这个文本框,等到自定义键盘dismiss或者点击上面的ok按钮时,通过上面WebView.loadUrl(....)的方式设置给这个密码框,这样简单很多。请看:

//如果输入的是\符号会被转义,需要输入\\两次才能设置进去
	private void setPassword(final String id ,final String pwd){
		webView.post(new Runnable() {
			@Override
			public void run() {
				webView.loadUrl("javascript:(function(){" +
						"var objs = document.getElementById(\""+id+"\");" +
						"objs.value=\""+pwd +"\";"+
                        "objs.blur();" +
                        "})()");
			}
		});
    }

这里由于直接用WebView.loadUrl会抛出一个异常,大致意思就是线程不能执行这个操作,所以就用了异步的方式。在设置值objs.value=pwd后执行objs.blur让此时的密码框失去焦点,为啥要这样做,因为我们注入的是onFocus事件,如果不让它失去焦点的话,你再点击到当前密码框,onFocus事件不会调用,所以你的键盘就弹不出来。特别注意:经过测试这种设置的方式目前发现两个特殊符号要做转义处理,否则这个值不但设置不进去而且你的js代码不会执行(是js报错了,但是不会引起程序崩溃)。两个符号是双引号(")、反斜杠(\)。

细节处理:怎么处理呢,就是把你的pwd拆分出来,如果有(")或者(\)就在他们前面添加一个(\)来转义一下例如:我输入的密码是(" " " " " ")六个双引号,那么你处理过后给js设置的时候应该是(\"\"\"\"\"\")。再例如我输入的密码是(\\\\\\)六个反斜杠那么你处理后给js设置的时候应该是(\\\\\\\\\\\\)没错十二根斜杠,因为每一根你都要给他转义,哈哈哈!

基本思路和核心代码都写出来了,相信大家应该可以自己撸起来了。

事后我感觉这种入侵代码的方式颇有爬虫的感觉,哈哈,大概暴力破解密码的方式就是基于这种思想来玩的吧!

以上代码写的很难看,用了很多+号什么的,因为项目本身代码是要保密的,这是临时撸的,思想大家理解了就可以有千万个实现方式。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章