【iOS】WKWebView使用Cookies遇到的坑

Apple推出WKWebView已經有一段時間了,相對于UIWebView而言,內存占用只有UIWebView的一半左右,但是響應速度和效率上卻是UIWebView的兩倍。
??總結WKWebView使用方法的帖子文章很多,這里不再贅述,這里重點總結一下cookies共享問題。

WKWebView會忽視默認的網絡存儲, NSURLCache, NSHTTPCookieStorage, NSCredentialStorage。 目前是這樣的,WKWebView有自己的進程,同樣也有自己的存儲空間用來存儲cookie和cache, 其他的網絡類如NSURLConnection是無法訪問到的。 同時WKWebView發(fā)起的資源請求也是不經過NSURLProtocol的,導致無法自定義請求。

由于以上原因,導致WKWebView無法與App自身的Cookies、UIWebView之間共享Cookies數(shù)據。WKWebView這樣做也有一定的好處,在不用操作原有Cookies的基礎上,獨立的一套Cookies,有效的防止了Web與App Api接口的Cookie相互污染。
??但是在使用過程中也遇到了一些坑。

有的時候因為業(yè)務需求,就是需要相互共享Cookies,這樣的案例也有很多,支付寶中大量的Web中,應該就有很多Cookie是和App共享的吧。


NSHTTPCookieStorage Cookies共享

在WkWebView接收到Response后,將Response帶的Cookies取出,然后直接放入[NSHTTPCookieStorage sharedHTTPCookieStorage] 容器中:

- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{
    NSHTTPURLResponse *response = (NSHTTPURLResponse *)navigationResponse.response;
    NSArray *cookies =[NSHTTPCookie cookiesWithResponseHeaderFields:[response allHeaderFields] forURL:response.URL];
    for (NSHTTPCookie *cookie in cookies) {
        [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie];
    }
    decisionHandler(WKNavigationResponsePolicyAllow);
}

然后在完全加載完成后:

// 頁面加載完成之后調用
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{
    //取出cookie
    NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
    //js函數(shù)
    NSString *JSFuncString =
    @"function setCookie(name,value,expires)\
    {\
    var oDate=new Date();\
    oDate.setDate(oDate.getDate()+expires);\
    document.cookie=name+'='+value+';expires='+oDate+';path=/'\
    }\
    function getCookie(name)\
    {\
    var arr = document.cookie.match(new RegExp('(^| )'+name+'=({FNXX==XXFN}*)(;|$)'));\
    if(arr != null) return unescape(arr[2]); return null;\
    }\
    function delCookie(name)\
    {\
    var exp = new Date();\
    exp.setTime(exp.getTime() - 1);\
    var cval=getCookie(name);\
    if(cval!=null) document.cookie= name + '='+cval+';expires='+exp.toGMTString();\
    }";

    //拼湊js字符串
    NSMutableString *JSCookieString = JSFuncString.mutableCopy;
    for (NSHTTPCookie *cookie in cookieStorage.cookies) {
        NSString *excuteJSString = [NSString stringWithFormat:@"setCookie('%@', '%@', 1);", cookie.name, cookie.value];
        [JSCookieString appendString:excuteJSString];
    }
    //執(zhí)行js
    [webView evaluateJavaScript:JSCookieString completionHandler:^(id obj, NSError * _Nullable error) {
        NSLog(@"%@",error);
    }];
}

為什么在完全加載完成后需要重新給WKWebView設置Cookie呢?如果你不這樣做的話很有可能因為a標簽跳轉,導致下一次跳轉的時候Cookie丟失。

302跳轉Set-Cookie丟失

上面的方法可以將服務器Set-Cookie攜帶到下一次請求中。但是如果302跳轉出現(xiàn)在你的第一次加載并且你使用了下面的方法設置第一次加載的Cookies,那么在302跳轉時服務器Set-Cookie將不會被攜帶到下一次302跳轉的目標請求中。

NSMutableURLRequest *request= [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https:/a.com/test1"]];
[request setValue:[NSString stringWithFormat:@"%@=%@",@"a", @"1"] forHTTPHeaderField:@"Cookie"];

比如:第一次加載https:/a.com/test1,然后加載https:/a.com/test1設置Cookie為a=1,服務器在https:/a.com/test1中Set-Cookie a=2;然后302跳轉到https:/a.com/test2,這個時候會發(fā)現(xiàn)https:/a.com/test2中獲取到的a還是1,Set-Cookie并沒有成功。

解決辦法

1.加載一個本地為空的html,域名指向你的第一次加載的url的域名。

//加載本地html
[self.webView loadHTMLString:@"" baseURL:[NSURL URLWithString:@"https:/a.com"]];

2.通過以下方法,在第一次加載完成后,將需要設置的Cookies設置到WKWebView中,因為是加載的本地的html以下方法會立即執(zhí)行。

// 頁面加載完成之后調用
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{
    
    if (isFirstLoaded) {
        NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
        //js函數(shù)
        NSString *JSFuncString =
        @"function setCookie(name,value,expires)\
        {\
        var oDate=new Date();\
        oDate.setDate(oDate.getDate()+expires);\
        document.cookie=name+'='+value+';expires='+oDate+';path=/'\
        }";
        
        //拼湊js字符串,按照自己的需求拼湊Cookie
        NSMutableString *JSCookieString = JSFuncString.mutableCopy;
        for (NSHTTPCookie *cookie in cookieStorage.cookies) {
            if (![cookie.name isEqualToString:@"__cust"]) {
                NSString *excuteJSString = [NSString stringWithFormat:@"setCookie('%@', '%@', 3);", cookie.name, cookie.value];
                [JSCookieString appendString:excuteJSString];
            }
        }
        
        //執(zhí)行js
        [webView evaluateJavaScript:JSCookieString completionHandler:^(id obj, NSError * _Nullable error) {
            //加載真正的第一次Request
            [self loadRealRequest];
        }];
    }
}

WKWebView中Cookie混亂

按道理來說每個WKWebView都有一個單獨的存儲Cookies的空間,相互不影響,但是,奇妙之處就是我在一個UIViewController中生成了一個WKWebView,然后進行了一系列的網絡訪問后,推出并銷毀這個UIViewcontroller;在下次進來的時候這個WKWebView會攜帶上次訪問的部分Cookies。
??這個原因是WKWebView會將Cookie存儲到沙盒目錄的文件中,下次WKWebView被實例化的時候,會去同步這個文件中的Cookies,如果不希望它去同步之歌Cookies,那么直接刪掉好了。

- (void)deleteWebCache {
    if ([[UIDevice currentDevice].systemVersion floatValue] >= 9.0) {
        NSSet *websiteDataTypes = [NSSet setWithArray:@[
                                WKWebsiteDataTypeDiskCache,
                                //WKWebsiteDataTypeOfflineWebApplicationCache,
                                WKWebsiteDataTypeMemoryCache,
                                //WKWebsiteDataTypeLocalStorage,
                                //WKWebsiteDataTypeCookies,
                                //WKWebsiteDataTypeSessionStorage,
                                //WKWebsiteDataTypeIndexedDBDatabases,
                                //WKWebsiteDataTypeWebSQLDatabases
                                ]];
        //// All kinds of data
        //NSSet *websiteDataTypes = [WKWebsiteDataStore allWebsiteDataTypes];
        //// Date from
        NSDate *dateFrom = [NSDate dateWithTimeIntervalSince1970:0];
        //// Execute
        [[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:websiteDataTypes modifiedSince:dateFrom completionHandler:^{
            // Done
        }];
    } else {
        NSString *libraryPath = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0];
        NSString *cookiesFolderPath = [libraryPath stringByAppendingString:@"/Cookies"];
        NSError *errors;
        [[NSFileManager defaultManager] removeItemAtPath:cookiesFolderPath error:&errors];
    }

}

不足之處,歡迎輕輕地噴一下......

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容