Android WebView通过动态的修改js去拦截post请求参数实例
需求背景:
需要在用户点击提交按钮的时候拦截用户提交的数据。
遇到的问题:
1.页面不是自家前端做的,不能修改网页中的代码
2.要拦截的请求不是get请求,而是一个post请求 (难点在于:如果拦截的请求是get请求的话,我只需要拿到url,将后面拼接的参数键值对取出来就好了,但是post请求的参数键值对我们是看不到的。。。)
解决重点:
重写webviewclient的shouldinterceptrequest这个方法
1.这个方法是api21以后才出现的,还有一个过时的方法也要重写,不要忘了!
2.在加载网页时,所有的资源都会经过shouldinterceptrequest这个方法,我们可以通过shouldinterceptrequest和抓包工具(fidder,charles)去获取你想要获取信息的网址和资源文件
3.这个方法是执行在子线程的,如果你想要更新ui的话,记得切换线程
解决方案:
我这里找到了两种解决方案(总有一款适合你)
方案a : 适合 精通js 的大大们
1.拦截页面上按钮的点击事件,将点击事件的操作进行替换
$('#j_submit').off('click'); //1.将id为j_submit的按钮点击事件关闭 $('#j_submit').on('click',function(){ //2.将id为j_submit的按钮点击事件重新打开,并执行function里的内容 if ($(this).hasclass("btn-disabled")) { // ----- 此处为原页面代码,不做解释 ----- return; } try { trackdealerevent('dlr_order_page_form_submit_click', { 'esfrom': _mediaid, 'business': 'songshu', 'series': _seriesid, 'city': _cityid }); } catch (e) { console.log(e); } // ----- 此处为原页面代码,不做解释 ----- var pageformdata = validateallfield(alertdiv); if (pageformdata) { //3.获取到页面内的数据 $.ajax({ //4.ajax方式上传到服务器中 url: 'https://gouche.jxedt.com/gouche/clue/submit', data: { cityid: _cityid, brandid: _brandid, seriesid: _seriesid, classesid: _specid, name: $("[name='username']").val(), phone: $('#phonenumber').val(), type: 4 } }); postorder(pageformdata); } })
2.动态的加载一段js代码
mcommonwebview.setcommonwebviewclient(new commonwebviewclient() { //添加自定义的 webviewclient @override public void onpagefinished(webview view, string url) { //重写onpagefinished方法 super.onpagefinished(view, url); //请求js的网址 runremotejs(constant.querycarprice.loadjsurl_carhome); } private void runjs(string remotejs){ //把获取到的js代码添加到当前网页 if(textutils.isempty(remotejs)) { return; } string js = "javascript:"; //作用:指明字符串后面的都是js代码 js+= "var script = document.createelement('script');"; // 作用:创建script节点 js+= "script.type = 'text/javascript';"; js+=remotejs; mcommonwebview.calljsfunction(js); //加载js代码 } private void runremotejs(string url) {//前端大大提供的一个网址,网址里面就是上面的js代码,将网页中的代码获取下来 rxrequest<string> request = new rxrequest<string>() .seturl(url) .setmethod(request.method.get); rxhttpenginewrapper.commonexec(request) .subscribeon(androidschedulers.mainthread()) .subscribe(new utilsrx.defaultsubscriber<string>(){ @override public void onnext(string s) { runjs(s); } }); } });
3.到时候只要前端的大大修改页面中的js就可以了
此方案的坑:
1.要加载的js代码中不能包含script节点
2.要加载的js代码中不能有注释
3.要加载的js代码一定要加上分号
*如果不满足上面的三点要求,要加载的js都不能正确的执行
方案b : 原生的android方式,相对于上一种方案,这种方案比较麻烦
1.重写shouldinterceptrequest去拦截资源
2.将第三方网页上进行网络请求的js页面下载下来(就是把网页的所有下载下来,找到进行网络请求的js页),对js页进行修改
3.将处理好的js页加载到本地,以后加载时就利用本地的js替换第三方的js(我会在本地的js页面中添加与webview沟通的桥梁)
//以下为具体操作,我把具体的方法贴了上去,如果不太懂的可以看看代码,我写了注释 //初始化webview private void initwebview() { mwebview.getsettings().setdomstorageenabled(true); mwebview.getsettings().setdefaulttextencodingname("utf-8"); if(build.version.sdk_int >=21){//added in api level 21 mwebview.getsettings().setmixedcontentmode(android.webkit.websettings.mixed_content_always_allow); } mwebview.getsettings().setjavascriptenabled(true); mwebview.getsettings().setusewideviewport(true); //设置webview推荐使用的窗口,使html界面自适应屏幕 mwebview.getsettings().setloadwithoverviewmode(true); mwebview.getsettings().setgeolocationenabled(true); mwebview.getsettings().setallowfileaccess(true); if (build.version.sdk_int >= 16) { //屏蔽webview的跨域漏洞 mwebview.getsettings().setallowfileaccessfromfileurls(false); mwebview.getsettings().setallowuniversalaccessfromfileurls(false); } mwebview.getsettings().setpluginstate(websettings.pluginstate.on); if (build.version.sdk_int >= 11) { mwebview.getsettings().setallowcontentaccess(true); } mwebview.loadurl(currurl); mwebview.setwebviewclient(new mywebviewclient()); //与js通讯的桥梁 mwebview.addjavascriptinterface(new stubclass(),"stub"); } public class mywebviewclient extends webviewclient { /*两个shouldinterceptrequest方法体中的内容大致相同,因为是demo,我也没有抽取方法*/ @override public webresourceresponse shouldinterceptrequest(webview view, string url) { //获取的请求参数的 map 集合 hashmap<string,string> params; uri uri=uri.parse(url); //获取网址对应的uri if (righturl(uri.tostring())) { /*get请求获取参数*/ params=paramforget(uri); /*重头戏,post请求获取参数*/ /* * 获取post请求参数的思路就是: * 找到其网址中进行网络请求的js代码,对这段js代码进行替换 * 我采取的是拦截第三方网址上请求数据的js资源,将本地的资源提交上去替换原资源 */ if (uri.tostring().contains("index.js")) { //拦截该网页下对应的js资源并进行替换 try { //webresourceresponse的构造器三个参数作用 string mimetype:指定替换资源的类型 string encoding:字符集 inputstream input:输入流 return new webresourceresponse("application/x-javascript","utf-8",getassets().open("index.js")); } catch (ioexception e) { e.printstacktrace(); } } } return super.shouldinterceptrequest(view, url); } //api21及21以后才支持此方法 @requiresapi(api = build.version_codes.lollipop) @override public webresourceresponse shouldinterceptrequest(webview view, webresourcerequest request) { //获取的请求参数的 map 集合 hashmap<string,string> params; string method=request.getmethod(); //当前网址的提交方式 map<string, string> requestheaders = request.getrequestheaders(); //获取请求头 uri uri=request.geturl(); //获取网址对应的uri if (righturl(uri.tostring())) { /*get请求获取参数*/ params=paramforget(uri); /*重头戏,post请求获取参数*/ /* * 获取post请求参数的思路就是: * 找到其网址中进行网络请求的js代码,对这段js代码进行替换 * 我采取的是拦截第三方网址上请求数据的js资源,将本地的资源提交上去替换原资源 */ if (uri.tostring().contains("index.js")) { //拦截该网页下对应的js资源并进行替换 try { //webresourceresponse的构造器三个参数作用 string mimetype:指定替换资源的类型 string encoding:字符集 inputstream input:输入流 return new webresourceresponse("application/x-javascript","utf-8",getassets().open("index.js")); } catch (ioexception e) { e.printstacktrace(); } } } return super.shouldinterceptrequest(view, request); } private boolean righturl(string url){ if (url.contains(collect_url)) //判断资源网址是否是我需要的 return true; return false; } private hashmap<string,string> paramforget(uri uri){ hashmap<string,string> params=new hashmap<>(); set<string> paramnames = uri.getqueryparameternames(); //获取此get请求中所有的参数名 /*我这里是将所有的参数都填了进去,大家在获取的时候可以进行筛选和过滤*/ for (string param : paramnames) { params.put(param,uri.getqueryparameter(param)); //存储键值对 } return params; } } public class stubclass{ @javascriptinterface public void getdata(string json){ log.i("xxx","json -> "+json); } }
这是我本地的js,对原来的js进行了修改,添加了与android通讯的桥梁,来截取数据。
补充知识:android webview使用post请求和设置浏览器弹框
这里要注意:post请求参数只能传byte数组,而且必须是键值对字符串形式的byte数组,其中的key是后台服务器接收key,后台规定key是什么值就是什么值,不能随意更改,没有key=value格式或者key不正确,都会请求不到数据网页打不开。
下面代码直接看initwebview()方法就好
package com.xxxxx.xxx.activity.banksign; import org.json.jsonexception; import org.json.jsonobject; import android.app.alertdialog; import android.content.dialoginterface; import android.graphics.bitmap; import android.os.bundle; import android.util.log; import android.view.keyevent; import android.view.view; import android.view.view.onclicklistener; import android.webkit.jsresult; import android.webkit.webchromeclient; import android.webkit.websettings; import android.webkit.webview; import android.webkit.webviewclient; import com.xinzong.etc.r; import com.xinzong.xx.base.basegestureactivty; import com.xinzong.xxx.utils.showreloadutil; /** * * @author * */ public class webviewactivity extends basegestureactivty implements onclicklistener{ private showreloadutil reloadutil; private string url = "http://120.1.1.1/xx/xxxx"; private webview webview; private string urlparameter = ""; @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.activity_sign_webview); findviewbyid(r.id.ibback).setonclicklistener(this); //获取传过来的支付参数 urlparameter = getintent().getstringextra("urlparameter"); log.i("tag", urlparameter); //初始化重新加载框 reloadutil = new showreloadutil(this); reloadutil.setreloadview(this, r.id.ll_show_data_mc, r.id.rl_reload_parent_mc); //刷新界面,加载webview refresh(); } private void refresh() { if(isnetworkconnected()){ findview(r.id.webview1).setvisibility(view.visible); reloadutil.showdataview(); initwebview(); }else{ findview(r.id.webview1).setvisibility(view.gone); reloadutil.showreload(); } } private void initwebview() { webview = (webview) findviewbyid(r.id.webview1); //初始化webview //启用支持javascript websettings settings = webview.getsettings(); settings.setjavascriptenabled(true);//支持javascript settings.setdefaulttextencodingname("utf-8");//设置网页默认编码 settings.setjavascriptcanopenwindowsautomatically(true); log.d("tag", "url:"+url); //post请求(使用键值对形式,格式与get请求一样,key=value,多个用&连接) urlparameter = "jsonprikey=" +urlparameter; webview.posturl(url, urlparameter.getbytes()); // webview.loadurl(url);//get webview.setwebchromeclient(new mywebchromeclient());// 设置浏览器可弹窗 //覆盖webview默认使用第三方或系统默认浏览器打开网页的行为,使网页用webview打开 webview.setwebviewclient(new webviewclient(){ @override public boolean shouldoverrideurlloading(webview view, string url) { //返回值是true的时候控制去webview打开,为false调用系统浏览器或第三方浏览器 log.d("tag", "url:"+url); view.loadurl(url); return true; } @override public void onpagestarted(webview view, string url, bitmap favicon) { log.d("tag", "onpagestarted--url:"+url); //支付完成后,点返回关闭界面 if(url.endswith("http://120.1.1.1/xxx/xx/xxx")){ finish(); } super.onpagestarted(view, url, favicon); } @override public void onpagefinished(webview view, string url) { super.onpagefinished(view, url); } }); } @override public void onclick(view v) { if (v.getid() == r.id.btnreload) {// 点击 ‘重新加载' reloadutil.showclickloadingview(); log.d("tag", "reload"); if (this.isnetworkconnected()) { webview.loadurl(url); } else { reloadutil.showreload(); } }else if(v.getid() == r.id.ibback){ if(webview !=null && webview.cangoback()){ webview.goback(); }else{ finish(); } } } @override public boolean onkeydown(int keycode, keyevent event) { if(keycode == keyevent.keycode_back && webview !=null && webview.cangoback()){ webview.goback(); return true; } return super.onkeydown(keycode, event); } /** * 浏览器可弹窗 * * @author administrator * */ final class mywebchromeclient extends webchromeclient { @override public boolean onjsconfirm(webview view, string url, string message, final jsresult result) { new alertdialog.builder(ctx) .settitle("app titler") .setmessage(message) .setpositivebutton(android.r.string.ok, new dialoginterface.onclicklistener() { public void onclick(dialoginterface dialog, int which) { result.confirm(); } }) .setnegativebutton(android.r.string.cancel, new dialoginterface.onclicklistener() { public void onclick(dialoginterface dialog, int which) { result.cancel(); } }).create().show(); return true; } } }
以上这篇android webview通过动态的修改js去拦截post请求参数实例就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。