采用UIWebView時(shí),用微信授權(quán)后進(jìn)入綁定手機(jī)號(hào)頁(yè)面截驮,綁定手機(jī)號(hào)成功笑陈,然后重新生成一個(gè)頁(yè)面(UIViewController主頁(yè)),進(jìn)入新頁(yè)面銷毀綁定手機(jī)號(hào)h5頁(yè)面(UIViewController)葵袭,主頁(yè)正常顯示涵妥。但是采用WKWebView,同樣的處理坡锡,這個(gè)主頁(yè)顯示是沒有綁定手機(jī)號(hào)的下載二維碼頁(yè)面蓬网。網(wǎng)上搜索到的說WKWebView的cookie需要用戶注入,而UIWebView是cookie自己注入和保存娜氏。我把cookie從綁定手機(jī)號(hào)頁(yè)面取出拳缠,傳遞到主頁(yè)頁(yè)面并且注入這個(gè)cookie還是不能顯示主頁(yè)。分析是cookie的問題贸弥,不知道獲取和注入的cookie哪里出問題了窟坐,希望大神指點(diǎn)?沒有辦法绵疲,現(xiàn)在暫時(shí)不跳轉(zhuǎn)頁(yè)面哲鸳,一個(gè)控制器處理所有js頁(yè)面的顯示了。
原因是: WKWebView 是一個(gè)多進(jìn)程組件盔憨,每個(gè)WKWebView頁(yè)面進(jìn)程都有自己的cookie徙菠,它們向服務(wù)器發(fā)送請(qǐng)求時(shí)都自己帶上自己的cookie,所以你在app中無論怎么攔截都發(fā)現(xiàn)請(qǐng)求中沒有帶cookie,實(shí)際上WKWebView頁(yè)面進(jìn)程肯定代了帶了cookie,不然服務(wù)器返回錯(cuò)誤郁岩。打印的cookie是:Cookie:JSESSIONID=A2B33F508E609B8208D8EA148114794E; _bl_uid=sOjwaley6yX6OFcn3nap0qt6p8dR婿奔。并且我測(cè)試發(fā)現(xiàn)_bl_uid有低概率沒有,JSESSIONID都存在问慎,還存在兩個(gè)相同的_bl_uid帶不同的值的情況萍摊。估計(jì)這就是WKWebView的cookie返回的說法吧,至于兩對(duì)bl_uid鍵值對(duì)如叼,估計(jì)是強(qiáng)制想向請(qǐng)求的HTTPHeaderFields注入cookie引起冰木。而一旦注冊(cè) http(s) scheme 后,你發(fā)現(xiàn)你跳轉(zhuǎn)的新頁(yè)面就正常,并且cookie的鍵值對(duì)還多了一對(duì)(如:Cookie:JSESSIONID=143219E3B0D66946C4D949D50811F88C; _bl_uid=e9jqhlz96tX9Fhhv9fh94s2qtRvm; PS=o8SkWwD1RV7VYdPH_PGzgC5EYdv4踊沸。注意:這里指的是通過[NSHTTPCookieStorage sharedHTTPCookieStorage]獲取到的app本地cookie, 不是通過通常decidePolicyForNavigationResponse(實(shí)際上兩種情況通過該函數(shù)獲取的cookie只有JSESSIONID=143219E3B0D66946C4D949D50811F88C一對(duì)鍵值歇终,只有讀本地cookie時(shí)不同)獲取到的cookie。)逼龟,fsCachedData存在大量緩存數(shù)據(jù)(注冊(cè) http(s) scheme前的app沒有那么多數(shù)據(jù))评凝,但是這樣做的嚴(yán)重后果是post 請(qǐng)求 body 數(shù)據(jù)被清空(這個(gè)問題我遇到過,是現(xiàn)在一直真實(shí)存在的問題)审轮。若從正常的微信授權(quán)成功h5頁(yè)面A跳轉(zhuǎn)的新控制器頁(yè)面(h5頁(yè)面)B肥哎,若在B頁(yè)面加載發(fā)送請(qǐng)求時(shí)設(shè)置cookie,那么B頁(yè)面加載失敗,從B頁(yè)面返回A頁(yè)面疾渣,刷新A頁(yè)面的相同控制器的子頁(yè)面請(qǐng)求全部失敗。我研究了三天了崖飘,想在不注冊(cè)http(s) scheme 的情況下正常加載B頁(yè)面成功都不可能榴捡。
WKWebView中Cookie混亂問題:按道理來說每個(gè)WKWebView都有一個(gè)單獨(dú)的存儲(chǔ)Cookies的空間,相互不影響朱浴,但是吊圾,奇妙之處就是我在一個(gè)UIViewController中生成了一個(gè)WKWebView,然后進(jìn)行了一系列的網(wǎng)絡(luò)訪問后翰蠢,推出并銷毀這個(gè)UIViewcontroller项乒;在下次進(jìn)來的時(shí)候這個(gè)WKWebView會(huì)攜帶上次訪問的部分Cookies。
??這個(gè)原因是WKWebView會(huì)將Cookie存儲(chǔ)到沙盒目錄的文件中梁沧,下次WKWebView被實(shí)例化的時(shí)候檀何,會(huì)去同步這個(gè)文件中的Cookies。
??decidePolicyForNavigationAction函數(shù)中navigationAction.request是只讀的廷支,decidePolicyForNavigationResponse函數(shù)的navigationResponse.response也是只讀的频鉴。你在這些函數(shù)中也沒有辦法重置請(qǐng)求的allHTTPHeaderFields的字段。
使用UIWebView沒有這樣的問題恋拍,這也許是UIWebView沒有完全代替WKWebView原因之一吧垛孔!
參考文章:《【騰訊Bugly干貨分享】WKWebView 那些坑》https://blog.csdn.net/tencent_bugly/article/details/54668721/。
測(cè)試使用的代碼如一施敢,它實(shí)際上及時(shí)更新cookie文件周荐,由于WKWebView和本app不在一個(gè)進(jìn)程中,它們不在一個(gè)程序空間僵娃,他們都有自己的cookie概作,它們兩者之間資源共享需要進(jìn)程間通信,你及時(shí)更新的是app空間的cookie悯许,不能處理WKWebView的cookie及時(shí)同步到app空間仆嗦,所以不能解決該問題:
//這個(gè)是網(wǎng)頁(yè)加載完成,導(dǎo)航的變化
-(void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{
NSString *strRequest = [webView.URL.absoluteString stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
FLDDLogVerbose(@"isHaveTelLoginPage:%d, webView.URL strRequest:%@",[AWSingleObject sharedInstance].isHaveTelLoginPage, strRequest);
// 獲取加載網(wǎng)頁(yè)的標(biāo)題
self.titleLabel.text = self.wkWebView.title;
//取出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);
}];
}
- (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];
NSLog(@"cookie:%@", cookie);
}
decisionHandler(WKNavigationResponsePolicyAllow);
}
測(cè)試方法二先壕,B頁(yè)面加載出來的不是期望的cookie校驗(yàn)成功的頁(yè)面瘩扼, 由于WKWebView和本app不在一個(gè)進(jìn)程中谆甜,它們不在一個(gè)程序空間,他們都有自己的cookie集绰,它們兩者之間資源共享需要進(jìn)程間通信规辱,你及時(shí)更新的是app空間的cookie,不能處理WKWebView的cookie及時(shí)同步到app空間栽燕,所以不能解決本問題:
#pragma mark - 頁(yè)面加載前處理
- (void)beforePush:(NSDictionary *)params
{
[super beforePush:params];
NSDictionary *userInfo = params[MGJRouterParameterUserInfo];
if ([[userInfo safeObjectForKey:@"jsWebEntity"] isKindOfClass:[AWJsWebEntity class]]) {
AWJsWebEntity *jsWebEntity = [userInfo safeObjectForKey:@"jsWebEntity"];
NSMutableArray *cookiesArr = [NSMutableArray array];
/** 獲取NSHTTPCookieStorage cookies */
NSHTTPCookieStorage * shareCookie = [NSHTTPCookieStorage sharedHTTPCookieStorage];
for (NSHTTPCookie *cookie in shareCookie.cookies){
[cookiesArr addObject:cookie];
}
self.cookiesArr = cookiesArr;
// self.cookies= [userInfo safeObjectForKey:@"cookies"];
[self loadWebURLSring:jsWebEntity.url];
}
}
#pragma mark ================ 加載方式 ================
- (void)webViewloadURLType{
// NSMutableURLRequest * Request_zsj = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:self.URLString] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10];
NSMutableURLRequest *Request_zsj = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:self.URLString]];
NSString *cookie = [self readCurrentCookieWithDomain:self.URLString];
//cookie = [NSString stringWithFormat:@"%@;PS=o8SkWwD1RV7VYdPH_PGzgC5EYdv4", cookie];
[Request_zsj addValue:cookie forHTTPHeaderField:@"Cookie"];
NSString *cookieSting = @"";
for (NSHTTPCookie *cookie in self.cookiesArr){
if(!isEmptyString(cookieSting))
{
cookieSting = [NSString stringWithFormat:@"%@; %@=%@",cookieSting, cookie.name,cookie.value];
}
else
{
cookieSting = [NSString stringWithFormat:@"%@=%@",cookie.name,cookie.value];
}
}
// [Request_zsj setValue:cookieSting forHTTPHeaderField:@"Cookie"];
NSLog(@"Cookie:%@", cookieSting);
[Request_zsj setValue:cookieSting forHTTPHeaderField:@"Cookie"];
// [Request_zsj setValue:@"Mozilla/5.0 (iPhone; CPU iPhone OS 11_4_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15G77Yixiangweipai/0.0.1" forHTTPHeaderField:@"User-Agent"];
NSLog(@"task.url:%@ \n currentRequest.allHTTPHeaderFields:%@", [NSString stringWithFormat:@"%@", Request_zsj.URL], Request_zsj.allHTTPHeaderFields);
// WKHTTPCookieStore *cookieStore = self.wkWebView.configuration.websiteDataStore.httpCookieStore;
// [cookieStore setCookie:self.cookies completionHandler:nil];
//加載網(wǎng)頁(yè)
[self.wkWebView loadRequest:Request_zsj];
}
- (NSString *)readCurrentCookieWithDomain:(NSString *)domainStr{
NSHTTPCookieStorage*cookieJar = [NSHTTPCookieStorage sharedHTTPCookieStorage];
NSMutableString * cookieString = [[NSMutableString alloc]init];
for (NSHTTPCookie*cookie in [cookieJar cookies]) {
[cookieString appendFormat:@"%@=%@;",cookie.name,cookie.value];
}
//刪除最后一個(gè)“罕袋;”
[cookieString deleteCharactersInRange:NSMakeRange(cookieString.length - 1, 1)];
return cookieString;
}
測(cè)試方法三:在A頁(yè)面注冊(cè)http(s) scheme 的情況[NSURLProtocol wk_registerScheme:@”https”]; 當(dāng)然你使用的h5頁(yè)面地址是以http:開頭的修改為:[NSURLProtocol wk_registerScheme:@”http”];具體SURLProtocol怎么用,大家在網(wǎng)上搜索一下吧碍岔,后期我會(huì)寫一篇關(guān)于js標(biāo)簽圖片在iOS替換的文章有相關(guān)的介紹)浴讯。app會(huì)攔截該應(yīng)用向服務(wù)器發(fā)送的所有https網(wǎng)絡(luò)請(qǐng)求(包括js,cs,png等資源請(qǐng)求,注意:攔截的請(qǐng)求是WKWebView在HTTPHeaderField加入cookie前的請(qǐng)求蔼啦,所以在canInitWithRequest函數(shù)打印NSLog(@”canInitWithRequest request.URL.absoluteString = %@,請(qǐng)求方式 == %@,scheme:%@,request.allHTTPHeaderFields:%@”,urlStr,request.HTTPMethod,scheme, request.allHTTPHeaderFields);得到是null,如:2018-08-23 14:16:51.390805+0800 ArtEnjoymentWeChatAuction[12030:1142813] canInitWithRequest request.URL.absoluteString = https://m.1-joy.com/market/product/cat/list.htm,請(qǐng)求方式 == GET,scheme:https,request.allHTTPHeaderFields:(null))榆纽,并且在fsCachedData緩存絕大部份網(wǎng)頁(yè)數(shù)據(jù)(如何獲取fsCachedData文件夾下的文件,見文章《如何在不越獄的情況下捏肢,獲取app中的所有常用文件和文件夾》https://blog.csdn.net/jia12216/article/details/81536960)奈籽。當(dāng)不注冊(cè)http(s) scheme 的情況,fsCachedData文件夾下一般有很少的文件鸵赫,幾乎沒有網(wǎng)頁(yè)數(shù)據(jù)衣屏。這樣做的嚴(yán)重后果是post 請(qǐng)求 body 數(shù)據(jù)被清空(這個(gè)問題我遇到過,是現(xiàn)在一直真實(shí)存在的問題)辩棒,就是h5頁(yè)面自己向后臺(tái)發(fā)送的帶參數(shù)的post請(qǐng)求狼忱,后臺(tái)收到的請(qǐng)求參數(shù)全部沒有。例如在h5頁(yè)面上創(chuàng)建聯(lián)系地址盗温,填寫成功以post的方式上傳后臺(tái)藕赞,那么參數(shù)全部掉丟失。當(dāng)然h5頁(yè)面調(diào)用iOS原生方法卖局,由iOS使用afnet等控件發(fā)送請(qǐng)求發(fā)送給后臺(tái)斧蜕,參數(shù)不會(huì)丟失,也就是只有h5頁(yè)面(WKWebView直接管理)直接向后臺(tái)發(fā)送post請(qǐng)求才會(huì)參數(shù)丟失砚偶。這種方式能解決頁(yè)面間的跳轉(zhuǎn)批销,但是有問題。
現(xiàn)在我只找到這么多的方法染坯,只有第三種不完美的方法能解決該問題均芽。網(wǎng)上說的WKWebView的cookie解決方案也就上面兩個(gè)類似的方案。大家抄來抄去单鹿,根本就沒有實(shí)際測(cè)試過掀宋,解決不了我們的問題。為何需要從A頁(yè)面(h5頁(yè)面,單獨(dú)的UIViewController)跳轉(zhuǎn)到B頁(yè)面(h5頁(yè)面劲妙,單獨(dú)的UIViewController)湃鹊,因?yàn)檫@樣可以把兩者的邏輯分離,若不分離镣奋,A頁(yè)面和B頁(yè)面的邏輯混在一起币呵,就會(huì)造成邏輯過于復(fù)雜,不利于組件化侨颈。
UIWebView是和app在一個(gè)進(jìn)程里余赢,它們的數(shù)據(jù)是共享,操作app的cookie和請(qǐng)求就是操作UIWebView的cookie和請(qǐng)求哈垢。UIWebView在發(fā)送請(qǐng)求時(shí)都自帶cookie妻柒。而WKWebView和app不在一個(gè)進(jìn)程中,操作app的cookie和請(qǐng)求并不都能影響操作WKWebView的cookie和請(qǐng)求温赔。有人說使用WKWebView的app蛤奢,app的cookie有延遲,這個(gè)是客觀存在的陶贼,因?yàn)樗鼈冊(cè)诓煌倪M(jìn)程中,進(jìn)程中的資源是不共享的待秃,它們不是實(shí)時(shí)同步的拜秧,是有同步時(shí)機(jī)的。當(dāng)然你想它們實(shí)時(shí)同步也可以就是注冊(cè)http(s) scheme章郁。
有人說WKWebView發(fā)送請(qǐng)求時(shí)是不自帶cookie的枉氮,這種方法是不正確的,它在WKWebView自己的進(jìn)程中發(fā)送請(qǐng)求是自帶cookie,只是它怎么自帶cookie發(fā)送請(qǐng)求蘋果系統(tǒng)沒有向用戶開放暖庄,你不知道它怎么發(fā)送的聊替,它只提供了提供一個(gè)NSMutableURLRequest請(qǐng)求給WKWebView。至于你想在這個(gè)請(qǐng)求中自己去app的cookie給他培廓,若你沒有跳轉(zhuǎn)到其他控制器惹悄,那么請(qǐng)求仍舊成功,只是出現(xiàn)本地的cookie可能出現(xiàn)同鍵不同值的鍵值對(duì)肩钠,若你跳轉(zhuǎn)了控制器泣港,那么你添加的cookie無效,因?yàn)槟悴恢繵KWebView怎么自帶cookie的价匠。若真的WKWebView的請(qǐng)求不自帶cookie ,那么我們微信授權(quán)成功進(jìn)入首頁(yè)当纱,讓后在首頁(yè)里跳轉(zhuǎn)頁(yè)面不會(huì)都正常的(我們的頁(yè)面除了協(xié)議頁(yè)面基本都校驗(yàn)cookie的)。我推測(cè)可能是你新起一個(gè)WKWebView頁(yè)面踩窖,也就是新起了一個(gè)WKWebView進(jìn)程坡氯,這個(gè)進(jìn)程首先找自己的cookie文件,若沒有直接把請(qǐng)求的cookie設(shè)置為空了,當(dāng)然它剛建立的進(jìn)程箫柳,當(dāng)然它的cookie文件不存在了手形,所以肯定被設(shè)置為空了,因此新的WKWebView頁(yè)面發(fā)送出去的請(qǐng)求就不自帶cookie了滞时∪保可見由于WKWebView是多進(jìn)程組件,cookie也真夠混亂的坪稽。
既然頁(yè)面不能完美解決兩個(gè)h5頁(yè)面控制器之間的cookie問題曼玩,但是咱們的普通https請(qǐng)求卻不受影響,下面是在h5頁(yè)面控制器里向后臺(tái)服務(wù)器發(fā)送https請(qǐng)求的代碼片段:
NSURL * url = [NSURL URLWithString:@"https://m.1-joy.com/market/user/weixin/subscribe.htm"];
NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:url];
NSURLSession * session = [NSURLSession sharedSession];
NSString *cookie = [self readCurrentCookieWithDomain:self.URLString];
// cookie = [NSString stringWithFormat:@"%@;PS=o8SkWwD1RV7VYdPH_PGzgC5EYdv4", cookie];
[request addValue:cookie forHTTPHeaderField:@"Cookie"];
// 發(fā)送請(qǐng)求
NSURLSessionTask * sessionTask = [session dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (error) {
return;
}
NSString *mmmmmmm = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"mmmmmmm: %@, response:%@, error:%@, request.allHTTPHeaderFields:%@", mmmmmmm, response, error, request.allHTTPHeaderFields);
}];
[sessionTask resume];