JS与Native通讯
addJavascriptInterface
通过Android官方提供的addJavaScriptInterface的方式
该方案的使用非常简单,定义好被调用的方法对象后直接配置映射关系即可。
//定义 Java 接口
public class NativeJsBridge extends Object {
@JavascriptInterface
public void hello(String msg) {
System.out.println("Hello World from Native");
}
}
WebView webview = (WebView) findViewById(R.id.webview);
webview.addJavascriptInterface(new NativeJsBridge(), 'bridge');
webview.loadUrl('http://test.html');
定义好JavascriptInterfac之后,webview中加载的页面就可以直接执行 bridge.hello() 来执行客户端方法了。
URL拦截
拦截url跳转,即WebViewClient的shouldOverrideUrlLoading()方法。shouldOverrideUrlLoading是 Webview 拦截 URL 的一种回调,当 Webview 发生 URL 跳转的时候会触发该回调。在该回调中我们能够获取到前端提供的 URL 地址。我们通过构造约定协议的 URL 地址提供给客户端识别,识别成功后执行对应的方法即可。
方法拦截
方法劫持主要是利用 JS 的一些方法执行时会触发 Android 客户端中的一些回调,通过对前端参数进行识别来执行对应的客户端代码。
这里我们可以拦截Js的alert/confirm/prompt等事件,由于prompt事件在js中很少使用,所以一般拦截该事件。
方案对比
方案1由于在4.2之前,Android的webview内核基于webkit并未正确限制addJavascriptInterface的使用方法,攻击者可以通过Java反射机制实现任意命令执行,存在严重安全漏洞。 在4.2之后,WebView使用Chromium内核,并限制了Jst对Java对象方法的调用权限,只有声明了@JavascriptInterace注解的方法才能被js调用,安全漏洞得到解决。即方案1存在版本兼容问题,假如app支持4.2(api17)以前的机型则不得不考虑其他更安全的方案。
方案2、3原理上来讲其实一样,都是通过定义好一套拦截协议、通讯协议来达到信息传递的效果,只是拦截位置不同罢了。
Native与JS通讯
loadUrl
webview直接访问 javascript:console.log(‘hello’) 这样的伪 URL 即可实现在页面注入或者访问 JS 代码。调用方法如下:
Webview webview = (WebView) findViewById(R.id.webView);
webView.loadUrl("javascript:console.log('hello')");
webView.loadUrl("javascript:jsObjectName.callFromNative();");
这样我们就实现了调用 JS 的目的了。对客户端来说,与JS 交互本质上其实就是一个拼接 JS 字符串的过程。遗憾的是该方式不能直接响应有回调的js请求。如果想要拿到返回的结果的话另外需要 JS 调用客户端的方法返回(即上面js与native通讯的方案)。
evaluateJavascript
在4.4以上系统可以通过evaluateJavascript()方法不仅可以调用js方法还能获取js方法的返回值。
Webview webview = (WebView) findViewById(R.id.webView);
webview.evaluateJavascript("javascript:Date.now()", new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
System.out.println(value); //1515827651551
}
});
可以看到调用方法和 loadUrl() 非常类似,区别是增加了一个 callback 方法可以获取到 JS 返回的值。同时也需要注意使用限制,该方法存在版本兼容问题,只有4.4以上系统才能使用。