正如文章 Android webview(三) addJavascriptInterface的安全问题 中所展示的那样, Android api 17 (Android 4.2)版本之前, 通过WebView.addJavascriptInterface()
方法添加的JavaBridge
存在安全性风险,那么该怎么避免呢?
js向java通信的渠道--prompt
我们知道js中常用的方法: alert
, console.log
, prompt
的调用都会被webview通过WebChromeClient
的相应的回调方法通知Java. 那么我们就可以使用这样的机制来实现js与java的通信.
那为什么选择prompt
呢?因为prompt
方法除了会提供参数,还期待着一个返回值. 因此可以通过设计将其参数映射为对java类和方法的调用, 而返回值这是java方法的返回值.
参数格式设计
对于参数的设计,常见的有2种方式:
- 借鉴Url的格式, 将schema映射为java类名, host映射为方法名, 参数部分则直接对应java方法参数.
- 优点: Url有现成的解析方法, 处理简单
- 缺点: 这种调用方式不够直观, 对于Url不兼容的字符,得进行Base64等方式的预处理
- 在js中直接使用java的语法进行调用
- 优点: 这种方式很直观, 对于特殊字符直接使用
\
进行转义即可 - 缺点: 需要自己实现一个简单的词法分析器, 并实现转义处理以及简单的语法分析
- 优点: 这种方式很直观, 对于特殊字符直接使用
java方法调用
当我们解析出要调用的java类名,方法名和参数时, 该如何去调用呢?
很显然是通过反射.<font color=red>不过要注意的是: </font>不能直接去反射.否则js依然可以直接调用getClass
方法.
我们可以借鉴Android的方案: 提供一个annotation, 反射时判断如果该方法拥有这样的annotation就允许执行,否则禁止.
另外一点需要注意的是: 通过Android提供的addJavascriptInterface()方法添加的JavaBridge的方法将在webview管理的子线程中执行,因此要注意保证这些方法是线程安全的, 而通过在onJsPrompt()方法中反射执行我们的java方法将在主线程中运行, 因此不能执行耗时过长的任务.
可以参考 github: SafeWebView的实现
也可以在gradle中添加如下依赖,直接使用:
//gradle 依赖
compile 'com.yyter.web:SafeWebView:1.0.0'
//用于检测提供给js的方法是否合法 (可选,但建议添加)
debugCompile 'com.yyter.web:SafeWebView-buildcheck:1.0.0'