多个WKWebView页面的cookie不共享问题及解决方案

采用UIWebView时,用微信授权后进入绑定手机号页面,绑定手机号成功,然后重新生成一个页面(UIViewController主页),进入新页面销毁绑定手机号h5页面(UIViewController),主页正常显示。但是采用WKWebView,同样的处理,这个主页显示是没有绑定手机号的下载二维码页面。网上搜索到的说WKWebView的cookie需要用户注入,而UIWebView是cookie自己注入和保存。我把cookie从绑定手机号页面取出,传递到主页页面并且注入这个cookie还是不能显示主页。分析是cookie的问题,不知道获取和注入的cookie哪里出问题了,希望大神指点?没有办法,现在暂时不跳转页面,一个控制器处理所有js页面的显示了。
原因是: WKWebView 是一个多进程组件,每个WKWebView页面进程都有自己的cookie,它们向服务器发送请求时都自己带上自己的cookie,所以你在app中无论怎么拦截都发现请求中没有带cookie,实际上WKWebView页面进程肯定代了带了cookie,不然服务器返回错误。打印的cookie是:Cookie:JSESSIONID=A2B33F508E609B8208D8EA148114794E; _bl_uid=sOjwaley6yX6OFcn3nap0qt6p8dR。并且我测试发现_bl_uid有低概率没有,JSESSIONID都存在,还存在两个相同的_bl_uid带不同的值的情况。估计这就是WKWebView的cookie返回的说法吧,至于两对bl_uid键值对,估计是强制想向请求的HTTPHeaderFields注入cookie引起。而一旦注册 http(s) scheme 后,你发现你跳转的新页面就正常,并且cookie的键值对还多了一对(如:Cookie:JSESSIONID=143219E3B0D66946C4D949D50811F88C; _bl_uid=e9jqhlz96tX9Fhhv9fh94s2qtRvm; PS=o8SkWwD1RV7VYdPH_PGzgC5EYdv4。注意:这里指的是通过[NSHTTPCookieStorage sharedHTTPCookieStorage]获取到的app本地cookie, 不是通过通常decidePolicyForNavigationResponse(实际上两种情况通过该函数获取的cookie只有JSESSIONID=143219E3B0D66946C4D949D50811F88C一对键值,只有读本地cookie时不同)获取到的cookie。),fsCachedData存在大量缓存数据(注册 http(s) scheme前的app没有那么多数据),但是这样做的严重后果是post 请求 body 数据被清空(这个问题我遇到过,是现在一直真实存在的问题)。若从正常的微信授权成功h5页面A跳转的新控制器页面(h5页面)B,若在B页面加载发送请求时设置cookie,那么B页面加载失败,从B页面返回A页面,刷新A页面的相同控制器的子页面请求全部失败。我研究了三天了,想在不注册http(s) scheme 的情况下正常加载B页面成功都不可能。
WKWebView中Cookie混乱问题:按道理来说每个WKWebView都有一个单独的存储Cookies的空间,相互不影响,但是,奇妙之处就是我在一个UIViewController中生成了一个WKWebView,然后进行了一系列的网络访问后,推出并销毁这个UIViewcontroller;在下次进来的时候这个WKWebView会携带上次访问的部分Cookies。
  这个原因是WKWebView会将Cookie存储到沙盒目录的文件中,下次WKWebView被实例化的时候,会去同步这个文件中的Cookies。
  decidePolicyForNavigationAction函数中navigationAction.request是只读的,decidePolicyForNavigationResponse函数的navigationResponse.response也是只读的。你在这些函数中也没有办法重置请求的allHTTPHeaderFields的字段。
使用UIWebView没有这样的问题,这也许是UIWebView没有完全代替WKWebView原因之一吧!
参考文章:《【腾讯Bugly干货分享】WKWebView 那些坑》https://blog.csdn.net/tencent_bugly/article/details/54668721/
测试使用的代码如一,它实际上及时更新cookie文件,由于WKWebView和本app不在一个进程中,它们不在一个程序空间,他们都有自己的cookie,它们两者之间资源共享需要进程间通信,你及时更新的是app空间的cookie,不能处理WKWebView的cookie及时同步到app空间,所以不能解决该问题:

//这个是网页加载完成,导航的变化
-(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);
    // 获取加载网页的标题
    self.titleLabel.text = self.wkWebView.title;
    //取出cookie
    NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
    //js函数
    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];
    }
    //执行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);
}

测试方法二,B页面加载出来的不是期望的cookie校验成功的页面, 由于WKWebView和本app不在一个进程中,它们不在一个程序空间,他们都有自己的cookie,它们两者之间资源共享需要进程间通信,你及时更新的是app空间的cookie,不能处理WKWebView的cookie及时同步到app空间,所以不能解决本问题:

#pragma mark - 页面加载前处理
- (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];
    //加载网页
    [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];
    }

    //删除最后一个“;”
    [cookieString deleteCharactersInRange:NSMakeRange(cookieString.length - 1, 1)];
    return cookieString;
}

测试方法三:在A页面注册http(s) scheme 的情况[NSURLProtocol wk_registerScheme:@”https”]; 当然你使用的h5页面地址是以http:开头的修改为:[NSURLProtocol wk_registerScheme:@”http”];具体SURLProtocol怎么用,大家在网上搜索一下吧,后期我会写一篇关于js标签图片在iOS替换的文章有相关的介绍)。app会拦截该应用向服务器发送的所有https网络请求(包括js,cs,png等资源请求,注意:拦截的请求是WKWebView在HTTPHeaderField加入cookie前的请求,所以在canInitWithRequest函数打印NSLog(@”canInitWithRequest request.URL.absoluteString = %@,请求方式 == %@,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,请求方式 == GET,scheme:https,request.allHTTPHeaderFields:(null)),并且在fsCachedData缓存绝大部份网页数据(如何获取fsCachedData文件夹下的文件,见文章《如何在不越狱的情况下,获取app中的所有常用文件和文件夹》https://blog.csdn.net/jia12216/article/details/81536960)。当不注册http(s) scheme 的情况,fsCachedData文件夹下一般有很少的文件,几乎没有网页数据。这样做的严重后果是post 请求 body 数据被清空(这个问题我遇到过,是现在一直真实存在的问题),就是h5页面自己向后台发送的带参数的post请求,后台收到的请求参数全部没有。例如在h5页面上创建联系地址,填写成功以post的方式上传后台,那么参数全部掉丢失。当然h5页面调用iOS原生方法,由iOS使用afnet等控件发送请求发送给后台,参数不会丢失,也就是只有h5页面(WKWebView直接管理)直接向后台发送post请求才会参数丢失。这种方式能解决页面间的跳转,但是有问题。
现在我只找到这么多的方法,只有第三种不完美的方法能解决该问题。网上说的WKWebView的cookie解决方案也就上面两个类似的方案。大家抄来抄去,根本就没有实际测试过,解决不了我们的问题。为何需要从A页面(h5页面,单独的UIViewController)跳转到B页面(h5页面,单独的UIViewController),因为这样可以把两者的逻辑分离,若不分离,A页面和B页面的逻辑混在一起,就会造成逻辑过于复杂,不利于组件化。
UIWebView是和app在一个进程里,它们的数据是共享,操作app的cookie和请求就是操作UIWebView的cookie和请求。UIWebView在发送请求时都自带cookie。而WKWebView和app不在一个进程中,操作app的cookie和请求并不都能影响操作WKWebView的cookie和请求。有人说使用WKWebView的app,app的cookie有延迟,这个是客观存在的,因为它们在不同的进程中,进程中的资源是不共享的,它们不是实时同步的,是有同步时机的。当然你想它们实时同步也可以就是注册http(s) scheme。
有人说WKWebView发送请求时是不自带cookie的,这种方法是不正确的,它在WKWebView自己的进程中发送请求是自带cookie,只是它怎么自带cookie发送请求苹果系统没有向用户开放,你不知道它怎么发送的,它只提供了提供一个NSMutableURLRequest请求给WKWebView。至于你想在这个请求中自己去app的cookie给他,若你没有跳转到其他控制器,那么请求仍旧成功,只是出现本地的cookie可能出现同键不同值的键值对,若你跳转了控制器,那么你添加的cookie无效,因为你不知道WKWebView怎么自带cookie的。若真的WKWebView的请求不自带cookie ,那么我们微信授权成功进入首页,让后在首页里跳转页面不会都正常的(我们的页面除了协议页面基本都校验cookie的)。我推测可能是你新起一个WKWebView页面,也就是新起了一个WKWebView进程,这个进程首先找自己的cookie文件,若没有直接把请求的cookie设置为空了,当然它刚建立的进程,当然它的cookie文件不存在了,所以肯定被设置为空了,因此新的WKWebView页面发送出去的请求就不自带cookie了。可见由于WKWebView是多进程组件,cookie也真够混乱的。
既然页面不能完美解决两个h5页面控制器之间的cookie问题,但是咱们的普通https请求却不受影响,下面是在h5页面控制器里向后台服务器发送https请求的代码片段:

    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"];
    // 发送请求
    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];
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342