js混合JAVA_Hybrid(混合式) Appz之WebView中如何让JS与Java安全地互相调用

在现在安卓应用原生开发中,为了追求开发的效率以及移植的便利性,使用WebView作为业务内容展示与交互的主要载体是个不错的折中方案。那么在这种Hybrid(混合式)

在现在安卓应用原生开发中,为了追求开发的效率以及移植的便利性,使用WebView作为业务内容展示与交互的主要载体是个不错的折中方案。那么在这种Hybrid(混合式) App中,难免就会遇到页面JS需要与Java相互调用,调用Java方法去做那部分网页JS不能完成的功能。

网上的方法可以告诉我们这个时候我们可以使用addjavascriptInterface来注入原生接口到JS中,但是在安卓4.2以下的系统中,这种方案却我们的应用带来了很大的安全风险。攻击者如果在页面执行一些非法的JS(诱导用户打开一些钓鱼网站以进入风险页面),极有可能反弹拿到用户手机的shell权限。接下来攻击者就可以在后台默默安装木马,完全洞穿用户的手机。详细的攻击过程可以见乌云平台的这份报告:WebView中接口隐患与手机挂马利用。

安卓4.2及以上版本(API >= 17),在注入类中为可调用的方法添加@JavascriptInterface注解,无注解的方法不能被调用,这种方式可以防范注入漏洞。那么有没一种安全的方式,可以完全兼顾安卓4.2以下版本呢?答案就是使用prompt,即WebChromeClient 输入框弹出模式。

我们参照 Android WebView的Js对象注入漏洞解决方案 这篇文章给出的解决方案, 但它JS下的方法有点笨拙,

动态生成JS文件过程也并没有清晰,且加载JS文件的时机也没有准确把握。那么如何改造才能便利地在JS代码中调用Java方法,并且安全可靠呢?

一、动态地生成将注入的JS代码

JsCallJava在构造时,将要注入类的public且static方法拿出来,逐个生成方法的签名,依据方法签名先将方法缓存起来,同时结合方法名称与静态的HostApp-JS代码动态生成一段将要注入到webview中的字符串。

;

}

(cls == (cls == JsCallback.class) {

sign += "_F";

} else {

sign += "_P";

}

}

return sign;

}

从上面可以看出,类的各个方法名称被拼接到前后两段静态压缩的JS代码当中,那么这样生成的完整清晰的HostApp-JS片段是怎样的呢?

我们假设HostJsScope类中目前只定义了toast、alert、getIMSI这三个公开静态方法,那么完整的片段就是下面这样:

({

{

+ res.code + {

return original.apply(hostApp, [property].concat(Array.prototype.slice.call(arguments, 0)));

};

}

});

global.HostApp = hostApp;

console.log("HostApp initialization end");

})(window);

二、HostApp JS片段注入时机

步骤一说明了HostApp-JS片段的拼接方法,同时JS片段拼接是在JsCallJava初始化完成的,而JsCallJava初始化是在实例化InjectedChromeClient对象时发起的。

public InjectedChromeClient (String injectedName, Class injectedCls) {

mJsCallJava = new JsCallJava(injectedName, injectedCls);

}

从步骤一的代码,我们知道JsCallJava拼接出来的JS代码暂时被存到mPreloadInterfaceJS字段中。那么我们何时把这段代码串注入到Webview的页面空间内呢?答案是页面加载进度变化的过程中。

(WebView view, int newProgress) {

//为什么要在这里注入JS

//1 OnPageStarted中注入有可能全局注入不成功,,导致页面脚本上所有接口任何时候都不可用

//2 OnPageFinished中注入,虽然最后都会全局注入成功,但是完成时间有可能太晚,当页面在初始化调用接口函数时会等待时间过长

//3 在进度变化时注入,刚好可以在上面两个问题中得到一个折中处理

//为什么是进度大于25%才进行注入,因为从测试看来只有进度大于这个数字页面才真正得到框架刷新加载,保证100%注入成功

if (newProgress <= 25) {

mIsInjectedJS = false;

} else if (!mIsInjectedJS) {

view.loadUrl(mJsCallJava.getPreloadInterfaceJS());

mIsInjectedJS = true;

Log.d(TAG, " inject js interface completely on progress " + newProgress);

}

super.onProgressChanged(view, newProgress);

}

从上面我们可以看出,注入的时机是准确把握在进度大于25%时。如果在OnPageFinished注入,页面document.ready的初始回调会等待时间过长,详细的原因我们会在后面讲到。

三、页面调用Java方法执行的过程

OK,上面两步解决了动态生成与成功注入的两大问题,接下来就要处理JS具体的调用过程。上面,我们知道页面调用Java方法时,匿名js函数在拼接好参数后prompt json数据。prompt消息被Java层的WebChromeClient.onJsPrompt拦截到。

(WebView view, String url, String message, String defaultValue, JsPromptResult result) {

result.confirm(mJsCallJava.call(view, message));

return true;

}

而JsCallJava.call的具体实现如下。

(("function".equals(currType)) {

sign += "_F";

values[k + 1] = new JsCallback(webView, argsVals.getInt(k));

} else {

sign += "_P";

}

}

Method currMethod = mMethodsMap.get(sign);

// 方法匹配失败

if (currMethod == null) {

return getReturn(jsonStr, 500, "not found method(" + methodName + ") with valid parameters");

}

// 数字类型细分匹配

if (numIndex > 0) {

Class[] methodTypes = currMethod.getParameterTypes();

int currIndex;

Class currCls;

while (numIndex > 0) {

currIndex = numIndex - numIndex / 10 * 10;

currCls = methodTypes[currIndex];

if (currCls == int.class) {

values[currIndex] = argsVals.getInt(currIndex - 1);

} else if (currCls == long.class) {

//WARN: argsJson.getLong(k + defValue) will return a bigger incorrect number

values[currIndex] = Long.parseLong(argsVals.getString(currIndex - 1));

} else {

values[currIndex] = argsVals.getDouble(currIndex - 1);

}

numIndex /= 10;

}

}

return getReturn(jsonStr, 200, currMethod.invoke(null, values));

} catch (Exception e) {

//优先返回详细的错误信息

if (e.getCause() != null) {

return getReturn(jsonStr, 500, "method execute error:" + e.getCause().getMessage());

}

return getReturn(jsonStr, 500, "method execute error:" + e.getMessage());

}

} else {

return getReturn(jsonStr, 500, "call data empty");

}

}

Transformer-CRF模型是一种结合了Transformer和条件随机场(CRF)的模型。它在自然语言处理(NLP)任务被广泛应用,特别是在序列标注任务。 在Transformer-CRF模型,Transformer用于学习输入序列的表示,它通过自注意力机制来捕捉序列的上下文信息。Transformer将输入序列映射为一系列的密集向量表示,这些向量表示被传递给CRF模块。 CRF模块是一个序列标注模型,它利用转移矩阵来建模标签之间的转移概率。CRF模块接收Transformer输出的向量表示作为输入,并通过前馈神经网络对输入进行处理。然后,CRF模块使用动态规划算法来计算最优的标签序列,以最大化整个序列的概率。 通过结合Transformer和CRF,Transformer-CRF模型能够同时捕捉输入序列的上下文信息和标签之间的依赖关系,从而提高序列标注任务的性能。 范例:<<引用:下面是DIET的架构图,可以看到在Transformer之上使用了CRF。Transformer输出的dense vector会传给CRF内部的一个前馈神经网络,另外,在CRF内部存在一个transition matrix(转移状态矩阵)。在这个架构里CRF的作用是完成信息的提取。 。 引用:NLP on Transformers 高手之路137课Pro版:https://appz0c1mshy7438.h5.xiaoeknow.com/v1/goods/goods_detail/p_621c0289e4b04d7e2fd0365a?type=3&share_type=5&share_user_id=u_621b7b85b8dc5_3yDAYnFXeM&entry=2&entry_type=2001。>> Transformer-CRF模型是一种结合了Transformer和条件随机场(CRF)的模型。它在自然语言处理(NLP)任务被广泛应用,特别是在序列标注任务。在这个模型,Transformer用于学习输入序列的表示,而CRF用于建模标签之间的转移概率。通过结合Transformer的上下文信息和CRF的标签依赖关系,Transformer-CRF模型能够提高序列标注任务的性能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值