從UIWebview換到WKWebView之后,會(huì)發(fā)現(xiàn)管理Cookie是很麻煩事眠菇,經(jīng)常出現(xiàn) App自定義Cookie的值丟失 或 更新不及時(shí) 的情況边败。蘋果iOS11之后也提供了WKWebView的Cookie API WKHTTPCookieStore,但是目前大多數(shù)App最低版本不可能設(shè)置最低版本到iOS11捎废,所以我們只能想別的辦法笑窜。
什么是Cookie?
Cookie是一種早期的客戶端存儲(chǔ)機(jī)制,起初是針對(duì)服務(wù)端腳本設(shè)計(jì)使用的登疗。以Cookie存儲(chǔ)的數(shù)據(jù)排截,不論服務(wù)端是否需要,每次HTTP請(qǐng)求都會(huì)把這些數(shù)據(jù)傳送到服務(wù)端辐益。
Cookie是指Web瀏覽器存儲(chǔ)的少量數(shù)據(jù)断傲,同時(shí)它是與具體的Web頁(yè)面或者站點(diǎn)相關(guān)。Cookie最早是設(shè)置為被服務(wù)端所用的智政,從最底層來(lái)看认罩,作為http協(xié)議的一種拓展實(shí)現(xiàn)它。
Cookie數(shù)據(jù)會(huì)自動(dòng)在Web瀏覽器和Web服務(wù)器之間傳輸续捂,因此服務(wù)端腳本就可以讀寫存儲(chǔ)在客戶端的cookie值垦垂。
Cookie的屬性
除了 name 與 value闺金,Cookie其他屬性:
domain:作用域名
舉例:
設(shè)置domain為 bj.jiehun.com.cn,則在app.jiehun.com.cn 等子域名不會(huì)生效攻谁;
如設(shè)置domain為 .jiehun.com.cn笤喳,則在jiehun.com.cn 的所有子域名生效勺疼。
注意:IP地址特殊,直接設(shè)置原地址即可页慷。
path:作用路徑
舉例:
如在 www.jiehun.com.cn撇簿,設(shè)置 path=/m,則只在 www.jiehun.com.cn/m 或 /m子目錄下生效差购,如 www.jiehun.com.cn/app 就不會(huì)生效;
不設(shè)置默認(rèn)為當(dāng)前URL默認(rèn)路徑汉嗽;我們通常會(huì)設(shè)置 “/” 代表所有目錄欲逃。
expires:過(guò)期時(shí)間,格林威治時(shí)間 (GMT)字符串格式饼暑,設(shè)置過(guò)期時(shí)間后將會(huì)存儲(chǔ)在一個(gè)文件直到過(guò)期稳析。
// 打印當(dāng)前時(shí)間
var date = new Date();
date.setTime(date.getTime());
console.log(date.toGMTString());
// 打印結(jié)果為:Mon, 25 Feb 2019 15:35:03 GMT
// 設(shè)置cookie時(shí)間
document.cookie='name=value;expires=' + date.toGMTString();
max-age:有效時(shí)長(zhǎng)弓叛,單位秒彰居。
// 設(shè)置 cookie 有效期 10秒
document.cookie='name=value;max-age=10;
Cookie默認(rèn)的有效期很短暫,只能維持在Web瀏覽器的回話期間撰筷,一旦用戶關(guān)閉瀏覽器陈惰,Cookie保存的數(shù)據(jù)就全部丟失。
如果想要延長(zhǎng)Cookie的有效期毕籽,可以通過(guò)設(shè)置max-age或expires屬性抬闯。一旦設(shè)置有效期,瀏覽器就會(huì)將Cookie數(shù)據(jù)存儲(chǔ)在一個(gè)文件中关筒,并且直到過(guò)了指定的有效期才會(huì)刪除該文件溶握。
secure: Bool值,是否為安全傳輸蒸播,如設(shè)置true睡榆,在Https或其他網(wǎng)絡(luò)安全協(xié)議才會(huì)傳輸。
演示
我們可以隨便找一個(gè)瀏覽器袍榆,如Safari胀屿,打開后快捷鍵 command + alt + c 打開調(diào)試窗口:
讀取Cookie
document.cookie
保存Cookie
document.cookie='testName=testValue;path=/;max-age=60*60';
因?yàn)镃ookie的名和值中不允許包含分號(hào)、逗號(hào)和空格蜡塌,因此碉纳,在存儲(chǔ)前可以采用全局函數(shù) encodeURIComponent()對(duì)值進(jìn)行編碼。相應(yīng)的馏艾,讀取cookie值的時(shí)候采用decodeURIComponent() 函數(shù)解碼劳曹。
刪除Cookie
相同的名字隨意設(shè)置一個(gè)值奴愉,并將max-age指定為0,相當(dāng)于刪除Cookie铁孵。
document.cookie='testName=;domain=.jiehun.com.cn;path=/;max-age=0';
Cookie的局限性
個(gè)數(shù)限制300(現(xiàn)在已經(jīng)可以超越)锭硼,每個(gè)Cookie數(shù)據(jù)即名字和值的總量不能超過(guò)4KB。
WKWebView 管理Cookie
上面了解了Cookie的基本知識(shí)蜕劝,我們?nèi)绾螒?yīng)用到iOS開發(fā)中檀头?這里主要介紹WKWebview的使用方法。
因?yàn)镴avaScript只能設(shè)置瀏覽器本地的cookie岖沛,往往客戶端第一次請(qǐng)求由開發(fā)者創(chuàng)建Request對(duì)象請(qǐng)求:
[webview loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"url"]]];
所以第一次請(qǐng)求需要手動(dòng)設(shè)置請(qǐng)求頭中的Cookie,可以繼承WKWebView重寫 loadRequest 方法暑始,或者直接通過(guò)runtime Swizzling loadRequest 方法:
/**重寫 loadRequest 方法,在請(qǐng)求頭中添加自定義到cookie*/
- (nullable WKNavigation *)loadRequest:(NSURLRequest *)request{
NSMutableURLRequest *newRequeset = [request mutableCopy];
// 自定義cookie值
NSDictionary *customCookieDic =@{
@"testName1":@"value1",
@"testName2":@"value2"
};
// 拼接cookie字符
NSString *cookie = @"";
for (NSString *key in customCookieDic.allKeys) {
NSString *keyValue = [NSString stringWithFormat:@"%@=%@;",key,[customCookieDic objectForKey:key]];
cookie = [cookie stringByAppendingString:keyValue];
}
// 設(shè)置到請(qǐng)求頭
[newRequeset setValue:cookie forHTTPHeaderField:@"Cookie"];
return [super loadRequest:newRequeset];
}
此時(shí)請(qǐng)求發(fā)出婴削,服務(wù)端可以收到我們請(qǐng)求頭中的cookie值廊镜,且請(qǐng)求頭的cookie會(huì)自動(dòng)保存到瀏覽器。
但我們發(fā)現(xiàn)請(qǐng)求頭中自動(dòng)保存到瀏覽器的cookie并不可靠唉俗,因?yàn)闆](méi)有設(shè)置域名和目錄的作用區(qū)嗤朴,往往瀏覽器內(nèi)二次跳轉(zhuǎn)就會(huì)丟失,我們?nèi)绾蝸?lái)保證瀏覽器的Cookie永不丟失虫溜?
WKUserScript
Webkit支持通過(guò) WKUserScript 向網(wǎng)頁(yè)中注入js腳本:
// 設(shè)置代碼塊
// @Source 腳本代碼
// @injectionTime 執(zhí)行時(shí)機(jī)雹姊,網(wǎng)頁(yè)渲染前或渲染后
// @MainFrameOnly Bool值,YES只注入主幀衡楞,NO所有幀
WKUserScript *cookieInScript = [[WKUserScript alloc] initWithSource:@"js腳本代碼"
injectionTime:WKUserScriptInjectionTimeAtDocumentStart
forMainFrameOnly:NO];
// 插入腳本
[webview.configuration.userContentController addUserScript:cookieInScript];
所以吱雏,保證Cookie不丟失可以通過(guò)此方法注入設(shè)置cookie js代碼,在頁(yè)面每次渲染都會(huì)設(shè)置瘾境,從而保證不會(huì)丟失坎背。
每個(gè)cookie值可以設(shè)置為一個(gè)代碼塊,為了表示每個(gè)代碼塊的唯一寄雀,我們可以在注入腳本的最后添加 注釋標(biāo)示得滤;其實(shí)就是一句注釋掉的代碼,來(lái)確定cookie代碼塊的唯一性盒犹,為以后刪除某個(gè)代碼塊做鋪墊懂更。
舉例設(shè)置某個(gè)cookie值的 js代碼:
document.cookie ='cityId=10010;domain=.jiehun.com.cn;path=/';
// The cookie code identified is cityId (代碼塊標(biāo)示)
為了避免Cookie重復(fù)問(wèn)題,可以直接把cookie設(shè)置在根域名急膀。
刪除代碼塊
每次調(diào)用 addUserScript方法插入腳本塊沮协,代碼塊會(huì)保存在WKUserContentController類的 userScripts數(shù)組屬性中:
@interface WKUserContentController : NSObject <NSSecureCoding>
/*! @abstract The user scripts associated with this user content
controller.
*/
@property (nonatomic, readonly, copy) NSArray<WKUserScript *> *userScripts;
因?yàn)?userScripts 為只讀權(quán)限,我們并不能修改卓嫂,所以修改某個(gè)某一個(gè)cookie代碼塊的時(shí)候慷暂,需要先全部清除,再把不需要?jiǎng)h除的代碼塊重新添加進(jìn)去晨雳,這里就需要用到前面所說(shuō)的代碼塊注釋的唯一標(biāo)示:
/**
刪除某個(gè)代碼片段
@param tag 片段標(biāo)示行瑞,//The cookie code identified is cookieName
*/
- (void)deleteUserSciptWithTag:(NSString *)tag {
if (tag) {
WKUserContentController *userContentController = webView.configuration.userContentController;
NSMutableArray<WKUserScript *> *array = [userContentController.userScripts mutableCopy];
int i = 0;
BOOL isHave = NO;
for (WKUserScript* wkUScript in userContentController.userScripts) {
// 通過(guò)注釋標(biāo)示判斷代碼塊是否為某個(gè)cookie
if ([wkUScript.source containsString:tag]) {
[array removeObjectAtIndex:i];
isHave = YES;
continue;
}
i ++;
}
if (isHave) {
///沒(méi)法修改數(shù)組 只能移除全部 再將不需要?jiǎng)h除的cookie重新添加
[userContentController removeAllUserScripts];
for (WKUserScript* wkUScript in array) {
[userContentController addUserScript:wkUScript];
}
}
}
}
通過(guò)這個(gè)Webkit可注入腳本的邏輯奸腺,我們基本可以保證使用cookie在WKWebview準(zhǔn)確性和不丟失。
下面也寫了一個(gè)Demo來(lái)演示如何通過(guò)一個(gè)代理方法來(lái)管理WKWebView所有Cookie:
github地址:https://github.com/GaoGuohao/GGWkCookie