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);
}//-----此处为原页面代码,不做解释-----
varpageFormData=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(newCommonWebViewClient(){//添加自定义的WebViewClient
@Override
publicvoidonPageFinished(WebViewview,Stringurl){//重写onPageFinished方法
super.onPageFinished(view,url);
//请求js的网址
runRemoteJs(Constant.QueryCarPrice.loadJsUrl_CarHome);
}
privatevoidrunJs(StringremoteJs){//把获取到的js代码添加到当前网页
if(TextUtils.isEmpty(remoteJs)){
return;
}
Stringjs="javascript:";//作用:指明字符串后面的都是js代码
js+="varscript=document.createElement('script');";//作用:创建script节点
js+="script.type='text/javascript';";
js+=remoteJs;
mCommonWebView.callJsFunction(js);//加载js代码
}
privatevoidrunRemoteJs(Stringurl){//前端大大提供的一个网址,网址里面就是上面的js代码,将网页中的代码获取下来
RxRequestrequest=newRxRequest()
.setUrl(url)
.setMethod(Request.Method.GET);
RxHttpEngineWrapper.commonExec(request)
.subscribeOn(AndroidSchedulers.mainThread())
.subscribe(newUtilsRx.DefaultSubscriber(){
@Override
publicvoidonNext(Strings){
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
privatevoidinitWebView(){
mWebView.getSettings().setDomStorageEnabled(true);
mWebView.getSettings().setDefaultTextEncodingName("utf-8");
if(Build.VERSION.SDK_INT>=21){//AddedinAPIlevel21
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(newMyWebViewClient());
//与js通讯的桥梁
mWebView.addJavascriptInterface(newStubClass(),"stub");
}
publicclassMyWebViewClientextendsWebViewClient{
/*两个shouldInterceptRequest方法体中的内容大致相同,因为是demo,我也没有抽取方法*/
@Override
publicWebResourceResponseshouldInterceptRequest(WebViewview,Stringurl){
//获取的请求参数的Map集合
HashMapparams;
Uriuri=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的构造器三个参数作用StringmimeType:指定替换资源的类型Stringencoding:字符集InputStreaminput:输入流
returnnewWebResourceResponse("application/x-javascript","UTF-8",getAssets().open("index.js"));
}catch(IOExceptione){
e.printStackTrace();
}
}
}
returnsuper.shouldInterceptRequest(view,url);
}
//API21及21以后才支持此方法
@RequiresApi(api=Build.VERSION_CODES.LOLLIPOP)
@Override
publicWebResourceResponseshouldInterceptRequest(WebViewview,WebResourceRequestrequest){
//获取的请求参数的Map集合
HashMapparams;
Stringmethod=request.getMethod();//当前网址的提交方式
MaprequestHeaders=request.getRequestHeaders();//获取请求头
Uriuri=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的构造器三个参数作用StringmimeType:指定替换资源的类型Stringencoding:字符集InputStreaminput:输入流
returnnewWebResourceResponse("application/x-javascript","UTF-8",getAssets().open("index.js"));
}catch(IOExceptione){
e.printStackTrace();
}
}
}
returnsuper.shouldInterceptRequest(view,request);
}
privatebooleanrightUrl(Stringurl){
if(url.contains(COLLECT_URL))//判断资源网址是否是我需要的
returntrue;
returnfalse;
}
privateHashMapparamForGET(Uriuri){
HashMapparams=newHashMap<>();
SetparamNames=uri.getQueryParameterNames();//获取此get请求中所有的参数名
/*我这里是将所有的参数都填了进去,大家在获取的时候可以进行筛选和过滤*/
for(Stringparam:paramNames){
params.put(param,uri.getQueryParameter(param));//存储键值对
}
returnparams;
}
}
publicclassStubClass{
@JavascriptInterface
publicvoidgetData(Stringjson){
Log.i("xxx","json->"+json);
}
}
这是我本地的js,对原来的js进行了修改,添加了与Android通讯的桥梁,来截取数据。
补充知识:androidWebView使用Post请求和设置浏览器弹框
这里要注意:post请求参数只能传byte数组,而且必须是键值对字符串形式的byte数组,其中的key是后台服务器接收key,后台规定key是什么值就是什么值,不能随意更改,没有key=value格式或者key不正确,都会请求不到数据网页打不开。
下面代码直接看initWebView()方法就好
packagecom.xxxxx.xxx.activity.banksign;
importorg.json.JSONException;
importorg.json.JSONObject;
importandroid.app.AlertDialog;
importandroid.content.DialogInterface;
importandroid.graphics.Bitmap;
importandroid.os.Bundle;
importandroid.util.Log;
importandroid.view.KeyEvent;
importandroid.view.View;
importandroid.view.View.OnClickListener;
importandroid.webkit.JsResult;
importandroid.webkit.WebChromeClient;
importandroid.webkit.WebSettings;
importandroid.webkit.WebView;
importandroid.webkit.WebViewClient;
importcom.xinzong.etc.R;
importcom.xinzong.xx.base.BaseGestureActivty;
importcom.xinzong.xxx.utils.ShowReloadUtil;
/**
*
*@author
*
*/
publicclassWebViewActivityextendsBaseGestureActivtyimplementsOnClickListener{
privateShowReloadUtilreloadUtil;
privateStringurl="http://120.1.1.1/xx/xxxx";
privateWebViewwebView;
privateStringurlParameter="";
@Override
protectedvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sign_webview);
findViewById(R.id.ibBack).setOnClickListener(this);
//获取传过来的支付参数
urlParameter=getIntent().getStringExtra("urlParameter");
Log.i("TAG",urlParameter);
//初始化重新加载框
reloadUtil=newShowReloadUtil(this);
reloadUtil.setReloadView(this,R.id.ll_show_data_mc,
R.id.rl_reload_parent_mc);
//刷新界面,加载webview
refresh();
}
privatevoidrefresh(){
if(isNetworkConnected()){
findView(R.id.webview1).setVisibility(View.VISIBLE);
reloadUtil.showDataView();
initWebView();
}else{
findView(R.id.webview1).setVisibility(View.GONE);
reloadUtil.showReload();
}
}
privatevoidinitWebView(){
webView=(WebView)findViewById(R.id.webview1);
//初始化webview
//启用支持javascript
WebSettingssettings=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(newMyWebChromeClient());//设置浏览器可弹窗
//覆盖WebView默认使用第三方或系统默认浏览器打开网页的行为,使网页用WebView打开
webView.setWebViewClient(newWebViewClient(){
@Override
publicbooleanshouldOverrideUrlLoading(WebViewview,Stringurl){
//返回值是true的时候控制去WebView打开,为false调用系统浏览器或第三方浏览器
Log.d("TAG","url:"+url);
view.loadUrl(url);
returntrue;
}
@Override
publicvoidonPageStarted(WebViewview,Stringurl,
Bitmapfavicon){
Log.d("TAG","onPageStarted--url:"+url);
//支付完成后,点返回关闭界面
if(url.endsWith("http://120.1.1.1/xxx/xx/xxx")){
finish();
}
super.onPageStarted(view,url,favicon);
}
@Override
publicvoidonPageFinished(WebViewview,Stringurl){
super.onPageFinished(view,url);
}
});
}
@Override
publicvoidonClick(Viewv){
if(v.getId()==R.id.btnReload){//点击‘重新加载'
reloadUtil.showClickloadingView();
Log.d("TAG","RELOAD");
if(this.isNetworkConnected()){
webView.loadUrl(url);
}else{
reloadUtil.showReload();
}
}elseif(v.getId()==R.id.ibBack){
if(webView!=null&&webView.canGoBack()){
webView.goBack();
}else{
finish();
}
}
}
@Override
publicbooleanonKeyDown(intkeyCode,KeyEventevent){
if(keyCode==KeyEvent.KEYCODE_BACK&&webView!=null&&webView.canGoBack()){
webView.goBack();
returntrue;
}
returnsuper.onKeyDown(keyCode,event);
}
/**
*浏览器可弹窗
*
*@authorAdministrator
*
*/
finalclassMyWebChromeClientextendsWebChromeClient{
@Override
publicbooleanonJsConfirm(WebViewview,Stringurl,Stringmessage,
finalJsResultresult){
newAlertDialog.Builder(CTX)
.setTitle("AppTitler")
.setMessage(message)
.setPositiveButton(android.R.string.ok,
newDialogInterface.OnClickListener(){
publicvoidonClick(DialogInterfacedialog,
intwhich){
result.confirm();
}
})
.setNegativeButton(android.R.string.cancel,
newDialogInterface.OnClickListener(){
publicvoidonClick(DialogInterfacedialog,
intwhich){
result.cancel();
}
}).create().show();
returntrue;
}
}
}
以上这篇AndroidWebView通过动态的修改js去拦截post请求参数实例就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持毛票票。