往者不可諫,來者猶可追html
原文連接git
Cookie和Session都是爲了保存客戶端和服務端之間的交互狀態,實現機制不一樣,各有優缺點。github
Session是基於Cookie來實現的,不一樣的是Session自己存在於服務端,可是每次傳輸的時候不會將數據傳輸給客戶端,只是把表明一個客戶端的sessionid(jsessionid只是Tomcat中對sessionid的叫法)寫在客戶端的Cookie中,這樣每次傳輸這個ID就能夠了。web
Session的優點就是傳輸數據量小,比較安全。Session有缺點,就是若是Session不作特殊的處理容易失效、過時、丟失或者Session過多致使服務器內存溢出,而且要實現一個穩定可用安全的分佈式Session框架也是有必定複雜度的。在實際使用中就要結合Cookie和Session的優缺點針對不一樣的問題來設計解決方案。objective-c
WKWebView 發起的請求不會自動帶上存儲於 NSHTTPCookieStorage 容器中的 Cookie
複製代碼
目前許多 H5 業務都依賴於 Cookie 做登陸態校驗,若是登錄是在 WebView 裏作的,不會有什麼問題;可是在不少場景下,在Native作登陸,須要將登陸信息帶給WebView;可是在Native作了登陸,也獲取了Cookie信息,也使用 NSHTTPCookieStorage 將Cookie存到了本地;可是WKWebView在打開時候,不會自動去NSHTTPCookieStorage獲取Cookie信息,這就是著名的首次 WKWebView 請求不攜帶 Cookie 的問題。跨域
WKWebView 實例其實會將 Cookie 存儲於 NSHTTPCookieStorage 中,但存儲時機有延遲,在iOS 8上,當頁面跳轉的時候,當前頁面的 Cookie 會寫入 NSHTTPCookieStorage 中,而在 iOS 10 上,JS 執行 document.cookie 或服務器 set-cookie 注入的 Cookie 會很快同步到 NSHTTPCookieStorage 中。瀏覽器
其實,iOS11 能夠解決首次 WKWebView 請求不攜帶 Cookie 的問題,只要是存在 WKHTTPCookieStore 裏的 cookie,WKWebView 每次請求都會攜帶。緩存
// 方法一
NSHTTPURLResponse *response = (NSHTTPURLResponse *)navigationResponse.response;
NSArray *cookies = [NSHTTPCookie cookiesWithResponseHeaderFields:[response allHeaderFields] forURL:response.URL];
NSLog(@"response-cookies = %@",cookies);
//方法二
NSString *cookieString = [[response allHeaderFields] valueForKey:@"Set-Cookie"];
NSLog(@"cookieString = %@",cookieString);
//方法三(若是有的話)
NSArray<NSHTTPCookie *> *httpCookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];
NSLog(@"httpCookies = %@",httpCookies);
//方法四
if(@available(iOS 11, *)){
//WKHTTPCookieStore的使用
WKHTTPCookieStore *cookieStore = self.wkWebView.configuration.websiteDataStore.httpCookieStore;
//獲取 cookies
[cookieStore getAllCookies:^(NSArray<NSHTTPCookie *> * _Nonnull cookies) {
[cookies enumerateObjectsUsingBlock:^(NSHTTPCookie * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSLog(@"cookieStore-cookies_%@:%@",@(idx),obj);
}];
}];
}
//將cookie設置到本地
for (NSHTTPCookie *cookie in cookies) {
//NSHTTPCookie cookie
[[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie];
}
decisionHandler(WKNavigationResponsePolicyAllow);
}
複製代碼
查看WKHTTPCookieStore中的某個cookie信息,以下安全
version:1
name:Hm_lvt_0c0e9d9b1e7d617b3e6842e85b9fb068
value:1554970993,1554971029,1554971246,1554971319
expiresDate:'2020-04-10 08:28:38 +0000'
created:'2019-04-11 08:28:38 +0000'
sessionOnly:FALSE
domain:.jianshu.com
partition:none
sameSite:none
path:/
isSecure:FALSE
path:"/"
isSecure:FALSE
複製代碼
未過時的 Cookie被持久化存儲在 NSLibraryDirectory
目錄下的 Cookies/
文件夾。bash
Cookie 持久化文件地址在 iOS 9+ 上在NSLibraryDirectory/Cookies
,可是在 iOS 8 上 cookie 被保存在兩部分,一部分如上所述,還有一部分保存在 App 沒法獲取的地方,/Users/Mac/Library/Developer/CoreSimulator/Devices/D2F74420-D59B-4A15-A50B-774D3D01FADE/data/Library/Cookies
,大概就是後者的 Cookie 是 iOS 的 Safari 使用 。
在 Cookies 目錄下兩個文件比較重要;
Cookies.binarycookies
<appid>.binarycookies
複製代碼
二者的區別是 .binarycookies 是 NSHTTPCookieStorage 文件對象;.binarycookies 對應 WKWebview 的實例化對象。
在初始化 WKWebView 的時候,經過 WKUserScript 設置,使用Javascript 注入 Cookie。
//js注入
WKUserContentController* userContentController = [[WKUserContentController alloc]init];
WKUserScript * cookieScript = [[WKUserScript alloc] initWithSource: @"document.cookie ='CookieKey=CookieValue';"injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO];
[userContentController addUserScript:cookieScript];
WKWebViewConfiguration* webViewConfig = [[WKWebViewConfiguration alloc]init];
webViewConfig.userContentController = userContentController;
WKWebView *webView = [[WKWebView alloc] initWithFrame:frame configuration:webViewConfig];
複製代碼
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{
//
}
複製代碼
//request攜帶
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]];
//[request setHTTPShouldHandleCookies:YES];
[request setValue:[NSString stringWithFormat:@"%@=%@",@"CookieKey", @"CookieValue"] forHTTPHeaderField:@"Cookie"];
[webView loadRequest:request];
複製代碼
說明:WKWebView loadRequest 前,在 request header 中設置 Cookie,能夠解決(首個)請求 Cookie 帶不上的問題;
if(@available(iOS 11, *)){
//發送請求前插入cookie
NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];
WKHTTPCookieStore *cookieStore = self.wkWebView.configuration.websiteDataStore.httpCookieStore;
for (NSHTTPCookie *cookie in cookies) {
[cookieStore setCookie:cookie completionHandler:^{
//
}];
}
[self.wkWebView loadRequest:request];
}
複製代碼
Session 級別的 cookie 是保存在 WKProcessPool
裏的,每一個 WKWebview 均可以關聯一個 WKProcessPool
的實例,若是須要在整個 App 生命週期裏訪問 h5 保留 h5 裏的登陸狀態的,能夠將使用 WKProcessPool
的單例來共享登陸狀態。
讓全部 WKWebView 共享同一個 WKProcessPool 實例,能夠實現多個 WKWebView 之間共享 Cookie(session Cookie and persistent Cookie) 數據。不過 WKWebView WKProcessPool 實例在 App 殺進程重啓後會被重置,致使 WKProcessPool 中的 Cookie、session Cookie 數據丟失,目前也沒法實現 WKProcessPool 實例本地化保存。
//WKProcessPool+SharedProcessPool.h
@interface WKProcessPool (SharedProcessPool)
+ (WKProcessPool*)sharedProcessPool;
@end
//WKProcessPool+SharedProcessPool.m
@implementation WKProcessPool (SharedProcessPool)
+ (WKProcessPool*)sharedProcessPool {
static WKProcessPool* shared;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
shared = [[WKProcessPool alloc] init];
});
return shared;
}
@end
//use
config.processPool = [WKProcessPool sharedProcessPool];
self.wkWebView = [[WKWebView alloc]initWithFrame:self.view.bounds configuration:config];
[self.view addSubview:self.wkWebView];
複製代碼
if (@available(iOS 9, *)){
// 以www.baidu.com爲例,是否包含baidu.com
NSString *displayName = @"baidu.com";
WKWebsiteDataStore *dataStore = [WKWebsiteDataStore defaultDataStore];
[dataStore fetchDataRecordsOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] completionHandler:^(NSArray<WKWebsiteDataRecord *> * __nonnull records) {
for (WKWebsiteDataRecord *record in records){
if ([displayName containsString:record.displayName]){
[[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:record.dataTypes forDataRecords:@[record] completionHandler:^{
NSLog(@"Cookies for %@ deleted successfully",record.displayName);
}];
}
}
}];
}
複製代碼
- (void)removeWebViewDataCache:(NSDate *)sinceDate {
if (@available(iOS 11.0, *)) {
// iOS 9 之後終於可使用 WKWebsiteDataStore 來清理緩存
NSSet *websiteDataTypes = [WKWebsiteDataStore allWebsiteDataTypes];
[[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:websiteDataTypes modifiedSince:sinceDate completionHandler:^{
NSLog(@"clear webView cache");
}];
} else {
// iOS 8 能夠經過清理 Library 目錄下的 Cookies 目錄來清除緩存
NSString *libraryPath = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES).firstObject;
NSString *cookiesFolderPath = [libraryPath stringByAppendingString:@"/Cookies"];
[[NSFileManager defaultManager] removeItemAtPath:cookiesFolderPath error:nil];
}
}
複製代碼