您的位置:首页技术文章
文章详情页

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

【字号: 日期:2022-09-25 13:40:43浏览:70作者:猪猪

需求背景:

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

遇到的问题:

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沟通的桥梁)

//以下为具体操作,我把具体的方法贴了上去,如果不太懂的可以看看代码,我写了注释 //初始化WebViewprivate 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 = '';@Overrideprotected 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);//刷新界面,加载webviewrefresh();} 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); } }); } @Overridepublic 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请求参数实例就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持好吧啦网。

标签: Android
相关文章: