Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
web安全對(duì)iOS開(kāi)發(fā)者來(lái)說(shuō)重要嗎?重要!APP中通常會(huì)使用很多web頁(yè)面,例如廣告、登錄流程、閃屏,或者需要使用跨平臺(tái)功能的時(shí)候。你可能在頁(yè)面中僅僅一部分使用web,也可以整個(gè)頁(yè)面都是webView,甚至做一個(gè)web app。因此web安全對(duì)于app來(lái)說(shuō)非常重要。
來(lái)自web的安全攻擊有以下幾種:
本文將針對(duì)這三種攻擊類(lèi)型,給出安全防御措施。
網(wǎng)絡(luò)傳輸相信大家都很熟悉了,安全的傳輸能夠保證接收到的數(shù)據(jù)來(lái)自可信任的站點(diǎn),并且在傳輸工程中不會(huì)被篡改。安全傳輸是其它安全措施的基礎(chǔ),采取的措施包括:
Allow Arbitrary Loads in Web Content 這個(gè)開(kāi)關(guān)一定要置為 NO!
web的內(nèi)容可以來(lái)自任何站點(diǎn),例如,webView上的一張圖片可以來(lái)自任何服務(wù)器,也可以從任意服務(wù)器上加載一個(gè)腳本或iframe。需要注意的是要當(dāng)心來(lái)自其它服務(wù)器的資源。跨域的保護(hù)已經(jīng)有20多年的歷史,并且形成了基本原則--同源策略:只有和頁(yè)面來(lái)源相同的腳本才會(huì)被該頁(yè)面執(zhí)行。例如iframe來(lái)自不同的域名,同源策略不允許加載這個(gè)iframe。僅僅靠同源策略還是不夠的,還需要采取其它的防御措施。
服務(wù)器可能會(huì)發(fā)生異常導(dǎo)致下發(fā)錯(cuò)誤的資源使得web發(fā)生crash,但是開(kāi)發(fā)者通常是知道所要請(qǐng)求哪個(gè)資源的,在腳本里面增加一個(gè)檢查簽名。如果簽名匹配則認(rèn)為是下發(fā)了正確的資源,如果不匹配仍然可以正常工作,此時(shí)嘗試從頁(yè)面的資源里查找或者從自己的服務(wù)器重新加載。這樣做雖然降低了性能,但是提升了安全性。
<script src="https://cdn.example/framework.js" integrity="sha256-8WqyJLuWKRB...oZkCnxQbWwJVw="> </script> window.framwork || // reload from own domain
HTTP response: :status:200 Content-Security-Policy: default 'self'; // No inline script-src cdn.example; frame-src social.example; frame-ancestors news.example;
HTTP response的Header里面,default設(shè)置成自己,默認(rèn)只能加載同源的資源;script-src和frame-src 分別指定可信任的腳本和iframe的來(lái)源;frame-ancestor設(shè)置成news.example,指定只有news.example可以iframe我們的web。
另外不使用inline屬性的腳本也是一種防御措施,不使用inline腳本,只從文件加載腳本,這么做分離了邏輯和文件,更加安全。
HTTPOnly cookies作為一種安全措施,已經(jīng)有至少15年的使用歷史。在這之前script通過(guò)document.cookie這個(gè)強(qiáng)大的api能拿到文檔的cookie,留下安全隱患。HTTPOnly cookies能夠阻止這種情況,只允許HTTP請(qǐng)求訪(fǎng)問(wèn)cookie,禁止使用script訪(fǎng)問(wèn)cookie。它的使用方式很簡(jiǎn)單,只需要在HTTP response的Header里面加上HttpOnly這一項(xiàng),如下
HTTP response: :status:200 Set-Cookie: auth=abc...123; HttpOnly;
在HTTP response的Header里面將SameSite cookies這一項(xiàng)設(shè)置為Strict,那么將不允許把cookie從一個(gè)域名發(fā)送到另一個(gè)域名。例如其他人的web里面嵌入了我們的web,如果我們的服務(wù)器HTTP response的Header里面SameSite cookies=Strict,那么其他人將無(wú)法使用他的cookie來(lái)訪(fǎng)問(wèn)我們的服務(wù)器。
HTTP response: :status:200 Set-Cookie: auth=abc...123; HttpOnly; SameSite=strict
Cross-Origin-Resource-Policy是推出的新功能。之前web可以加載任意web中的資源,例如圖片或者script。在HTTP response的Header里面將Cross-Origin-Resource-Policy這一項(xiàng)設(shè)置為Same,將不允許別人的web向我們的服務(wù)器請(qǐng)求圖片或者script,但是我們自己的web可以。
HTTP response: :status:200 Cross-Origin-Resource-Policy:Same
Cross-Origin-Window-Policy也是新推出的功能。之前通過(guò)window.open這個(gè)強(qiáng)大的api,其他人的web可以在新窗口中打開(kāi)我們域名下的web,通過(guò)一些手段可以修改我們的web,導(dǎo)航到攻擊者指定的頁(yè)面。在HTTP response的Header里面將Cross-Origin-Resource-Policy這一項(xiàng)設(shè)置為Deny,將阻止其他人修改我們web中的內(nèi)容,當(dāng)然別人仍然還是可以打開(kāi)我們的web。Cross-Origin-Resource-Policy適用于希望使用post message 進(jìn)行窗口間通信,但是不想讓別人控制我們自己web內(nèi)容的情況。
HTTP response: :status:200 Cross-Origin-Window-Policy:Deny
Cross-Origin Attacks
Cross-Site Scripting
例如我們的web里面有一個(gè)文本框,用戶(hù)可以輸入文字,如下圖。假如攻擊者注入了這么一段腳本,如果沒(méi)有采取防御措施,那么我們web的cookie就會(huì)被盜取。
在HTTP response的Header中添加HTTPOnly這一項(xiàng),就能阻止腳本訪(fǎng)問(wèn)文檔的cookie,從而防御跨域腳本攻擊。
另外一種防御手段是Content-Security-Policy,如下
HTTP response: :status:200 Content-Security-Policy: default-src 'self'; // No inline
Content-Security-Policy能保證拒絕加載外部來(lái)源的腳本,并且不使用inline屬性的腳本,只從文件中加載腳本。
例如我們的web需要從某個(gè)外部資源裝載一個(gè)framework,攻擊者可能攔截這個(gè)請(qǐng)求,并把它重定向到自己的攻擊腳本上,如下圖
使用Content-Security-Policy中script-src這個(gè)屬性可以指定信任的腳本來(lái)源,并且在引用資源的時(shí)候指定來(lái)源和校驗(yàn)簽名,如下
在HTTP response中:
HTTP response: :status:200 Content-Security-Policy: default-src 'self'; script-src cdn.example;
在HTML中:
<script src="https://cdn.example/framework.js" integrity="sha256-8WqyJLuWKRB...oZkCnxQbWwJVw="> </script> window.framwork || // reload from own domain
攻擊者可能在自己的web中嵌入我們的web,然后向我們的服務(wù)器發(fā)起一個(gè)偽造的網(wǎng)絡(luò)請(qǐng)求(使用的是攻擊者網(wǎng)站的cookie),如下圖
如果采取了防御措施,將HTTP response的Header里面的SameSite設(shè)置為strict,那么就會(huì)禁止攻擊者網(wǎng)站的cookie發(fā)動(dòng)到我們的服務(wù)器上面,如下
HTTP response: :status:200 Set-Cookie: auth=abc...123; SameSite=strict
防御措施有:
Speculative execution 的定義:預(yù)測(cè)執(zhí)行類(lèi)似于批量執(zhí)行條件判斷語(yǔ)句,例如計(jì)算機(jī)大量執(zhí)行"x是否會(huì)造成數(shù)組array越界"這條指令,就能推測(cè)出這個(gè)數(shù)組的長(zhǎng)度,進(jìn)一步推測(cè)出這個(gè)數(shù)組在內(nèi)存緩沖區(qū)中的地址邊界。利用緩沖區(qū)溢出這種攻擊手段,可以向web中注入攻擊腳本。當(dāng)x超過(guò)數(shù)組邊界的時(shí)候,本來(lái)應(yīng)該執(zhí)行越界的error回調(diào),但是確取出了攻擊腳本并執(zhí)行,造成數(shù)據(jù)泄露。顯然只靠同源策略是無(wú)法防御這種攻擊的,因?yàn)楣裟_本和文檔處在同一個(gè)域名下,并且在同一個(gè)線(xiàn)程中。
防御預(yù)測(cè)執(zhí)行攻擊的方法是確保web內(nèi)容和其他iframe(例如攻擊腳本)處在不同的線(xiàn)程中。
以Safari app為例,WKWebView會(huì)單獨(dú)分離出一個(gè)NetWork線(xiàn)程用于處理添加cookie等邏輯,而且每個(gè)網(wǎng)頁(yè)處在不同的線(xiàn)程當(dāng)中,所以evil網(wǎng)頁(yè)是無(wú)法通過(guò)預(yù)測(cè)執(zhí)行攻擊手段攻擊我們的頁(yè)面。而且因?yàn)镹etWork線(xiàn)程也是獨(dú)立的,所以evil網(wǎng)頁(yè)也無(wú)法通過(guò)預(yù)測(cè)執(zhí)行攻擊手段拿到重要數(shù)據(jù),例如cookie。
如果使用UIWebView,所有的web包括NetWork線(xiàn)程都在app的同一個(gè)線(xiàn)程中,所以是無(wú)法防御預(yù)測(cè)執(zhí)行攻擊手段的。
Content security policy的封鎖功能是處于Network線(xiàn)程中,和web線(xiàn)程是分離的,因此可以防御預(yù)測(cè)執(zhí)行攻擊手段。
例如web要加載一個(gè)廣告iframe,但是這個(gè)廣告iframe被重定向到了一個(gè)攻擊腳本,如果使用了Content security policy,如下,因?yàn)楣裟_本不在信任的frame-src里面,所以會(huì)禁止加載。還有一種情況,攻擊者的web引入了我們的web,因?yàn)樵O(shè)置了frame-ancestors為none,所以會(huì)禁止攻擊者網(wǎng)站引入我們的web,從而防御攻擊。
HTTP response: :status:200 Content-Security-Policy: default-src 'self'; frame-src ad.example social.example frame-ancestors 'none'
HttpOnly cookies 和 SameSite cookies的封鎖功能也是處于Network線(xiàn)程中,和web線(xiàn)程是分離的,因此可以防御預(yù)測(cè)執(zhí)行攻擊手段。HttpOnly cookies能夠禁止攻擊者通過(guò)腳本拿到cookie。SameSite cookies設(shè)為strict能夠禁止cookie從一個(gè)域發(fā)送到另一個(gè)域。
Cross-Origin-Resource-Policy
Cross-Origin-Resource-Policy的封鎖功能也是處于Network線(xiàn)程中,和web線(xiàn)程是分離的,因此可以防御預(yù)測(cè)執(zhí)行攻擊手段。Cross-Origin-Resource-Policy設(shè)置成Same能禁止攻擊者的web加載我們網(wǎng)站的資源。
Window Control Attacks
Cross-Origin-Window-Policy
攻擊者的頁(yè)面可以通過(guò)window.open這個(gè)api在新的窗口打開(kāi)我們的web,攻擊者趁我們不注意的時(shí)候,把我們的頁(yè)面導(dǎo)航到釣魚(yú)頁(yè)面,然后誘導(dǎo)用戶(hù)填寫(xiě)用戶(hù)名和密碼,這樣就竊取到了用戶(hù)信息。把HTTP response Header里面的Cross-Origin-Window-Policy設(shè)置為Deny,能夠禁止攻擊者修改我們的web,這樣攻擊者就無(wú)法導(dǎo)航到釣魚(yú)頁(yè)面。
總結(jié)
每種安全措施防御的攻擊類(lèi)型
言
WKWebView是iOS8 出來(lái)的瀏覽器控件,用來(lái)取代UIWebView.對(duì)于WKWebView與UIWebView的對(duì)比特點(diǎn),這里就不過(guò)多的敘述,都算是老生常談的問(wèn)題了,網(wǎng)上的說(shuō)明也很多.近來(lái)在做Web端,需要植入移動(dòng)端,并且做JS交互工作.以前寫(xiě)過(guò)的JavaScript:淺談iOS與H5的交互-JavaScriptCore框架是用于UIWebView.在WKWebView并不適用了,自己做的過(guò)程中遇到一些坑,在這里總結(jié)一下,做一下記錄.
WKWebView加載本地 html文件
現(xiàn)在的項(xiàng)目要求就是使用存儲(chǔ)在本地的html文件 js文件 css文件 img圖片等文件.我使用HBuilder創(chuàng)建了一個(gè)Demo工程.里面包含了基本的文件以及圖片資源.如下所示.然后拖到Xcode工程中.
然后我們把整個(gè)Html工程文件夾導(dǎo)入工程中.
WKWebView 在iOS 9 有新的加載本地方法- (nullable WKNavigation *)loadFileURL:(NSURL *)URL allowingReadAccessToURL:(NSURL *)readAccessURL API_AVAILABLE(macosx(10.11), ios(9.0));.但是我看了網(wǎng)上有一些博客說(shuō)- (nullable WKNavigation *)loadRequest:(NSURLRequest *)request;并不能加載本地html文件.其實(shí)兩者都能加載.只是需要把路徑寫(xiě)對(duì).第一個(gè)方法就不過(guò)多敘述了.網(wǎng)上有很多的博客.這里我就用第二個(gè)方法來(lái)看一下如何加載.
NSString *path=[[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:@"index.html"];
NSURLRequest *request=[NSURLRequest requestWithURL:[NSURL fileURLWithPath:path]];
[_mainWebView loadRequest: request];
運(yùn)行效果圖如下.
Xcode工程中 html文件 加載js文件 css文件 img文件
上面我們加載了html.可是css樣式 .img文件和js文件都沒(méi)有加載出來(lái).那么我們?cè)撊绾谓鉀Q呢?(html原始加載圖如下所示.)
對(duì)于css文件、img文件 ,js文件我們只需要把html文件中的文件夾路徑刪除即可.如下所示.
???????? 注意:這里是使用的- (WKNavigation *)loadRequest:(NSURLRequest *)request;方式加載的網(wǎng)頁(yè).所有如上設(shè)置即可.如果使用的是loadFileURL:(NSURL *)URL allowingReadAccessToURL:(NSURL *)readAccessURL或者loadHTMLString:(NSString *)string baseURL:(NSURL *)baseURL;都需要設(shè)置本地資源文件所對(duì)應(yīng)的路徑! 我們只需要把資源文件所對(duì)應(yīng)的路徑設(shè)置給readAccessURL 和 baseURL即可(). 例如下面所示.
//假定都在工程根目錄之下.
NSURL *baseURL=[NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]];
[_mainWebView loadHTMLString:[self loadHtmlString] baseURL:baseURL];
對(duì)于js文件,有時(shí)候我們發(fā)現(xiàn)我們就算如上刪除了js文件夾的路徑依然不能使用.這是為什么呢?.這里就要去檢測(cè)查看工程的設(shè)置,是否把js文件當(dāng)成一個(gè)可編譯文件使用了.我們需要把它移動(dòng)到資源文件中.這樣就能正常加載了.如下圖所示.
WKWebView中警告窗 確認(rèn)面板 輸入框的顯示
相比于UIWebView,WKWebView對(duì)警告窗 確認(rèn)面板 輸入框并不能直接顯示.是通過(guò)WKUIDelegate代理方法收到對(duì)應(yīng)的回調(diào)方法.如下所示.
//接收到警告面板
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler;
//接收到確認(rèn)面板
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler;
//接收到輸入框
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable result))completionHandler;
我們只需要對(duì)上面的回調(diào)方法進(jìn)行實(shí)現(xiàn)即可實(shí)現(xiàn)顯示警告窗 確認(rèn)面板 輸入框等需求.
WKWebView中OC方法調(diào)用JS方法
在WKWebView OC方法調(diào)用JS方法方法比較簡(jiǎn)單.我們只需要使用如下方法即可.
- (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^ _Nullable)(_Nullable id, NSError * _Nullable error))completionHandler;
看一下我寫(xiě)的例子.在index.js中定義一個(gè)方法.方法內(nèi)容為彈出一個(gè)警告框.代碼如下所示.
function alertAction(message) {
alert(message);
}
然后在ViewController控制器中定義一個(gè)Button.定義它的點(diǎn)擊方法.代碼如下所示.
- (UIButton *)alertButton{
if (_alertButton==nil) {
_alertButton=[[UIButton alloc] initWithFrame:CGRectMake(KMainWidth*0.2, KMainHeight - 60, KMainWidth * 0.6, 40)];
_alertButton.backgroundColor=[UIColor colorWithRed:250/255.0 green:204/255.0 blue:96/255.0 alpha:1.0];
_alertButton.layer.cornerRadius=6.0f;
_alertButton.layer.masksToBounds=YES;
_alertButton.titleLabel.font=[UIFont systemFontOfSize:16];
[_alertButton setTitle:@"彈出彈窗" forState:UIControlStateNormal];
[_alertButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[_alertButton addTarget:self action:@selector(alertButtonAction) forControlEvents:UIControlEventTouchUpInside];
}
return _alertButton;
}
- (void)alertButtonAction{
[self.mainWebView evaluateJavaScript:@"alertAction('OC調(diào)用JS警告窗方法')" completionHandler:^(id _Nullable item, NSError * _Nullable error) {
NSLog(@"alert");
}];
}
因?yàn)閺棿安荒苤苯语@示.所以我們實(shí)現(xiàn)了對(duì)應(yīng)的代理方法.如下所示.
//接收到警告面板
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler{
UIAlertController *alert=[UIAlertController alertControllerWithTitle:@"提示" message:message preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *action=[UIAlertAction actionWithTitle:@"ok" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
completionHandler();//此處的completionHandler()就是調(diào)用JS方法時(shí),`evaluateJavaScript`方法中的completionHandler
}];
[alert addAction:action];
[self presentViewController:alert animated:YES completion:nil];
}
這時(shí)候我們點(diǎn)擊按鈕就會(huì)調(diào)用對(duì)應(yīng)的JS方法了.相比于JavaScriptCore框架還是非常簡(jiǎn)單的.效果圖如下所示.
WKWebView中JS方法調(diào)用OC方法
上一個(gè)模塊我們看到了OC方法調(diào)用JS方法.那么JS方法調(diào)用OC方法呢?我們也只需要幾步就可以完成調(diào)用.
第一步.我們需要在WKWebView創(chuàng)建的過(guò)程中初始化添加ScriptMessageHandler.(命名自定例如:currentCookies.如下所示)
WKWebViewConfiguration *configuration=[[WKWebViewConfiguration alloc] init];
WKUserContentController *userController=[[WKUserContentController alloc] init];
configuration.userContentController=userController;
_mainWebView=[[WKWebView alloc] initWithFrame:CGRectMake(0, 0, KMainWidth, KMainHeight) configuration:configuration];
[userController addScriptMessageHandler:self name:@"currentCookies"];
然后實(shí)現(xiàn)代理方法.監(jiān)聽(tīng)JS的回調(diào).為了查看效果,我們?nèi)匀皇褂脧棿暗男问秸故疚覀兊幕卣{(diào)信息.代碼如下所示.
//JS調(diào)用的OC回調(diào)方法
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
if ([message.name isEqualToString:@"currentCookies"]) {
NSString *cookiesStr=message.body;
NSLog(@"當(dāng)前的cookie為: %@", cookiesStr);
UIAlertController *alert=[UIAlertController alertControllerWithTitle:@"提示" message:@"JS調(diào)用的OC回調(diào)方法" preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *action=[UIAlertAction actionWithTitle:@"ok" style:UIAlertActionStyleCancel handler:nil];
[alert addAction:action];
[self presentViewController:alert animated:YES completion:nil];
}
}
然后在Html文件中添加點(diǎn)擊方法.
<div id="button_div" onclick="buttonDivAction()">
點(diǎn)擊調(diào)用OC方法
</div>
緊接著.在js文件中實(shí)現(xiàn)點(diǎn)擊事件.這時(shí)候要注意的是ScriptMessageHandler的名稱(chēng)要與上面定義的一致.
function buttonDivAction() {
window.webkit.messageHandlers.currentCookies.postMessage({
"body": "buttonActionMessage"
});
}
然后我們點(diǎn)擊網(wǎng)頁(yè)中對(duì)應(yīng)的div就會(huì)出現(xiàn)對(duì)應(yīng)的彈窗.效果圖如下所示.
并且控制臺(tái)打印信息如下所示.
總結(jié)
相比于UIWebView,WKWebView確實(shí)更加的方便快捷.本篇博客就到這里了.如果有任何問(wèn)題,歡迎在評(píng)論區(qū)回復(fù)騷棟.感謝觀(guān)看,最后還是附上本篇博客的Demo傳送門(mén).
者:Mino
原因:html代碼下載到WebView后,webkit開(kāi)始解析網(wǎng)頁(yè)各個(gè)節(jié)點(diǎn),發(fā)現(xiàn)有外部樣式文件或者外部腳本文件時(shí),會(huì)異步發(fā)起網(wǎng)絡(luò)請(qǐng)求下載文件,但如果在這之前也有解析到image節(jié)點(diǎn),那勢(shì)必也會(huì)發(fā)起網(wǎng)絡(luò)請(qǐng)求下載相應(yīng)的圖片。在網(wǎng)絡(luò)情況較差的情況下,過(guò)多的網(wǎng)絡(luò)請(qǐng)求就會(huì)造成帶寬緊張,影響到css或js文件加載完成的時(shí)間,造成頁(yè)面空白loading過(guò)久。
解決方法:告訴WebView先不要自動(dòng)加載圖片,等頁(yè)面finish后再發(fā)起圖片加載。
//設(shè)置是否開(kāi)啟密碼保存功能,不建議開(kāi)啟,默認(rèn)已經(jīng)做了處理,存在盜取密碼的危險(xiǎn)
WebView.setSavePassword(false);
原因:4.0以上的系統(tǒng)我們開(kāi)啟硬件加速后,WebView渲染頁(yè)面更加快速,拖動(dòng)也更加順滑。但有個(gè)副作用就是,當(dāng)WebView視圖被整體遮住一塊,然后突然恢復(fù)時(shí)(比如使用SlideMenu將WebView從側(cè)邊滑出來(lái)時(shí)),這個(gè)過(guò)渡期會(huì)出現(xiàn)白塊同時(shí)界面閃爍。
解決方法:是在過(guò)渡期前將WebView的硬件加速臨時(shí)關(guān)閉,過(guò)渡期后再開(kāi)啟。
/**
* 請(qǐng)求網(wǎng)絡(luò)出現(xiàn)error
* @param view view
* @param errorCode 錯(cuò)誤
* @param description description
* @param failingUrl 失敗鏈接
*/
@Override
public void onReceivedError(WebView view, int errorCode, String description, String
failingUrl) {
super.onReceivedError(view, errorCode, description, failingUrl);
if (errorCode==404) {
//用javascript隱藏系統(tǒng)定義的404頁(yè)面信息
String data="Page NO FOUND!";
view.loadUrl("javascript:document.body.innerHTML=\"" + data + "\"");
} else {
if (webListener!=null){
webListener.showErrorView();
}
}
}
// 向主機(jī)應(yīng)用程序報(bào)告Web資源加載錯(cuò)誤。這些錯(cuò)誤通常表明無(wú)法連接到服務(wù)器。
// 值得注意的是,不同的是過(guò)時(shí)的版本的回調(diào),新的版本將被稱(chēng)為任何資源(iframe,圖像等)
// 不僅為主頁(yè)。因此,建議在回調(diào)過(guò)程中執(zhí)行最低要求的工作。
// 6.0 之后
@Override
public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
super.onReceivedError(view, request, error);
if (Build.VERSION.SDK_INT >=Build.VERSION_CODES.M) {
X5WebUtils.log("服務(wù)器異常"+error.getDescription().toString());
}
//ToastUtils.showToast("服務(wù)器異常6.0之后");
//當(dāng)加載錯(cuò)誤時(shí),就讓它加載本地錯(cuò)誤網(wǎng)頁(yè)文件
//mWebView.loadUrl("file:///android_asset/errorpage/error.html");
if (webListener!=null){
webListener.showErrorView();
}
}
/**
* 這個(gè)方法主要是監(jiān)聽(tīng)標(biāo)題變化操作的
* @param view view
* @param title 標(biāo)題
*/
@Override
public void onReceivedTitle(WebView view, String title) {
super.onReceivedTitle(view, title);
if (title.contains("404") || title.contains("網(wǎng)頁(yè)無(wú)法打開(kāi)")){
if (webListener!=null){
webListener.showErrorView();
}
} else {
// 設(shè)置title
}
}
原因:WebView.loadUrl("url") 不會(huì)立馬就回調(diào) onPageStarted 或者 onProgressChanged 因?yàn)樵谶@一時(shí)間段,WebView 有可能在初始化內(nèi)核,也有可能在與服務(wù)器建立連接,這個(gè)時(shí)間段容易出現(xiàn)白屏,白屏用戶(hù)體驗(yàn)是很糟糕的。
解決方法:提前顯示進(jìn)度條雖然不是提升性能 , 但是對(duì)用戶(hù)體驗(yàn)來(lái)說(shuō)也是很重要的一點(diǎn)。
/**
* 在加載資源時(shí)通知主機(jī)應(yīng)用程序發(fā)生SSL錯(cuò)誤
* 作用:處理https請(qǐng)求
* @param view view
* @param handler handler
* @param error error
*/
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
super.onReceivedSslError(view, handler, error);
if (error!=null){
String url=error.getUrl();
}
//https忽略證書(shū)問(wèn)題
if (handler!=null){
//表示等待證書(shū)響應(yīng)
handler.proceed();
// handler.cancel(); //表示掛起連接,為默認(rèn)方式
// handler.handleMessage(null); //可做其他處理
}
}
原因:WebView 默認(rèn)開(kāi)啟密碼保存功能 mWebView.setSavePassword(true),如果該功能未關(guān)閉,在用戶(hù)輸入密碼時(shí),會(huì)彈出提示框,詢(xún)問(wèn)用戶(hù)是否保存密碼,如果選擇”是”,密碼會(huì)被明文保到 /data/data/com.package.name/databases/webview.db 中,這樣就有被盜取密碼的危險(xiǎn)。
解決方法:通過(guò) WebSettings.setSavePassword(false) 關(guān)閉密碼保存提醒功能。
@Override
protected void onDestroy() {
try {
//有音頻播放的web頁(yè)面的銷(xiāo)毀邏輯
//在關(guān)閉了Activity時(shí),如果Webview的音樂(lè)或視頻,還在播放。就必須銷(xiāo)毀Webview
//但是注意:webview調(diào)用destory時(shí),webview仍綁定在Activity上
//這是由于自定義webview構(gòu)建時(shí)傳入了該Activity的context對(duì)象
//因此需要先從父容器中移除webview,然后再銷(xiāo)毀webview:
if (webView !=null) {
ViewGroup parent=(ViewGroup) webView.getParent();
if (parent !=null) {
parent.removeView(webView);
}
webView.removeAllViews();
webView.destroy();
webView=null;
}
} catch (Exception e) {
}
super.onDestroy();
}
原因:當(dāng)WebView加載頁(yè)面出錯(cuò)時(shí)(一般為404 NOT FOUND,Android WebView會(huì)默認(rèn)顯示一個(gè)出錯(cuò)界面。當(dāng)WebView加載出錯(cuò)時(shí),會(huì)在WebViewClient實(shí)例中的onReceivedError(),還有onReceivedTitle方法接收到錯(cuò)誤。
解決方法:自定義錯(cuò)誤頁(yè)面樣式。
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
String host=Uri.parse(url).getHost();
if (!BuildConfig.IS_DEBUG) {
if (Arrays.binarySearch(domainList, host) < 0) {
//不在白名單內(nèi),非法網(wǎng)址,這個(gè)時(shí)候給用戶(hù)強(qiáng)烈而明顯的提示
} else {
//合法網(wǎng)址
}
}
}
原因:webView加載一些別人的url時(shí)候,有時(shí)候會(huì)發(fā)生證書(shū)認(rèn)證錯(cuò)誤的情況。
解決方法:要將正常的呈現(xiàn)頁(yè)面給用戶(hù),我們需要忽略證書(shū)錯(cuò)誤,需要調(diào)用WebViewClient類(lèi)的onReceivedSslError方法,調(diào)用handler.proceed()來(lái)忽略該證書(shū)錯(cuò)誤。
//在onResume里面設(shè)置setJavaScriptEnabled(true)。
@Override
protected void onResume() {
super.onResume();
if (mWebView !=null) {
mWebView.getSettings().setJavaScriptEnabled(true);
}
}
//在onStop里面設(shè)置setJavaScriptEnabled(false);
@Override
protected void onStop() {
super.onStop();
if (mWebView !=null) {
mWebView.getSettings().setJavaScriptEnabled(false)
}
}
原因:WebView頁(yè)面中播放了音頻,退出Activity后音頻仍然在播放。
解決方法:需要在Activity的onDestory()中從父容器中移除WebView。
@Override
protected void onDestroy() {
try {
//有音頻播放的web頁(yè)面的銷(xiāo)毀邏輯
//在關(guān)閉了Activity時(shí),如果Webview的音樂(lè)或視頻,還在播放。就必須銷(xiāo)毀Webview
//但是注意:webview調(diào)用destory時(shí),webview仍綁定在Activity上
//這是由于自定義webview構(gòu)建時(shí)傳入了該Activity的context對(duì)象
//因此需要先從父容器中移除webview,然后再銷(xiāo)毀webview:
if (webView !=null) {
ViewGroup parent=(ViewGroup) webView.getParent();
if (parent !=null) {
parent.removeView(webView);
}
webView.removeAllViews();
webView.destroy();
webView=null;
}
} catch (Exception e) {
}
super.onDestroy();
}
原因:客戶(hù)端內(nèi)的WebView都是可以通過(guò)客戶(hù)端的某個(gè)schema打開(kāi)的,而要打開(kāi)頁(yè)面的URL很多都并不寫(xiě)在客戶(hù)端內(nèi),而是可以由URL中的參數(shù)傳遞過(guò)去的。上面4.0.5 使用scheme協(xié)議打開(kāi)鏈接風(fēng)險(xiǎn)已經(jīng)說(shuō)明了scheme使用的危險(xiǎn)性。
解決方法:設(shè)置運(yùn)行訪(fǎng)問(wèn)的白名單,或者當(dāng)用戶(hù)打開(kāi)外部鏈接前給用戶(hù)強(qiáng)烈而明顯的提示。設(shè)置白名單操作其實(shí)和過(guò)濾廣告是一個(gè)意思,這里你可以放一些合法的網(wǎng)址允許訪(fǎng)問(wèn)。
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
String host=Uri.parse(url).getHost();
if (!BuildConfig.IS_DEBUG) {
if (Arrays.binarySearch(domainList, host) < 0) {
//不在白名單內(nèi),非法網(wǎng)址,這個(gè)時(shí)候給用戶(hù)強(qiáng)烈而明顯的提示
} else {
//合法網(wǎng)址
}
}
}
原因:有些手機(jī)你如果webView加載的html里,有一些js一直在執(zhí)行比如動(dòng)畫(huà)之類(lèi)的東西,如果此刻webView 掛在了后臺(tái)這些資源是不會(huì)被釋放用戶(hù)也無(wú)法感知。導(dǎo)致一直占有cpu 耗電特別快。
解決方法:WebView在后臺(tái)的時(shí)候,會(huì)調(diào)用onStop方法,即此時(shí)關(guān)閉js交互,回到前臺(tái)調(diào)用onResume再開(kāi)啟js交互。
//在onResume里面設(shè)置setJavaScriptEnabled(true)。
@Override
protected void onResume() {
super.onResume();
if (mWebView !=null) {
mWebView.getSettings().setJavaScriptEnabled(true);
}
}
//在onStop里面設(shè)置setJavaScriptEnabled(false);
@Override
protected void onStop() {
super.onStop();
if (mWebView !=null) {
mWebView.getSettings().setJavaScriptEnabled(false)
}
}
原因:WebView從Lollipop(5.0)開(kāi)始webView默認(rèn)不允許混合模式, https當(dāng)中不能加載http資源, 而開(kāi)發(fā)的時(shí)候可能使用的是https的鏈接,但是鏈接中的圖片可能是http的,所以顯示圖片失敗。
解決方案:需要設(shè)置開(kāi)啟。
*請(qǐng)認(rèn)真填寫(xiě)需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。