WebView与JavaScript交互的一种可行性方案

WebView提供的网页和本地交互方式十分简单:
    setJavaScriptEnabled(true);

addJavascriptInterface(Object obj, String interfaceName)PS1;

一个自定义的接口名和一个用来JS调用的方法对象,就可以搭建一个JavaScript桥接接口。当然,简单就意味的着有漏洞,所以才有了后来的反射攻击PS2,android提供的解决方案是在需要JavaScript调用的方法上增加注解@JavaScriptInterface标记,以此来限制JavaScript反射后所能调用的方法。

本地代码如下:

JavaInterface jif;
webView.getSettings().setJavaScriptEnabled(true);
webView.addJavascriptInterface(jif, "testObj");
    /** JS待调用功能集合 */
    public class JavaInterface{
        Context context;
        public JavaInterface(Context context)
        {
            this.context = context;
        }
        @android.webkit.JavascriptInterface
        public void showToast(){
            Toast.makeText(context, "被JavaScript调用的操作", Toast.LENGTH_LONG).show();
        }
    }

网页代码如下,当触发“点击调用按钮”时,便会触发showToast()方法:


虽然在4.2以上使用@JavaScriptInterface(@android.webkit.JavascriptInterface)可以避免反射攻击,但是有时我们难免要去适配低版本的系统,比如4.0,大家在Studio上创建工程时也会注意到,4.0及以上的普及率是98.7%,4.2以下的版本还是有市场占比的。这也意味着我们需要考虑一个万全之策。

 

   我的方案是利用模拟链接弹窗来实现交互。

   大家都知道Intent是如何唤醒电话、浏览器等系统应用的,通过传递一个具有特殊意义的uri地址,其实可以理解为,就是一个双方约定好的特殊字符串。大家在设置WebViewClient时会去重写shouldOverrideUrlLoading方法,来获取页面发起的link链接,这个链接的url是可以获得的。所以,我们可以模仿Intent这种方法,在JavaScript编写时约定好待调用本地方法对应的匹配串,当需要时,以一个伪url地址的形式发起链接,本地在shouldOverrideUrlLoading中对获取的url进行分析,如果是某一本地方法的匹配串,有本地代码来执行,如果不是,就正常加载url。代码如下:

webView.setWebViewClient(new WebViewClient(){
     @Override public boolean shouldOverrideUrlLoading(WebView view,String url)
     {
         // TODO Auto-generated method stub
         Log.w("zheng.li","shouldOverrideUrlLoading:"+url);
         if(url.contains("target:")){
         /**执行相关操作*/
         Toast.makeText(MainActivity.this, "通过识别请求串调用的操作",Toast.LENGTH_LONG).show();
         }else{
         /**继续进行本地加载*/
          view.loadUrl(url);
         }
            return true;
        }
    }); 

网页代码如下:


    这种方法适合实现例如跳转本地页面、跳转第三方页面、本地下载等功能,不适合用来传递参数。当然想要传递参数的话建议加密后挂在匹配串后。

实现数据交互我们需要另外的组件,WebChromeClient提供了获取页面弹窗的方法:

onJsAlert(WebViewview, String url, String message, JsResult result)

Tell the client todisplay a javascript alert dialog. If the client returns true, WebView willassume that the client will handle the dialog. If the client returns false, itwill continue execution.

通知客户端展示一个JS对话框,如果返回true,WebView将允许客户端自行处理这个弹窗,如果返回false,那么继续之后的展示操作。

onJsConfirm(WebViewview, String url, String message, JsResult result)

Tell the client todisplay a confirm dialog to the user. If the client returns true, WebView willassume that the client will handle the confirm dialog and call the appropriateJsResult method. If the client returns false, a default value of false will bereturned to javascript. The default behavior is to return false.

通知客户端展示一个JS确认框,如果返回true,WebView将允许客户端自行处理这个弹窗并适时触发JsResult方法,如果返回false,那么JS脚本将会获得一个专属于false的默认值,默认展示位false状态。

onJsPrompt(WebViewview, String url, String message, String defaultValue, JsPromptResult result)

Tell the client todisplay a prompt dialog to the user. If the client returns true, WebView willassume that the client will handle the prompt dialog and call the appropriateJsPromptResult method. If the client returns false, a default value of falsewill be returned to to javascript. The default behavior is to return false.

通知客户端展示一个JS输入框,如果返回true,WebView将允许客户端自行处理这个弹窗并适时触发JsPromptResult方法,如果返回false,那么JS脚本将会获得一个专属于false的默认值,默认展示位false状态。

结合注解我们发现,三个弹窗都适合用来传递数据,其中onJsPrompt最满足我们的需求,可以根据发送过来的message,提供针对性的返回数据,返回数据类型是字符串。

当我们返回TRUE,弹窗就会被当前的WebView来处理,而不是展示的页面上,入参包含了一个String类型对象,Android将它命名为message,不言而喻这个message就是网页传递给我们的数据,在需要传递参数的地方,通过触发一个alert或者confirm弹窗,将需要的数据传递给本地页面。

如果需要返回参数,通过触发一个prompt弹窗, 将需要的数据传递给本地页面,本地处理完毕后,通过confirm(“返回值”)将数据回传回去。具体代码如下:

webViews.setWebChromeClient(new WebChromeClient(){
      public boolean onJsAlert(WebView view, String url,String message, JsResult result)
            {
                // TODO Auto-generated methodstub
                Log.w("zheng.li","onJsAlert view:"+url);
                Log.w("zheng.li","message:"+message);
                result.confirm();
                return true;
//                returnsuper.onJsAlert(view, url, message, result);
            }
           
      public boolean onJsConfirm(WebView view, String url, String message,JsResult result)
            {
                // TODO Auto-generated methodstub
                Log.w("zheng.li","onJsConfirm view:"+url);
                return super.onJsConfirm(view, url, message,result);
            }
           
      public boolean onJsPrompt(WebView view, Stringurl, String message, String defaultValue,
                    JsPromptResultresult)
            {
                // TODO Auto-generated methodstub
                Log.w("zheng.li","onJsPrompt view:"+url);
                Log.w("zheng.li","message:"+message);
                result.confirm("admin");
                return true;
            }
        });

网页代码如下:

 


这两种方式适用于Android各版本,只是需要双方约定好数据传递规则,可以用作在规范方法之外的辅助使用,毕竟4.0~4.2的时代终将过去,更多的情况下,我们还是按照Google的官方指导办事吧。



最后,在这里祝大家新年快乐,码运昌兴。

 

PS1:addJavascriptInterface这个方法也被所有的安全加固公司热捧,应用源码中出现这个,就意味着有了让你购买服务的借口。

PS2:因为我们在网络上暴漏自己的对象,JavaScript通过interfaceName指向我们的对象obj,就可以利用.getClass().forName(“XXXX”).getMethod().invoke()直接调用用户手机的相关功能,有兴趣的同学可以阅读这篇文章http://www.tuicool.com/articles/jeYVFrN

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值