欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android WebView通过动态的修改js去拦截post请求参数实例

程序员文章站 2024-02-03 10:52:46
需求背景:需要在用户点击提交按钮的时候拦截用户提交的数据。遇到的问题:1.页面不是自家前端做的,不能修改网页中的代码2.要拦截的请求不是get请求,而是一个post请求 (难点在于:如果拦截的请求是g...

需求背景:

需要在用户点击提交按钮的时候拦截用户提交的数据。

遇到的问题:

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请求参数实例就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。