Android WebView中Java与JavaScript的交互方式与安全策略

软件工程

  Android在加载网页H5时,会用到WebView组件,以实现服务端可配置,灵活定义Action。

  这个时候就需要Java与JavaScript进行交互,典型的应用场景就是电商类APP,如京东和淘宝。

  WebViewJavaScript这些交互动作,那么问题来了,我们怎么实现Java编写的安卓程序与JavaScript编写的网页进行交互呢?下面给大家介绍下,如何实现Java与JavaScript之间的相互调用。

  在Java层调用JavaScript的方式:

  Java中调用JavaScript有两种方式,同步和异步:

  同步阻塞UI线程的方式: webView.loadUrl("javascript:funtion()")

  异步非阻塞方式: evaluateJavaScript()(在API level 19加入)

  请看下面代码:

  java中

  // android 调用 JavaScriptpublic void callJavaScript(View view) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { // 异步运行调用JavaScript的方式 mWebView.evaluateJavascript("javascript:callAlert()", new ValueCallback<String>() { @Override public void onReceiveValue(String value) { Log.e(TAG, "onReceiveValue" + value);//打印返回的值 } }); } else { if (mWebView != null) { // 同步阻塞UI线程式调用 mWebView.loadUrl("javascript:callAlert()"); } }}JavaScript中

  function callAlert(){ alert("Android call html Alert function !"); return "这是需要返回的值";}在JavaScript调用Android中Java的方式

  在JavaScript有三种方式可以调用java代码:

  使用 mWebView.addJavascriptInterface(new JavaScriptInject(this), "JavaScriptInject")

  java中

  mWebView = findViewById(R.id.webview);mWebView.getSettings().setAllowFileAccessFromFileURLs(true);mWebView.addJavascriptInterface(new JavaScriptInject(this), "JavaScriptInject");public class JavaScriptInject { private Context mContext; JavaScriptInject(Context context) { this.mContext = context; } // 被JS调用的方法必须加入@JavascriptInterface注解 @JavascriptInterface public void callAndroid(String string) { Toast.makeText(mContext,"javascript call Android", Toast.LENGTH_LONG).show(); Log.e("JavaScriptInject", "getAndroidInfo " + string); }}JavaScript中

  function callAndroid(){ JavaScriptInject.callAndroid("javascript call android");}2. 通过重写WebChromeClient中的回调方法拦截JS不同对话框消息:

  onJsAlert(WebView view, String url, String message, JsResult result) 对应拦截 alert()

  onJsConfirm(WebView view, String url, String message, JsResult result) 对应拦截 confirm()

  onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) 对应拦截 prompt()

  java中

  mWebView.setWebChromeClient(new WebChromeClient() { @Override public boolean onJsAlert(WebView view, String url, String message, JsResult result) { Log.i(TAG,"onJsAlert"); return super.onJsAlert(view, url, message, result); } @Override public boolean onJsConfirm(WebView view, String url, String message, JsResult result) { Log.i(TAG,"onJsConfirm"); return super.onJsConfirm(view, url, message, result); } @Override public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) { Log.i(TAG,"onJsPrompt:"+ message); return super.onJsPrompt(view, url, message, defaultValue, result); }});JavaScript中

  function clickPrompt(){ prompt("传递给Android的值");}function clickConfirm(){ confirm("传递给Android的值");}function clickAlert(){ alert("传递给Android的值");}webview页面中

  <body> <button type="button" id="button3" onclick="clickPrompt()">点击调用Android onJsPrompt</button> <button type="button" id="button4" onclick="clickConfirm()">点击调用Android onJsConfirm</button> <button type="button" id="button5" onclick="clickAlert()">点击调用Android onJsAlert</button></body>3. 通过 WebViewClient 的 shouldOverrideUrlLoading 回调,拦截url。

  Java中

  mWebView.setWebViewClient(new WebViewClient() {@Override public boolean shouldOverrideUrlLoading(WebView view, String url) { // 解析该 url 的协议,如果检测到是预先约定好的协议,就调用相应方法 Log.e(TAG,"call shouldOverrideUrlLoading url : " + url); return true; }});JavaScript中

  function callLoadProtocol(){ // 在JS约定所需要的Url协议 document.location = "http://www.360.cn?p=ppp&q=qqq";}安全方面策略

  WebView的addJavascriptInterface方法在Android 4.2版本以下使用时可能出现的安全情况有:

  WebView所在页面对外暴露,即AndroidManifest.xml设置了android:exported="true",会被其他应用恶意调起

  网络劫持导致代码注入

  2. 开发者需对加载的网页Url多加限制,禁止加载非合法url。

  3. WebView对证书错误的页面会打开空白页。如果当遇到证书错误时调用proceed()函数,会忽略证书错误继续访问,导致https证书校验完全失效,存在安全隐患。

  Java中

  mWebView.setWebViewClient(new WebViewClient() { @Override public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { handler.proceed();//使用proceed()忽略证书错误,会存在安全隐患 }}4. WebView加载File协议的Url存在安全隐患,需要禁止File协议调用JavaScript,设置为false。如下即可拿到file:///sdcard/test.txt文件内容,这是很危险的情况。

  Java中

  mWebView.getSettings().setJavaScriptEnabled(true);mWebView.getSettings().setAllowFileAccessFromFileURLs(true);JavaScript中

  function getFileContent(){ var file = "file:///sdcard/test.txt"; var xmlHttpReq = new XMLHttpRequest(); xmlHttpReq.onreadystatechange = function(){ if (xmlHttpReq.readyState == 4) { //4: 请求已完成,且响应已就绪 alert(xmlHttpReq.responseText);//执行上传到远程 } } xmlHttpReq.open("GET",file,true); xmlHttpReq.s();}getFileContent();5. Android3.0以下,Android系统会默认通过searchBoxJavaBridge的Js接口给WebView添加一个JS映射对象:searchBoxJavaBridge对象。该接口可能被利用,实现远程任意代码,实现中可以进行删除该接口。

  Java中

  super.removeJavascriptInterface("searchBoxJavaBridge_");6. 为了解决Android4.2中开启了辅助模式后,LocalActivityManager控制的Activity与AccessibilityInjector不兼容导致的崩溃问题可以在mWebView.getSettings().setJavaScriptEnabled之前执行下面代码。

  Java中

  public void fixedAccessibilityInjectorException() { if (Build.VERSION.SDK_INT == 17) { try { Object webViewProvider = WebView.class.getMethod("getWebViewProvider").invoke(this); Method getAccessibilityInjector = webViewProvider.getClass().getDeclaredMethod("getAccessibilityInjector"); getAccessibilityInjector.setAccessible(true); Object accessibilityInjector = getAccessibilityInjector.invoke(webViewProvider); getAccessibilityInjector.setAccessible(false); Field mAccessibilityManagerField = accessibilityInjector.getClass().getDeclaredField("mAccessibilityManager"); mAccessibilityManagerField.setAccessible(true); Object mAccessibilityManager = mAccessibilityManagerField.get(accessibilityInjector); mAccessibilityManagerField.setAccessible(false); Field mIsEnabledField = mAccessibilityManager.getClass().getDeclaredField("mIsEnabled"); mIsEnabledField.setAccessible(true); mIsEnabledField.set(mAccessibilityManager, false); mIsEnabledField.setAccessible(false); } catch (Exception e) { e.printStackTrace(); } }}7. 为了防止Js拿到Android对象后,通过反射(通过getClass()方法来得到Runtime实例)获取该对象的其他所有方法,从而导致信息泄露和恶意代码注入,需要过滤掉继承Object类的方法,包括getClass()方法。过滤方法包括:

  getClass

  hashCode

  notify

  notifyAll

  equals

  toString

  wait

  免责声明:转载自网络 不用于商业宣传 版权归原作者所有 侵权删

标签: 软件工程