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通讯的桥梁,来截取数据



  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值