快半个月没有更新文章了,最近不少朋友催更了,今天我们进入Android APP漏洞之战系列文章中的一个重要篇幅——WebView漏洞,我们都知道在当下App漏洞中,WebView漏洞的占比是十分巨大的,各种类型的漏洞问题层出不穷,这篇文章就带着大家一起揭开WebView漏洞神奇的面纱。
本文第二节讲述WebView的基本知识
本文第三节讲述WebView的漏洞面
本文第四节进行了漏洞原理介绍和漏洞复现
总结:本文从WebView开发出发,从0开始进行漏洞的讲解和复现,花了几天时间,列举了WebView中的20多种漏洞案例,并手动复现了其中十余种案例,希望用这篇万字长文介绍WebView漏洞的基本发展。
Android WebView在Android平台上是一个特殊的View,它能用来显示网页,这个WebView类可以被用来在app中仅仅显示一张在线的网页,还可以用来开发浏览器。
WebView内部实现是采用渲染引擎(WebKit)来展示view的内容,提供网页前进后退、网页放大、缩小、搜索等功能。Android WebView 在低版本和高版本采用了不同的 webkit 版本内核,在 4.4 版本后使用 Chrome 内核。
Web最简单显示网页内容,基本步骤:
具体操作:
首先,我们在布局文件中来添加WebView控件,如下
然后我们在代码中让WebView控件加载显示网页,如下:
最后我们在配置文件中添加网络权限:
运行程序,显示如下:
首先,我们直接在本地新建js文件
然后我们开启一个简易的http_server的监听
我们编写代码:
然后我们再次运行程序
这里就说明我们远程加载文件成功了,我们点击按钮
可以发现可以成功的通过JS调用Android代码
WebView的状态:
webView.onResume();
webView.onPause();
webView.pauseTimers()
webView.resumeTimers()
rootLayout.removeView(webView)
webView.destory()
前进、后退网页
在不做任何处理前提下,浏览网页时点击系统的“Back”键时,整个 Browser 会调用 finish()而结束自身,因此需要在当前Activity中处理并消费掉该 Back 事件,当按下返回键时,调用goBack方法。
我们可以做一些处理,让点击“Back”键后,让网页返回上一页而不是直接退出浏览器,此时我们可以在当前的Activity中处理Back事件,如下:
清除缓存数据
作用:对WebView进行配置和管理
配置步骤:
第一步:添加访问网络权限(AndroidManifest.xml)
注意:从Android 9.0(API级别28)开始,默认情况下禁用明文支持,会显示 ERR_CLEARTEXT_NOT_PERMITTED
。因此http的url均无法在webview中加载,可以在manifest中application节点添加android:usesCleartextTraffic="true"
。
第二步:生成一个WebView组件(有两种方式)
第三步:进行配置-利用WebSettings子类(常见方法)
常见方法:设置WebView缓存
用来处理各种通知 & 请求事件
shouldOverrideUrlLoading()
作用:打开网页时不调用系统浏览器, 而是在本WebView中显示;在网页上的所有加载都经过这个方法,这个函数我们可以做很多操作。
onPageStarted()
作用:开始载入页面调用的,我们可以设定一个loading的页面,告诉用户程序在等待网络响应。
onLoadResource()
作用:在加载页面资源时会调用,每一个资源(比如图片)的加载都会调用一次。
onReceivedError()
作用:加载页面的服务器出现错误时(如404)调用。
App里面使用webview控件的时候遇到了诸如404这类的错误的时候,若也显示浏览器里面的那种错误提示页面就显得很丑陋了,那么这个时候我们的app就需要加载一个本地的错误提示页面,即webview如何加载一个本地的页面
onReceivedSslError()
作用:处理https请求
webView默认是不处理https请求的,页面显示空白,需要进行如下设置:
c.WebChromeClient
作用:辅助 WebView 处理 Javascript 的对话框,网站图标,网站标题等等。
onProgressChanged()
作用:获得网页的加载进度并显示
onReceivedTitle()
作用:获取Web页中的标题
每个网页的页面都有一个标题,比如www.baidu.com这个页面的标题即“百度一下,你就知道”,那么如何知道当前webview正在加载的页面的title并进行设置呢?
Android WebView与JS的交互:
WebView.loadUrl()
首先,准备html文件,放到assets中
然后在Java层中添加代码,进行调用JS文件以及JS中的方法:
效果演示:
我们可以发现程序成功的加载了html文件,然后我们点击按钮去调用js中的方法
这里就成功的通过loadUrl进行了调用
特别注意:JS代码调用一定要在 onPageFinished()
回调之后才能调用,否则不会调用
WebView.evaluateJavascript()
优点:该方法比第一种方法效率更高、使用更简洁
具体使用:
同样加载成功
addJavascriptInterface
首先,准备html文件,放到assets中
然后加载调用
然后我们切换到第二个测试用例
直接点击
此时已经成功的加载了我们的JS,我们点击按钮
js中就成功的加载了Android中的hello方法
WebViewClient.shouldOverrideUrlLoading ()
具体原理:
即JS需要调用Android的方法
具体使用:
首先,在JS中约定所需要的Url协议,以.html格式放到src/main/assets文件夹里
然后我们新建一个类,并编写代码
运行程序:
这里就调用Android代码成功
WebChromeClient
的onJsAlert()
、onJsConfirm()
、onJsPrompt()
在JS中,上面三种方法分别回调l拦截对话框alert()
、confirm()
、prompt()
的信息:
Android通过 WebChromeClient
的onJsAlert()
、onJsConfirm()
、onJsPrompt()
方法回调分别拦截JS对话框(即上述三个方法),得到他们的消息内容,然后解析即可
然后我们再次编写js代码:
我们编写onJsPrompt 回调方法
运行程序:
我们可以发现这里已经成功的加载了html,然后我们再次点击按钮可以发现通过回调成功的调用了函数,并且显示了弹窗
其余两种方法同理:
WebView 漏洞在Android APP中占比十分巨大,根据梆梆安全的《2021年移动安全形势分析与2022年研判》中显示,WebView漏洞总和仍然占据目前Android APP漏洞类别前列,如下图所示:
因此了解和掌握WebView漏洞的原理和技巧是十分重要的
谈起WebView漏洞,我们的目光回退到2020年看雪SDC沙龙会议中,来自OPPO实验室的何恩大佬发表的《Android WebView安全攻防指南2020》的演讲,大佬从WebView的本地攻击面、远程攻击面、特殊攻击面
共3个角度讲述了WebView漏洞的成因,原文链接:Android WebView安全攻防指南2020,这里引用大佬的WebView示例图,如下图所示:
从图中我们可以看出WebView的漏洞攻击面,从引导组件Intent、deeplink,包括了WebView自身的接口,以及一些端口协议等等攻击面
考虑到WebView漏洞的攻击面十分的杂乱,这里我对WebView漏洞的发展进行了一个梳理总结,如下所示:
下面我们就将一一的讲解每个漏洞的原理并进行复现
漏洞原理:
我们在上文中讲述过JS和Android直接的通信可以通过addJavascriptInterface
接口进行对象映射
当JS拿到Android这个对象后,就可以调用这个Android对象中所有的方法,包括系统类(java.lang.Runtime 类),从而进行任意代码执行。
具体的攻击步骤:
示例代码:
漏洞防护:
Google在Android4.2以后对调用的函数以@JavascriptInterface
进行注解从而避免漏洞攻击,也就是说我们js调用Android的方法,必须要在JavascriptInterface中进行声明,这样才能调用,如我们上文中实现的便是如此
后来这种漏洞越来越少,在当前高版本的手机上基本没有了
在4.2之前,可以采用拦截prompt()的方式进行漏洞修复,不过这种版本太过久远,所以我们这里就不细讲了
漏洞原理:
在Android 3.0以下,Android系统会默认通过searchBoxJavaBridge_
的Js接口给 WebView 添加一个JS映射对象:searchBoxJavaBridge_
对象,该接口可能被利用,实现远程任意代码。
安全防护:
删除searchBoxJavaBridge_
接口
而accessibility
和 accessibilityTraversal
接口引起远程代码执行漏洞的原理和这里相似,由于这些漏洞在当前的Android版本上基本不存在,我们不做深入讲解了。
漏洞原理:
WebView默认开启密码保存功能 :
开启后,在用户输入密码时,会弹出提示框:询问用户是否保存密码;
如果选择”是”,密码会被明文保到 /data/data/com.package.name/databases/webview.db
中,这样就有被盗取密码的危险
安全防护:
通过 WebSettings.setSavePassword(false) 关闭密码保存提醒功能,防止明文密码存在本地被盗用。
2018 年国家信息安全漏洞共享平台(CNVD)发布关于Android平台 WebView 控件存在跨域访问高危漏洞的安全公告 (CNVD-2017-36682),漏洞产生的原因在Android系统中,WebView 开启了 file 域访问,且允许 file 域对 http 域进行访问,同时未对 file 域的路径进行严格限制所致。攻击者通过 URL Scheme 的方式,可远程打开并加载恶意 HTML 文件,远程获取 APP 中包括用户登录凭证在内的所有本地敏感数据。
针对WebView的跨域问题,主要是三个重要的API,如下所示:
漏洞原理:
setAllowFileAccess(true) + setAllowFileAccessFromFileURLs(true)
这样使得WebView可以使用File协议,和加载Js代码读取本地文件,或访问http源,这样会导致攻击者操作用户点击后无感知下载恶意的HTML/JS,并窃取相关的私有文件信息
漏洞复现:
首先我们可以查询Android手机中的/etc/hosts
私有文件信息
我们怎么利用WebView的跨域漏洞去访问本地的私有目录
首先我们编写目标JS文件:
fileAttack.html
然后我们将文件上传到手机的目录
接着我们编写攻击脚本
这是程序肯定会报错误,因为Android4.1后就默认禁止setAllowFileAccessFromFileURLs(true)
然后我们开启setAllowFileAccessFromFileURLs
按钮,再次执行
我们可以发现现在的Android版本中开始删除了这样的API,这是为了安全性,不过这里我们还是可以使用,于是我们开启API
这里就获取了相应的私有文件信息,而且加载了我们的恶意html文件
漏洞原理:
setAllowFileAccess(true) + setAllowUniversalAccessFromFileURLs(true)
用同样的方式测试 setAllowUniversalAccessFromFileURLs 的值,当 setAllowUniversalAccessFromFileURLs 的值为 true 时,可以利用 js 来访问恶意网站(HTTP 或 HTTPS)的链接
我们将上面html中的访问改为看雪的网站:
然后开启setAllowUniversalAccessFromFileURLs 函数API,并运行
我们可以发现这里我们注入html后还可以去访问网站,这样我们可以在脚本中使其访问恶意的网站界面,并返回这样就可以进行恶意界面的注入
安全防护:
若需要开启 file 域访问,则设置 file 路径的白名单,严格控制 file 域的访问范围,具体如下:
我们回顾2020SDC中研究提到的这类漏洞,只有setAllowFileAccess为True
漏洞原理:
其攻击过程首先是操纵WebView去访问一个攻击APP自己公开出来的网页,然后这个网页执行的内容其实就是延时去读取自身。在延时读取自身的时间窗口内,这个文件悄悄被进行了替换,替换成了软链接,指向受害APP的一个私有文件,最终读取窃取其内容。
具体攻击步骤:
漏洞复现:
这里由于该漏洞在Android7.0版本上已经修复了,所以这里我引用大佬的博客来进行描述,大家具体可以按照步骤在Android7.0以下的版本上去复现,参考文章:Android安全检测-WebView File域同源策略绕过漏洞
构造HTML文件:
构造恶意APP的攻击代码
目标样本:
实现效果:
安全防护:
本漏洞参考:Android-Webview中的漏洞利用总结
漏洞原理:
攻击者创造符号链接,然后绕过校验,访问攻击的html,再通过软链接去加载symlink.html,然后窃取Cooike,如下图所示:
漏洞复现:
首先创建了符号链接,然后过URL校验,访问我们的服务器http://ip地址/easydroid.html
:
建立一个easydroid.html,它里面有两个重定向:一个是设置Cookie,一个是加载与Cookies文件符号链接后的那个html文件
easydroid.html
攻击代码:
通过Intent重定向,首先加载exp.html来设置cookie,然后再加载symlink.html,将所要Cookies内容返回给我们的服务器。最终达到窃取Cookies的目的。注意,这里要保证setAllowFileAccess(true),API 29以下默认为true,否则会利用失败
效果显示:
在WebView漏洞中,许多URL可以通过各种方式去绕过验证,从而引起各式各样的漏洞
URL的结构如下:
漏洞原理:
首先我们来看一个简单的url校验例子:
这里是对url进行域名校验,然后开启我们的enableJavaScriptInterface
接口可以访问js文件
我们在实际中会发现这样的例子:
很明显这里是开发者为了防止加载file的同源策略进行的防护,但是我们有很多的绕过方法:
总结:
我们还可以发现在一般的url中会对首尾进行校验
存在问题:endWith未闭合点号
使⽤startsWith、contains、indexOf、正则匹配等⾮严格字符串匹配
contains+indexOf绕过:
//和第一个/之间提取host
绕过方式:
漏洞复现:
我们打开一个样本APP,分析其代码块:
这里我们可以简单的发现代码对URL的相应部分进行了校验,这样我们就可以进行构造
我们构造了两条URL,然后可以绕过检验,然后我们直接启动
效果如下:
这里我们可以实现任意的URI的访问和跳转
假设我们加载的Uri不是通过Uri.parse,而是通过外部直接获取,我们可以构造hearachical 来绕过
我们可以使用 HierarchicalUri 和 Java Reflection API 进行攻击,同样我们分析一个样本的URL验证
android.net.Uri
在Android上被广泛使用,但实际上它是一个抽象类。android.net.Uri$HierarchicalUri
是它的子类之一。Java 反射 API 使创建能够绕过此检查的 Uri 成为可能。
通过反射传⼊⼀个scheme、authoritiy和path,构造⼀个形式为http://legitimate.com@attacker.com的HierachicalUri实例即可绕过
文件TestActivity.java
:
然后可以获得日志信息:
漏洞防护:
我们只需要让攻击的应用程序获取Uri
攻击者控制的对象并专门使用该对象
从 API 级别 28 (Android 9) 开始,禁止使用内部接口——但这可以通过使用RestrictionBypass等工具轻松绕过
漏洞原理:
代码在进行URL校验是,检查了host,但是并未检查scheme,这样我们可以通过“javascript”进行绕过
例如:
也可以通过file://www.mysite.com/sdcard/evil.html
绕过,某些版本WebView可正常解析为file:///sdcard/evil.html
漏洞复现:
我们打开一个样本:
然后进行构造html
最后就绕过了校验,实现了XSS注入
安全防护:
URL校验函数
Intent Scheme校验建议写法:
漏洞原理:
⽩名单域名内的服务端出现跳转漏洞时,仍然可以通过检查,并调⽤javascriptInterface
,例如我们构造一个这样的URL:
前面的https://www.site1.com/redirect.php
是我们构造的虚拟的站点,当主机访问时,打开这个URL后,服务器会返回一个302响应,然后浏览器侧会斩词请求Location中指定的URL,对于具有单点登录功能的网站,这种类型的接口很常见。
然后我们就可以构造URL来绕过域名白名单:
漏洞复现:
首先我们编写攻击的htm:
然后我们使用文件服务器来进行模拟
我们接着编写测试样本代码
这里我们可以分析代码中有一个白名单的域名检测函数checkDomain
,但是我们可以通过构造URL来绕过:
然后我们启动应用:
我们点击白名单绕过按钮:
我们可以发现这里就成功的绕过了白名单进行跳转
安全防护:
此时可以在shouldOverrideUrlLoading
函数中拦截跳转,并对跳转的Url进行检查
而很多使用shouldOverrideUrlLoading
,也是不安全的使用状态,这样还是会存在一些攻击,具体参考:Android:访问受应用程序保护的组件
APP经常会使用file://协议加载本地文件,通常会限制在一些特定路径中,参考文章:Android WebView URL检查绕过
漏洞原理:
主要通过 WebView 对外暴露的接口和Intent访问导出组件可以导致的攻击手段
漏洞复现:
我们编写一个样例代码
这里我们可以看出我们获取了密码WindXaa12345678
,一般应用程序会对这里进行加密或者混淆,我们这里简单演示就不进行加密了
我们将该组件设置为导出组件
我们对实例样本分析,这里就可以利用接口导出的漏洞进行攻击
漏洞复现:
我们再编写攻击样本:
然后我们在本地编写恶意的html,利用前面讲述的远程加载
然后启动http_server
此时我们启动攻击样本,并点击按钮
这里我们可以成功的加载我们的页面说明恶意html注入成功
然后我们再次点击js中的按钮,就可以获取敏感数据
这里就获取的敏感的数据
漏洞原理:
我们都知道Android中的组件需要导出才能够访问,而导出的组件往往存在一定的安全问题。
导出的组件一般有以下三种形式:
未导出的组件:
而通过一定的方法可以访问未导出的组件,我们将这种漏洞成为launchAnyWhere
漏洞
Intent可以通过重定向的原理,通过携带数据信息,访问一个可导出的组件,然后再进行数据传递去触发不可导出的组件,最后实现访问私有组件的目的,引起launchAnyWhere
漏洞
漏洞复现:
我们查看一个样本案例:
首先是不可导出组件PrivateActivity:
然后可导出的组件:WebView2Activity
代码层的校验代码:
WebView2Activity
PrivateActivity
然后我们编写攻击代码:
效果如下:
可以看出我们通过WebView+Intent重定向就可以访问私有组件,从而实现launchAnyWhere漏洞
deeplink 是一种在网页中启动App的超链接。当用户点击deeplink链接时,Android系统会启动注册该deeplink的应用,打开在Manifest文件中注册该deeplink的activity。
deeplink在APP中会导致多类漏洞:通过deeplink操纵WebView造成的远程代码执行、敏感信息泄露、应用克隆、launchAnyWhere等漏洞。
漏洞原理:
样本代码层通过反射调用去安装的列表中搜索安装的应用,然后去调用其方法进行实现,我们可以构造相同的包名,然后将攻击样本去安装到手机上,是的应用触发任意代码执行漏洞
漏洞复现:
样本代码段:
我们构造Poc:
注意这里我们要保证构造的攻击应用的包名和代码中校验的一致才能触发,然后我们启动,可以发现成功的触发了
漏洞防护:
需要对具体的包名进行校验,并对DeepLink进行过滤
漏洞原理:
这也是我们结合Deeplink+WebView导致的一个漏洞问题,我们可以通过构造含深度调用链的JS,然后通过加载去实现XSS注入
漏洞复现:
首先我们查看样本的DeepLinks
然后我们可以发现样本的代码:
我们构造攻击脚本:
然后我们通过去访问该html:
接着我们使用目标样本打开:
即可以成功的进行XSS注入
我们前面分析了很多DeepLinks在WebView上的漏洞,我们还可以将这些漏洞组合使用,我在测试一款银行的样本中发现组合漏洞:
这个结合了前面我们讲的网络漏洞、路径穿越、XSS、WebView漏洞,具体的大家可以去听我在平安SRC&&看雪的沙龙会议中的演讲。
以及我在网上收集的一个大佬的文章,描述的漏洞利用流程,大家可以参考:
参考文章:APP漏洞利用组合拳——应用克隆案例分析
当loadDataWithBaseURL
的域名和内容同时可控是,可以构造任意域下的XSS
除了明显的情况外,攻击者控制调用中的baseUri
和参数data
漏洞原理:
deeplink加载任意fragment,转化为WebView loadDataWithBaseURL漏洞
漏洞复现:
实现步骤:
攻击代码:
可以通过这个deeplink打开任意fragment的漏洞,实现可控任意域执行任意JS,实现盗取登陆态的用户cookie!
具体参考:Android中的特殊攻击面(二)——危险的deeplink
本文我编写了很久,主要是一些案例和网上的漏洞复现需要大量的时间去做,很多案例和攻击poc,我都进行了手动编写,一些网上已有的大佬的文章样例,我也进行了一一的复现,将一些不能复现的全部剔除,WebView漏洞是Android APP上当前十分重要的漏洞,漏洞的种类十分多,本文归纳了20多种类别的漏洞并进行了一一的复现,具体漏洞扩展,大家可以参考后面的文献,而实验的poc和攻击样本我也会上传知识星球和github。
github网址:WindXaa
会议:
WebView的原理:
远程代码执行漏洞:
WebView跨域漏洞:
URL配置漏洞:
Intent Scheme绕过:
Intent:
deeplink:
WebView控件功能强大,除了具有一般View的属性和设置外,还可以对url请求、页面加载、渲染、页面交互进行强大的处理。
WebView控件功能强大,除了具有一般View的属性和设置外,还可以对url请求、页面加载、渲染、页面交互进行强大的处理。
1.
在布局文件中添加WebView控件;
2.
在代码中让WebView控件加载显示网页。
1.
在布局文件中添加WebView控件;
2.
在代码中让WebView控件加载显示网页。
<WebView
android:
id
=
"@+id/Wind_webview"
android:layout_width
=
"match_parent"
android:layout_height
=
"match_parent"
/
>
<WebView
android:
id
=
"@+id/Wind_webview"
android:layout_width
=
"match_parent"
android:layout_height
=
"match_parent"
/
>
/
/
获得控件
WebView webView
=
(WebView) findViewById(R.
id
.Wind_webview);
/
/
访问网页
webView.loadUrl(
"http://www.baidu.com"
);
/
/
系统默认会通过手机浏览器打开网页,为了能够直接通过WebView显示网页,则必须设置
webView.setWebViewClient(new WebViewClient(){
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
/
/
使用WebView加载显示url
view.loadUrl(url);
/
/
返回true
return
true;
}
});
/
/
获得控件
WebView webView
=
(WebView) findViewById(R.
id
.Wind_webview);
/
/
访问网页
webView.loadUrl(
"http://www.baidu.com"
);
/
/
系统默认会通过手机浏览器打开网页,为了能够直接通过WebView显示网页,则必须设置
webView.setWebViewClient(new WebViewClient(){
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
/
/
使用WebView加载显示url
view.loadUrl(url);
/
/
返回true
return
true;
}
});
<!
-
-
添加网络权限
-
-
>
<uses
-
permission android:name
=
"android.permission.INTERNET"
/
>
<!
-
-
添加网络权限
-
-
>
<uses
-
permission android:name
=
"android.permission.INTERNET"
/
>
<!DOCTYPE html>
<html>
<head>
<meta charset
=
"utf-8"
>
<title>Carson<
/
title>
<script>
function callAndroid(){
/
/
由于对象映射,所以调用test对象等于调用Android映射的对象
test.hello(
"WindXaa!"
);
}
<
/
script>
<
/
head>
<body>
<!
-
-
点击按钮则调用callAndroid函数
-
-
>
<button
type
=
"button"
id
=
"button1"
onclick
=
"callAndroid()"
>Internet Click connect<
/
button>
<
/
body>
<
/
html>
<!DOCTYPE html>
<html>
<head>
<meta charset
=
"utf-8"
>
<title>Carson<
/
title>
<script>
function callAndroid(){
/
/
由于对象映射,所以调用test对象等于调用Android映射的对象
test.hello(
"WindXaa!"
);
}
<
/
script>
<
/
head>
<body>
<!
-
-
点击按钮则调用callAndroid函数
-
-
>
<button
type
=
"button"
id
=
"button1"
onclick
=
"callAndroid()"
>Internet Click connect<
/
button>
<
/
body>
<
/
html>
{
WebView mWebView
=
(WebView) findViewById(R.
id
.Wind_webview1);
WebSettings webSettings
=
mWebView.getSettings();
/
/
设置与Js交互的权限
webSettings.setJavaScriptEnabled(true);
/
/
通过addJavascriptInterface()将Java对象映射到JS对象
/
/
参数
1
:Javascript对象名
/
/
参数
2
:Java对象名
mWebView.addJavascriptInterface(new AndroidtoJs(),
"test"
);
/
/
AndroidtoJS类对象映射到js的test对象
mWebView.loadData("
","
text
/
html",null);
/
/
加载JS代码
/
/
格式规定为:
file
:
/
/
/
android_asset
/
文件名.html
/
/
mWebView.loadUrl(
"file:///android_asset/javascript.html"
);
mWebView.loadUrl(
"http://ip地址填自己的/attack.html"
);
}
/
*
*
*
提供接口在Webview中供JS调用
*
/
public
class
AndroidtoJs {
/
/
定义JS需要调用的方法,被JS调用的方法必须加入@JavascriptInterface注解
@JavascriptInterface
public void hello(String msg) {
Log.e(
"WindXaa"
,
"Hello,"
+
msg);
}
}
{
WebView mWebView
=
(WebView) findViewById(R.
id
.Wind_webview1);
WebSettings webSettings
=
mWebView.getSettings();
/
/
设置与Js交互的权限
webSettings.setJavaScriptEnabled(true);
/
/
通过addJavascriptInterface()将Java对象映射到JS对象
/
/
参数
1
:Javascript对象名
/
/
参数
2
:Java对象名
mWebView.addJavascriptInterface(new AndroidtoJs(),
"test"
);
/
/
AndroidtoJS类对象映射到js的test对象
mWebView.loadData("
","
text
/
html",null);
/
/
加载JS代码
/
/
格式规定为:
file
:
/
/
/
android_asset
/
文件名.html
/
/
mWebView.loadUrl(
"file:///android_asset/javascript.html"
);
mWebView.loadUrl(
"http://ip地址填自己的/attack.html"
);
}
/
*
*
*
提供接口在Webview中供JS调用
*
/
public
class
AndroidtoJs {
/
/
定义JS需要调用的方法,被JS调用的方法必须加入@JavascriptInterface注解
@JavascriptInterface
public void hello(String msg) {
Log.e(
"WindXaa"
,
"Hello,"
+
msg);
}
}
/
/
激活WebView为活跃状态,能正常执行网页的响应
/
/
激活WebView为活跃状态,能正常执行网页的响应
/
/
当页面被失去焦点被切换到后台不可见状态,需要执行onPause
/
/
通过onPause动作通知内核暂停所有的动作,比如DOM的解析、plugin的执行、JavaScript执行。
/
/
当页面被失去焦点被切换到后台不可见状态,需要执行onPause
/
/
通过onPause动作通知内核暂停所有的动作,比如DOM的解析、plugin的执行、JavaScript执行。
/
/
当应用程序(存在webview)被切换到后台时,这个方法不仅仅针对当前的webview而是全局的全应用程序的webview
/
/
它会暂停所有webview的layout,parsing,javascripttimer。降低CPU功耗。
/
/
当应用程序(存在webview)被切换到后台时,这个方法不仅仅针对当前的webview而是全局的全应用程序的webview
/
/
它会暂停所有webview的layout,parsing,javascripttimer。降低CPU功耗。
/
/
恢复pauseTimers状态
/
/
webview调用destory时,webview仍绑定在Activity上
/
/
需要先从父容器中移除webview,然后再销毁webview
/
/
webview调用destory时,webview仍绑定在Activity上
/
/
需要先从父容器中移除webview,然后再销毁webview
/
/
是否可以后退
Webview.canGoBack()
/
/
后退网页
Webview.goBack()
/
/
是否可以前进
Webview.canGoForward()
/
/
前进网页
Webview.goForward()
/
/
以当前的index为起始点前进或者后退到历史记录中指定的steps
/
/
如果steps为负数则为后退,正数则为前进
Webview.goBackOrForward(intsteps)
/
/
是否可以后退
Webview.canGoBack()
/
/
后退网页
Webview.goBack()
/
/
是否可以前进
Webview.canGoForward()
/
/
前进网页
Webview.goForward()
/
/
以当前的index为起始点前进或者后退到历史记录中指定的steps
/
/
如果steps为负数则为后退,正数则为前进
Webview.goBackOrForward(intsteps)
public boolean onKeyDown(
int
keyCode, KeyEvent event) {
if
((keyCode
=
=
KEYCODE_BACK) && mWebView.canGoBack()) {
mWebView.goBack();
return
true;
}
return
super
.onKeyDown(keyCode, event);
}
public boolean onKeyDown(
int
keyCode, KeyEvent event) {
if
((keyCode
=
=
KEYCODE_BACK) && mWebView.canGoBack()) {
mWebView.goBack();
return
true;
}
return
super
.onKeyDown(keyCode, event);
}
/
/
清除网页访问留下的缓存
/
/
由于内核缓存是全局的因此这个方法不仅仅针对webview而是针对整个应用程序.
Webview.clearCache(true);
/
/
清除当前webview访问的历史记录
/
/
只会webview访问历史记录里的所有记录除了当前访问记录
Webview.clearHistory();
/
/
这个api仅仅清除自动完成填充的表单数据,并不会清除WebView存储到本地的数据
Webview.clearFormData();
/
/
清除网页访问留下的缓存
/
/
由于内核缓存是全局的因此这个方法不仅仅针对webview而是针对整个应用程序.
Webview.clearCache(true);
/
/
清除当前webview访问的历史记录
/
/
只会webview访问历史记录里的所有记录除了当前访问记录
Webview.clearHistory();
/
/
这个api仅仅清除自动完成填充的表单数据,并不会清除WebView存储到本地的数据
Webview.clearFormData();
<uses
-
permission android:name
=
"android.permission.INTERNET"
/
>
<uses
-
permission android:name
=
"android.permission.INTERNET"
/
>
/
/
方式
1
:直接在在Activity中生成
WebView webView
=
new WebView(this)
/
/
方法
2
:在Activity的layout文件里添加webview控件:
WebView webview
=
(WebView) findViewById(R.
id
.webView1);
/
/
方式
1
:直接在在Activity中生成
WebView webView
=
new WebView(this)
/
/
方法
2
:在Activity的layout文件里添加webview控件:
WebView webview
=
(WebView) findViewById(R.
id
.webView1);
/
/
声明WebSettings子类
WebSettings webSettings
=
webView.getSettings();
/
/
如果访问的页面中要与Javascript交互,则webview必须设置支持Javascript
webSettings.setJavaScriptEnabled(true);
/
/
支持插件
webSettings.setPluginsEnabled(true);
/
/
设置自适应屏幕,两者合用
webSettings.setUseWideViewPort(true);
/
/
将图片调整到适合webview的大小
webSettings.setLoadWithOverviewMode(true);
/
/
缩放至屏幕的大小
/
/
缩放操作
webSettings.setSupportZoom(true);
/
/
支持缩放,默认为true。是下面那个的前提。
webSettings.setBuiltInZoomControls(true);
/
/
设置内置的缩放控件。若为false,则该WebView不可缩放
webSettings.setDisplayZoomControls(false);
/
/
隐藏原生的缩放控件
/
/
其他细节操作
webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
/
/
关闭webview中缓存
webSettings.setAllowFileAccess(true);
/
/
设置可以访问文件
webSettings.setJavaScriptCanOpenWindowsAutomatically(true);
/
/
支持通过JS打开新窗口
webSettings.setLoadsImagesAutomatically(true);
/
/
支持自动加载图片
webSettings.setDefaultTextEncodingName(
"utf-8"
);
/
/
设置编码格式
/
/
声明WebSettings子类
WebSettings webSettings
=
webView.getSettings();
/
/
如果访问的页面中要与Javascript交互,则webview必须设置支持Javascript
webSettings.setJavaScriptEnabled(true);
/
/
支持插件
webSettings.setPluginsEnabled(true);
/
/
设置自适应屏幕,两者合用
webSettings.setUseWideViewPort(true);
/
/
将图片调整到适合webview的大小
webSettings.setLoadWithOverviewMode(true);
/
/
缩放至屏幕的大小
/
/
缩放操作
webSettings.setSupportZoom(true);
/
/
支持缩放,默认为true。是下面那个的前提。
webSettings.setBuiltInZoomControls(true);
/
/
设置内置的缩放控件。若为false,则该WebView不可缩放
webSettings.setDisplayZoomControls(false);
/
/
隐藏原生的缩放控件
/
/
其他细节操作
webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
/
/
关闭webview中缓存
webSettings.setAllowFileAccess(true);
/
/
设置可以访问文件
webSettings.setJavaScriptCanOpenWindowsAutomatically(true);
/
/
支持通过JS打开新窗口
webSettings.setLoadsImagesAutomatically(true);
/
/
支持自动加载图片
webSettings.setDefaultTextEncodingName(
"utf-8"
);
/
/
设置编码格式
/
/
优先使用缓存
WebView.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
/
/
缓存模式如下:
/
/
LOAD_CACHE_ONLY: 不使用网络,只读取本地缓存数据
/
/
LOAD_DEFAULT: (默认)根据cache
-
control决定是否从网络上取数据。
/
/
LOAD_NO_CACHE: 不使用缓存,只从网络获取数据.
/
/
LOAD_CACHE_ELSE_NETWORK,只要本地有,无论是否过期,或者no
-
cache,都使用缓存中的数据
/
/
不使用缓存
WebView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
/
/
优先使用缓存
WebView.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
/
/
缓存模式如下:
/
/
LOAD_CACHE_ONLY: 不使用网络,只读取本地缓存数据
/
/
LOAD_DEFAULT: (默认)根据cache
-
control决定是否从网络上取数据。
/
/
LOAD_NO_CACHE: 不使用缓存,只从网络获取数据.
/
/
LOAD_CACHE_ELSE_NETWORK,只要本地有,无论是否过期,或者no
-
cache,都使用缓存中的数据
/
/
不使用缓存
WebView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
/
/
Webview控件
Webview webview
=
(WebView) findViewById(R.
id
.webView);
/
/
加载一个网页
webView.loadUrl(
"http://www.google.com/"
);
/
/
重写shouldOverrideUrlLoading()方法,使得打开网页时不调用系统浏览器, 而是在本WebView中显示
webView.setWebViewClient(new WebViewClient(){
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return
true;
}
});
/
/
Webview控件
Webview webview
=
(WebView) findViewById(R.
id
.webView);
/
/
加载一个网页
webView.loadUrl(
"http://www.google.com/"
);
/
/
重写shouldOverrideUrlLoading()方法,使得打开网页时不调用系统浏览器, 而是在本WebView中显示
webView.setWebViewClient(new WebViewClient(){
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return
true;
}
});
webView.setWebViewClient(new WebViewClient(){
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
/
/
设定加载开始的操作
}
});
webView.setWebViewClient(new WebViewClient(){
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
/
/
设定加载开始的操作
}
});
webView.setWebViewClient(new WebViewClient(){
@Override
public boolean onLoadResource(WebView view, String url) {
/
/
设定加载资源的操作
}
});
webView.setWebViewClient(new WebViewClient(){
@Override
public boolean onLoadResource(WebView view, String url) {
/
/
设定加载资源的操作
}
});
/
/
步骤
1
:写一个html文件(error_handle.html),用于出错时展示给用户看的提示页面
/
/
步骤
2
:将该html文件放置到代码根目录的assets文件夹下
/
/
步骤
3
:复写WebViewClient的onRecievedError方法
/
/
该方法传回了错误码,根据错误类型可以进行不同的错误分类处理
webView.setWebViewClient(new WebViewClient(){
@Override
public void onReceivedError(WebView view,
int
errorCode, String description, String failingUrl){
switch(errorCode)
{
case HttpStatus.SC_NOT_FOUND:
view.loadUrl(
"file:///android_assets/error_handle.html"
);
break
;
}
}
});
/
/
步骤
1
:写一个html文件(error_handle.html),用于出错时展示给用户看的提示页面
/
/
步骤
2
:将该html文件放置到代码根目录的assets文件夹下
/
/
步骤
3
:复写WebViewClient的onRecievedError方法
/
/
该方法传回了错误码,根据错误类型可以进行不同的错误分类处理
webView.setWebViewClient(new WebViewClient(){
@Override
public void onReceivedError(WebView view,
int
errorCode, String description, String failingUrl){
switch(errorCode)
{
case HttpStatus.SC_NOT_FOUND:
view.loadUrl(
"file:///android_assets/error_handle.html"
);
break
;
}
}
});
webView.setWebViewClient(new WebViewClient() {
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
handler.proceed();
/
/
表示等待证书响应
/
/
handler.cancel();
/
/
表示挂起连接,为默认方式
/
/
handler.handleMessage(null);
/
/
可做其他处理
}
});
webView.setWebViewClient(new WebViewClient() {
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
handler.proceed();
/
/
表示等待证书响应
/
/
handler.cancel();
/
/
表示挂起连接,为默认方式
/
/
handler.handleMessage(null);
/
/
可做其他处理
}
});
webview.setWebChromeClient(new WebChromeClient(){
@Override
public void onProgressChanged(WebView view,
int
newProgress) {
if
(newProgress <
100
) {
String progress
=
newProgress
+
"%"
;
progress.setText(progress);
}
else
{
}
});
webview.setWebChromeClient(new WebChromeClient(){
@Override
public void onProgressChanged(WebView view,
int
newProgress) {
if
(newProgress <
100
) {
String progress
=
newProgress
+
"%"
;
progress.setText(progress);
}
else
{
}
});
webview.setWebChromeClient(new WebChromeClient(){
@Override
public void onReceivedTitle(WebView view, String title) {
titleview.setText(title);
}
webview.setWebChromeClient(new WebChromeClient(){
@Override
public void onReceivedTitle(WebView view, String title) {
titleview.setText(title);
}
<html>
<head>
<meta charset
=
"utf-8"
>
<title>测试<
/
title>
<script>
function callJS(){
alert(
"Android调用了JS的callJS方法"
);
}
<
/
script>
<
/
head>
<body>
<h1>Android调用JS方法测试<
/
h1>
<
/
body>
<
/
html>
<html>
<head>
<meta charset
=
"utf-8"
>
<title>测试<
/
title>
<script>
function callJS(){
alert(
"Android调用了JS的callJS方法"
);
}
<
/
script>
<
/
head>
<body>
<h1>Android调用JS方法测试<
/
h1>
<
/
body>
<
/
html>
/
/
设置与Js交互的权限
webSettings.setJavaScriptEnabled(true);
/
/
设置允许JS弹窗
webSettings.setJavaScriptCanOpenWindowsAutomatically(true);
/
/
先载入JS代码
/
/
格式规定为:
file
:
/
/
/
android_asset
/
文件名.html
mWebView.loadUrl(
"file:///android_asset/AndroJs.html"
);
Button button
=
(Button) findViewById(R.
id
.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
/
/
通过Handler发送消息
mWebView.post(new Runnable() {
@Override
public void run() {
/
/
注意调用的JS方法名要对应上
/
/
调用javascript的callJS()方法
mWebView.loadUrl(
"javascript:callJS()"
);
}
});
}
});
/
/
由于设置了弹窗检验调用结果,所以需要支持js对话框
/
/
webview只是载体,内容的渲染需要使用webviewChromClient类去实现
/
/
通过设置WebChromeClient对象处理JavaScript的对话框
/
/
设置响应js 的Alert()函数
mWebView.setWebChromeClient(new WebChromeClient() {
@Override
public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
AlertDialog.Builder b
=
new AlertDialog.Builder(MainActivity.this);
b.setTitle(
"Alert"
);
b.setMessage(message);
b.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog,
int
which) {
result.confirm();
}
});
b.setCancelable(false);
b.create().show();
return
true;
}
});
/
/
设置与Js交互的权限
webSettings.setJavaScriptEnabled(true);
/
/
设置允许JS弹窗
webSettings.setJavaScriptCanOpenWindowsAutomatically(true);
/
/
先载入JS代码
/
/
格式规定为:
file
:
/
/
/
android_asset
/
文件名.html
mWebView.loadUrl(
"file:///android_asset/AndroJs.html"
);
Button button
=
(Button) findViewById(R.
id
.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
/
/
通过Handler发送消息
mWebView.post(new Runnable() {
@Override
public void run() {
/
/
注意调用的JS方法名要对应上
/
/
调用javascript的callJS()方法
mWebView.loadUrl(
"javascript:callJS()"
);
}
});
}
});
/
/
由于设置了弹窗检验调用结果,所以需要支持js对话框
/
/
webview只是载体,内容的渲染需要使用webviewChromClient类去实现
/
/
通过设置WebChromeClient对象处理JavaScript的对话框
/
/
设置响应js 的Alert()函数
mWebView.setWebChromeClient(new WebChromeClient() {
@Override
public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
AlertDialog.Builder b
=
new AlertDialog.Builder(MainActivity.this);
b.setTitle(
"Alert"
);
b.setMessage(message);
b.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog,
int
which) {
result.confirm();
}
});
b.setCancelable(false);
b.create().show();
return
true;
}
});
onPageFinished()属于WebViewClient类的方法,主要在页面加载结束时调用
onPageFinished()属于WebViewClient类的方法,主要在页面加载结束时调用
因为该方法的执行不会使页面刷新,而第一种方法(loadUrl )的执行则会。
Android
4.4
后才可使用
因为该方法的执行不会使页面刷新,而第一种方法(loadUrl )的执行则会。
Android
4.4
后才可使用
/
/
使用evaluateJavascript来加载
mWebView.evaluateJavascript(
"javascript:callJS()"
, new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
/
/
此处为 js 返回的结果
}
});
/
/
使用evaluateJavascript来加载
mWebView.evaluateJavascript(
"javascript:callJS()"
, new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
/
/
此处为 js 返回的结果
}
});
/
/
JS调用Android
<html>
<head>
<meta charset
=
"utf-8"
>
<title>Carson<
/
title>
<script>
function callAndroid(){
/
/
由于对象映射,所以调用test对象等于调用Android映射的对象
test.hello(
"WindXaa js调用了android中的hello方法"
);
}
<
/
script>
<
/
head>
<body>
<!
-
-
点击按钮则调用callAndroid函数
-
-
>
<button
type
=
"button"
id
=
"button1"
onclick
=
"callAndroid()"
>Click Attack<
/
button>
<
/
body>
<
/
html>
/
/
JS调用Android
<html>
<head>
<meta charset
=
"utf-8"
>
<title>Carson<
/
title>
<script>
function callAndroid(){
/
/
由于对象映射,所以调用test对象等于调用Android映射的对象
test.hello(
"WindXaa js调用了android中的hello方法"
);
}
<
/
script>
<
/
head>
<body>
<!
-
-
点击按钮则调用callAndroid函数
-
-
>
<button
type
=
"button"
id
=
"button1"
onclick
=
"callAndroid()"
>Click Attack<
/
button>
<
/
body>
<
/
html>
public
class
JsToAndroActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
setContentView(R.layout.activity_js_to_andro);
WebView mWebView
=
(WebView) findViewById(R.
id
.Wind_webview1);
WebSettings webSettings
=
mWebView.getSettings();
/
/
设置与Js交互的权限
webSettings.setJavaScriptEnabled(true);
/
/
通过addJavascriptInterface()将Java对象映射到JS对象
/
/
参数
1
:Javascript对象名
/
/
参数
2
:Java对象名
mWebView.addJavascriptInterface(new AndroidtoJs(),
"test"
);
/
/
AndroidtoJS类对象映射到js的test对象
/
/
加载JS代码
/
/
格式规定为:
file
:
/
/
/
android_asset
/
文件名.html
mWebView.loadUrl(
"file:///android_asset/javascript.html"
);
}
/
*
*
*
提供接口在Webview中供JS调用
*
/
public
class
AndroidtoJs {
/
/
定义JS需要调用的方法,被JS调用的方法必须加入@JavascriptInterface注解
@JavascriptInterface
public void hello(String msg) {
Log.e(
"WindXaa"
,
"Hello,"
+
msg);
}
}
}
public
class
JsToAndroActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
setContentView(R.layout.activity_js_to_andro);
WebView mWebView
=
(WebView) findViewById(R.
id
.Wind_webview1);
WebSettings webSettings
=
mWebView.getSettings();
/
/
设置与Js交互的权限
webSettings.setJavaScriptEnabled(true);
/
/
通过addJavascriptInterface()将Java对象映射到JS对象
/
/
参数
1
:Javascript对象名
/
/
参数
2
:Java对象名
mWebView.addJavascriptInterface(new AndroidtoJs(),
"test"
);
/
/
AndroidtoJS类对象映射到js的test对象
/
/
加载JS代码
/
/
格式规定为:
file
:
/
/
/
android_asset
/
文件名.html
mWebView.loadUrl(
"file:///android_asset/javascript.html"
);
}
/
*
*
*
提供接口在Webview中供JS调用
*
/
public
class
AndroidtoJs {
/
/
定义JS需要调用的方法,被JS调用的方法必须加入@JavascriptInterface注解
@JavascriptInterface
public void hello(String msg) {
Log.e(
"WindXaa"
,
"Hello,"
+
msg);
}
}
}
(
1
)Android通过 WebViewClient 的回调方法shouldOverrideUrlLoading ()拦截 url
(
2
)解析该 url 的协议
(
3
)如果检测到是预先约定好的协议,就调用相应方法
(
1
)Android通过 WebViewClient 的回调方法shouldOverrideUrlLoading ()拦截 url
(
2
)解析该 url 的协议
(
3
)如果检测到是预先约定好的协议,就调用相应方法
<html>
<head>
<meta charset
=
"utf-8"
>
<title>Carson_Ho<
/
title>
<script>
function callAndroid(){
/
*
约定的url协议为:js:
/
/
webview?arg1
=
WindXaa&arg2
=
attack
*
/
document.location
=
"js://webview?arg1=WindXaa&arg2=attack"
;
}
<
/
script>
<
/
head>
<!
-
-
点击按钮则调用callAndroid()方法
-
-
>
<body>
<button
type
=
"button"
id
=
"button1"
onclick
=
"callAndroid()"
>点击调用Android代码<
/
button>
<
/
body>
<
/
html>
<html>
<head>
<meta charset
=
"utf-8"
>
<title>Carson_Ho<
/
title>
<script>
function callAndroid(){
/
*
约定的url协议为:js:
/
/
webview?arg1
=
WindXaa&arg2
=
attack
*
/
document.location
=
"js://webview?arg1=WindXaa&arg2=attack"
;
}
<
/
script>
<
/
head>
<!
-
-
点击按钮则调用callAndroid()方法
-
-
>
<body>
<button
type
=
"button"
id
=
"button1"
onclick
=
"callAndroid()"
>点击调用Android代码<
/
button>
<
/
body>
<
/
html>
WebView mWebView
=
(WebView) findViewById(R.
id
.Wind_webview2);
WebSettings webSettings
=
mWebView.getSettings();
/
/
设置与Js交互的权限
webSettings.setJavaScriptEnabled(true);
/
/
设置允许JS弹窗
webSettings.setJavaScriptCanOpenWindowsAutomatically(true);
/
/
步骤
1
:加载JS代码
/
/
格式规定为:
file
:
/
/
/
android_asset
/
文件名.html
mWebView.loadUrl(
"file:///android_asset/javascript1.html"
);
/
/
复写WebViewClient类的shouldOverrideUrlLoading方法
mWebView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
/
/
步骤
2
:根据协议的参数,判断是否是所需要的url
/
/
一般根据scheme(协议格式) & authority(协议名)判断(前两个参数)
/
/
约定的url协议为:js:
/
/
webview?arg1
=
WindXaa&arg2
=
attack(同时也是约定好的需要拦截的)
Uri uri
=
Uri.parse(url);
/
/
如果url的协议
=
预先约定的 js 协议
/
/
就解析往下解析参数
if
( uri.getScheme().equals(
"js"
)) {
/
/
如果 authority
=
预先约定协议里的 webview,即代表都符合约定的协议
/
/
所以拦截url,下面JS开始调用Android需要的方法
if
(uri.getAuthority().equals(
"webview"
)) {
/
/
步骤
3
:
/
/
执行JS所需要调用的逻辑
System.out.println(
"js调用了Android的方法"
);
/
/
可以在协议上带有参数并传递到Android上
HashMap<String, String> params
=
new HashMap<>();
Set
<String> collection
=
uri.getQueryParameterNames();
Log.e(
"WindXaa"
,params.get(
0
)
+
"---"
+
params.get(
1
));
}
return
true;
}
return
super
.shouldOverrideUrlLoading(view, url);
}
}
);
WebView mWebView
=
(WebView) findViewById(R.
id
.Wind_webview2);
WebSettings webSettings
=
mWebView.getSettings();
/
/
设置与Js交互的权限
webSettings.setJavaScriptEnabled(true);
/
/
设置允许JS弹窗
webSettings.setJavaScriptCanOpenWindowsAutomatically(true);
/
/
步骤
1
:加载JS代码
/
/
格式规定为:
file
:
/
/
/
android_asset
/
文件名.html
mWebView.loadUrl(
"file:///android_asset/javascript1.html"
);
/
/
复写WebViewClient类的shouldOverrideUrlLoading方法
mWebView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
/
/
步骤
2
:根据协议的参数,判断是否是所需要的url
/
/
一般根据scheme(协议格式) & authority(协议名)判断(前两个参数)
/
/
约定的url协议为:js:
/
/
webview?arg1
=
WindXaa&arg2
=
attack(同时也是约定好的需要拦截的)
Uri uri
=
Uri.parse(url);
/
/
如果url的协议
=
预先约定的 js 协议
/
/
就解析往下解析参数
if
( uri.getScheme().equals(
"js"
)) {
/
/
如果 authority
=
预先约定协议里的 webview,即代表都符合约定的协议
/
/
所以拦截url,下面JS开始调用Android需要的方法
if
(uri.getAuthority().equals(
"webview"
)) {
/
/
步骤
3
:
/
/
执行JS所需要调用的逻辑
System.out.println(
"js调用了Android的方法"
);
/
/
可以在协议上带有参数并传递到Android上
HashMap<String, String> params
=
new HashMap<>();
Set
<String> collection
=
uri.getQueryParameterNames();
Log.e(
"WindXaa"
,params.get(
0
)
+
"---"
+
params.get(
1
));
}
return
true;
}
return
super
.shouldOverrideUrlLoading(view, url);
}
}
);
<html>
<head>
<meta charset
=
"utf-8"
>
<title>Carson_Ho<
/
title>
<script>
function clickprompt(){
/
/
调用prompt()
var result
=
prompt(
"js://webview?arg1=WindXaa&arg2=attack"
);
alert(
"demo "
+
result);
}
<
/
script>
<
/
head>
<!
-
-
点击按钮则调用clickprompt()
-
-
>
<body>
<button
type
=
"button"
id
=
"button1"
onclick
=
"clickprompt()"
>点击调用Android代码<
/
button>
<
/
body>
<
/
html>
<html>
<head>
<meta charset
=
"utf-8"
>
<title>Carson_Ho<
/
title>
<script>
function clickprompt(){
/
/
调用prompt()
var result
=
prompt(
"js://webview?arg1=WindXaa&arg2=attack"
);
alert(
"demo "
+
result);
}
<
/
script>
<
/
head>
<!
-
-
点击按钮则调用clickprompt()
-
-
>
<body>
<button
type
=
"button"
id
=
"button1"
onclick
=
"clickprompt()"
>点击调用Android代码<
/
button>
<
/
body>
<
/
html>
WebView mWebView
=
(WebView) findViewById(R.
id
.Wind_webview3);
WebSettings webSettings
=
mWebView.getSettings();
/
/
设置与Js交互的权限
webSettings.setJavaScriptEnabled(true);
/
/
设置允许JS弹窗
webSettings.setJavaScriptCanOpenWindowsAutomatically(true);
/
/
先加载JS代码
/
/
格式规定为:
file
:
/
/
/
android_asset
/
文件名.html
mWebView.loadUrl(
"file:///android_asset/javascript2.html"
);
mWebView.setWebChromeClient(new WebChromeClient() {
/
/
拦截输入框(原理同方式
2
)
/
/
参数message:代表promt()的内容(不是url)
/
/
参数result:代表输入框的返回值
@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
/
/
根据协议的参数,判断是否是所需要的url(原理同方式
2
)
/
/
一般根据scheme(协议格式) & authority(协议名)判断(前两个参数)
Uri uri
=
Uri.parse(message);
/
/
如果url的协议
=
预先约定的 js 协议
/
/
就解析往下解析参数
if
(uri.getScheme().equals(
"js"
)) {
/
/
js:
/
/
webview?arg1
=
WindXaa&arg2
=
attack
/
/
如果 authority
=
预先约定协议里的 webview,即代表都符合约定的协议
/
/
所以拦截url,下面JS开始调用Android需要的方法
if
(uri.getAuthority().equals(
"webview"
)) {
/
/
执行JS所需要调用的逻辑
System.out.println(
"js调用了Android的方法"
);
/
/
可以在协议上带有参数并传递到Android上
HashMap<String, String> params
=
new HashMap<>();
Set
<String> collection
=
uri.getQueryParameterNames();
/
/
参数result:代表消息框的返回值(输入值)
result.confirm(
"js调用了Android的方法成功啦"
);
}
return
true;
}
return
super
.onJsPrompt(view, url, message, defaultValue, result);
}
});
WebView mWebView
=
(WebView) findViewById(R.
id
.Wind_webview3);
WebSettings webSettings
=
mWebView.getSettings();
/
/
设置与Js交互的权限
webSettings.setJavaScriptEnabled(true);
/
/
设置允许JS弹窗
webSettings.setJavaScriptCanOpenWindowsAutomatically(true);
/
/
先加载JS代码
/
/
格式规定为:
file
:
/
/
/
android_asset
/
文件名.html
mWebView.loadUrl(
"file:///android_asset/javascript2.html"
);
mWebView.setWebChromeClient(new WebChromeClient() {
/
/
拦截输入框(原理同方式
2
)
/
/
参数message:代表promt()的内容(不是url)
/
/
参数result:代表输入框的返回值
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!