android里边的WebView是一个经常用到的控件,尤其随着H5的发展,WebView被应用的更多。在使用WebView时,与JS交互是一个常见的场景,这里我简单的总结一下。
开启JS
为何要开启
开启很简单, 但是做之前我们可以先想一下为什么要开启JS支持。 其实开JS支持的最主要作用是使得H5页面可以正常运行,因为H5的火爆相当程度上要依靠JS的支撑,才实现那么多酷炫的效果。
我刚开始接触WebView与JS交互的时候,想当然的认为开启JS支持就是为了与JS交互。自己关闭了JS的支持,调试了一下才体会到原来并不是这个。
如何开启
开启的方法其实相当简单,如下:
webView.getSettings().setJavaScriptEnabled(true);
Java调用JS
Java调用JS相当简单,如下:
String call = "javascript:javaCallJsWithArgs('我来自Java')";
webView.loadUrl(call);
上面的代码让我想到了在Html页面里边调用JS函数也是类似的写法
<a onClick="javascript:javaCallJsWithArgs('我来自Html')">在Html页面里执行的Js函数</a>
不过在Html里边一般使用简写,即把前缀”javascript:”去掉,但是对于WebView来说则不能去掉,否则无法区别是要load一个url还是要执行一个javascript函数了。
JS调用Java
如何调用
API Level < 17 以前我们可以这样写
webView.addJavascriptInterface(new Object(){
public void callJavaFunction(){
Toast.makeText(getApplicationContext(), "通过js调用了java函数", Toast.LENGTH_SHORT).show();
}
}, "jsInteraction");
API Level >= 17 现在需要这样写才能正常调用
webView.addJavascriptInterface(new Object(){
@JavascriptInterface
public void callJavaFunction(){
Toast.makeText(getApplicationContext(), "通过js调用了java函数", Toast.LENGTH_SHORT).show();
}
}, "jsInteraction");
对了,在Html中我们需要怎么调用在Java里定义的方法呢? 如下
<a onClick="window.jsInteraction.callJavaFunction()">点击调用java代码</a>
看起来WebView#addJavascriptInterface()方法将传入的对象加入到了JS中window对象下面了。
上面定义Java逻辑的两种写法的区别在于后者显式的需要我们对要暴露到js中的方法加上注解”@JavascriptInterface”。因为之前的版本引发了一个安全问题,具体见下面的内容。
另一种方法
TO BE ADDED
安全问题
问题产生的原因
在API Level < 17时(即4.2版本之前),JS通过WebView暴露的对象,可以访问其任意的public fields(我专门测试了一下,静态方法和实例方法都可以被访问)。 这里边最严重的是Object#getClass()方法,网上传的都是通过JS调用Java对象的这个方法来达到执行恶意操作的目的,在这里我就不复制粘贴了,参考部分贴的链接里边都有相应的示例代码。
其实,我在老的模拟器里边(genymotion 2.3.7, 4.1.1)调试,发现JS调用getClass().forName()方法并不能正常执行,不知道这些版本模拟器是否经过调整。
解决方案
如果只适配API Level >= 17的设备,那google已经解决了这个问题,通过加上”@JavascriptInterface”注解暴露给JS指定的方法。
如果是适配老的设备 不需要JS与Java交互,但要保证Html页面里的JS正常执行 这种情况比较简单,首先不要addJavaInterface(),此外还要调用webView.removeJavascriptInterface("searchBoxJavaBridge_")删除掉系统自动添加到JS中的interface,名字为searchBoxJavaBridge_。
需要JS与Java交互 首先要执行上面的操作删除暴露给JS的interface。 这里边有另外一种思路[1](看到别人写得,觉得应该可行,不过我并没有试验),即在Html中需要与Java交互的地方调用JS的alert, prompt, confirm方法,然后系统会调用WebChromeClient里对应的回调,即onJsAlert, onJsPrompt, onJsConfirm方法。
拓展思考
前面调试JS调用Java静态方法的时候发现如下的一段log:
D/dalvikvm: GetMethodID: not returning static method LXXX/JSInteractionActivity$JSInterfaceClass;.callJavaFunction ()V
D/: 通过JS调用了Java的静态函数
由此可以推断出JS调用Java方法是通过Java反射来实现的,可以通过查阅WebView的源代码来验证这个猜测。在这里先留给自己一个ToDo Task。
参考
- Android WebView的Js对象注入漏洞解决方案
- WebView注入Java对象注意事项
- 百度网易等数大量安卓应用存在远程代码执行漏洞