WKWebView的cookie问题

以下内容转载http://xiaozhuanlan.com/topic/2517460839 终端杂谈的一篇文章

前言

如果开发者一开始选择 UIWebView 作为 h5 的容器,开发者无须关注 cookie 的存取问题,因为后台下发的cookie会自动存储到NSHTTPCookieStorage这个容器中,webView内部的请求会自动从NSHTTPCookieStorage获取合适的cookie带上去。

但是苹果爸爸说UIWebView在iOS12之后是一个deprecated接口,希望开发者迁移到WKWebView中。但是WKWebView关于cookie的存取却不像UIWebView那么方便,主要是因为整个框架的改变,WKWebView已经不单单是在app这一侧,WKWebView容器发起的h5请求已不在app进程中发起和响应处理,而是在专门的web进程中处理,所以WKWebView的网络请求无法直接从NSHTTPCookieStorage取到cookie,所以当h5访问一些带鉴权的接口就会出现问题。

下面我提供个case给大家解析cookie机制在新容器是如何运转和提供正确的设置姿势。

案例

首先我们先在我们的代码打个log,将后台下发的cookie给打印出来,也验证下后台是否下发成功了:

通过控制台log,发现后台下发的cookie已经同步到NSHTTPCookieStorage,我们打印出cookie的key和value:

接着我启动我们的WKWebView容器发起第一个请求:

同时我们启动Charles进行网络抓包,抓取webview中的网络请求:

图中那个请求就是webview容器的第一个请求,它会进行鉴权相关的判断,通过查看这个请求的请求体发现请求体里没有cookie字段,说明这个请求没有带上cookie。对应的业务展示也变成系统异常:

接下开启Safari的开发调试模式,调试当前这个h5界面,我们发现后台下发的cookie已经同步到WKWebView侧,这是因为WKWebView内也有cookie的容器,而且每隔一段时间就和app侧NSHTTPCookieStorage进行同步,而且这个同步是进程级别的同步,而且这个同步是单向,这个后面会进行解析。为什么第一次请求没有带上cookie是因为app侧NSHTTPCookieStorage的cookie还没同步给WKWebView的cookieStorage,导致网络请求没有带上cookie,当我们开启Safari的开发调试模式的时候已经完成同步,所以我们可以看到对应WKWebView的cookieStorage存的cookie跟app控制台log的cookie一样:

如果这时候我们点击Safari调试器的刷新按钮,响应会是业务正常,因为刷新重新发起请求,这时候请求已经带上cookie

设置cookie的正确姿势

在iOS11之后,苹果爸爸终于理解开发者关于设置cookie的痛楚,所以开放一个接口给我们设置WKWebView的cookieStorage,要注意这个接口是异步的,所以我们需要等待WKWebView异步设置cookie完成后才发起请求:

@wb_weakify(self)

if(@available(iOS11.0, *)) {

[self.webview.configuration.websiteDataStore.httpCookieStoresetCookie:eventCKcompletionHandler:^{

@wb_strongify(self)

NSURLRequest *request = [NSURLRequestrequestWithURL:url];

[self.webviewloadRequest:request];

            }];

为了适配iOS11以前的系统版本,设置cookie就没有那么直接,我们查阅网上很多文章,甚至是一些比较权威的团队发出来的文章,都是以下这种思路:通过key-Value构造一个cookie,WKWebView loadRequest 前,在 request header 中设置 Cookie, 解决首个请求 Cookie 带不上的问题,通过 document.cookie 设置 Cookie 解决后续页面(同域)Ajax、iframe 请求的 Cookie 问题:

NSMutableURLRequest*request = [NSMutableURLRequestrequestWithURL:url];

[request setValue:[NSStringstringWithFormat:@"%@;",[self_getCookieString:eventCK]] forHTTPHeaderField:@"Cookie"];

WKUserScript* cookieScript = [[WKUserScriptalloc] initWithSource:

[NSStringstringWithFormat:@"document.cookie = '%@';",[self_getCookieString:eventCK]]

injectionTime:WKUserScriptInjectionTimeAtDocumentStartforMainFrameOnly:NO];

    [_webview.configuration.userContentController addUserScript: cookieScript];

    [_webview loadRequest:request];

- (NSString*)_getCookieString:(NSHTTPCookie*)cookie {

NSString*string = [NSStringstringWithFormat:@"%@=%@;",

                        cookie.name,

                        cookie.value];

returnstring;

}

按着这些文章改造我们的代码还是没有解决业务鉴权失败的问题,因为这些文章都忽律一个关键的操作,就是cookie的构建,很多文章只是简单构造key-Value,导致因为同源策略请求带不上cookie。cookie有四个关键的标识:value(键值对)、expires(过期日期)、domain(域)、path(路径),如果有一个标识不一样,它就是一个新的cookie,在iOS中如果cookie只指定value,其他会设置为默认值,我们通过Safari调试器捕获这个cookie:

我们可以看到WKWebView的cookieStorage存在两个同名同值的cookie,但是他们的域、路径、过期时间都不同,我们可以看出只设置key-Value的cookie默认的域就是发起的请求的url的域名,对应的路径也是发起请求的url的path,第二个cookie是正确的但他是后面同步过来的,第一个cookie也就是我们通过key-Value构建的cookie,请求带不上去这个cookie,因为它与业务请求不同源(业务请求只支持域为".webank.com"和path为"/"的cookie)。所以正确的姿势应该是构造一个全cookie:

- (NSString*)_getCookieString:(NSHTTPCookie*)cookie {

NSString*string = [NSStringstringWithFormat:@"%@=%@;domain=%@;expiresDate=%@;path=%@;sessionOnly=%@;isSecure=%@",

                        cookie.name,

                        cookie.value,

                        cookie.domain,

                        cookie.expiresDate,

cookie.path ?:@"/",

cookie.isSecure ?@"TRUE":@"FALSE",

cookie.sessionOnly ?@"TRUE":@"FALSE"];

returnstring;

}

经过这样设置之后,业务请求终于能够带上正确的cookie:

CookieStorage同步是单向的?

想必很多基于WKWebView开发的同学都看过bugly的这篇文章《WKWebView 那些坑》,里面提到Cookie那一块内容讲到“ WKWebView 实例其实也会将 Cookie 存储于 NSHTTPCookieStorage 中,但存储时机有延迟,在iOS 8上,当页面跳转的时候,当前页面的 Cookie 会写入 NSHTTPCookieStorage 中,而在 iOS 10 上,JS 执行 document.cookie 或服务器 set-cookie 注入的 Cookie 会很快同步到 NSHTTPCookieStorage 中”。其实这种说法讲的不够准确,只有最后一话的后半句是正确,正确的说法是cookie是同步是单向,且只有app侧的NSHTTPCookieStorage将cookie同步到WKWebView维护的cookieStorage,WKWebView的cookie无法同步到NSHTTPCookieStorage中,本着对读者负责的态度,我进行实验进行验证,验证代码如下,验证步骤已经注释在代码中:

//WKWebView请求带上cookie

WKUserScript* cookieScript = [[WKUserScriptalloc] initWithSource:

[NSStringstringWithFormat:@"document.cookie = '%@';",[self_getCookieString:eventCK]]

injectionTime:WKUserScriptInjectionTimeAtDocumentStartforMainFrameOnly:NO];

            [_webview.configuration.userContentController addUserScript: cookieScript];

            [_webview loadRequest:request];

//一般cookie同步应该在很短时间内完成,我们假定一个较长的时间10s,10s后从NSHTTPCookieStorage获取所有cookie,验证WKWebView的cookie有没有同步到这里

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10*NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

NSArray*cookies = [NSHTTPCookieStoragesharedHTTPCookieStorage].cookies;

for(NSHTTPCookie*ckincookies) {

NSLog(@"app cookies : [%@:%@]",ck.name,ck.value);

                }

            });

//生成测试cookie,key为testWKCK,为了让请求带上cookie,cookie的domain和path设置为符合业务要求的值

- (NSString*)_getCookieString:(NSHTTPCookie*)cookie {

NSString*string = [NSStringstringWithFormat:@"%@=%@;domain=%@;expiresDate=%@;path=%@;sessionOnly=%@;isSecure=%@",

@"testWKCK",

@"111111111",

                        cookie.domain,

                        cookie.expiresDate,

cookie.path ?:@"/",

cookie.isSecure ?@"TRUE":@"FALSE",

cookie.sessionOnly ?@"TRUE":@"FALSE"];

returnstring;

}

结果验证:

通过Sarafi调试器我们可以看到我们WKWebView存在一个名为testWKCK的cookie,我们弄个10秒的定时器,10秒后打印app侧所有cookie,发现只有后台下发的那个cookie,名为testWKCK的cookie在app侧没有保存,这说明了cookie的同步是单向,只有从app侧到webThread侧。

既然是单向同步。那怎么保证WKWebView的cookie也同步到App的NSHTTPCookieStorage?在iOS11之后,苹果提供一个接口用于配置observer监听cookie的改变:

WK_EXTERNAPI_AVAILABLE(macosx(10.13), ios(11.0))

@interfaceWKHTTPCookieStore:NSObject

/*! @abstract Adds a WKHTTPCookieStoreObserver object with the cookie store.

@param observer The observer object to add.

@discussion The observer is not retained by the receiver. It is your responsibility

to unregister the observer before it becomes invalid.

*/

- (void)addObserver:(id)observer;

/*! @abstract Removes a WKHTTPCookieStoreObserver object from the cookie store.

@param observer The observer to remove.

*/

- (void)removeObserver:(id)observer;

@end

如果为了适配iOS11之前的版本,则需要native这边注册一个同步cookie的JS函数,用于h5将新的cookie同步到native这边。

cookie不会自动覆盖?

cookie会同名覆盖(保证上文说的四要素相同),但有时候后台可能下发的cookie某个要素不一致导致cookie不会自动覆盖,或者存在测试、生产域切换缓存,为了保险起见你可以每次重写cookie之前要先删除旧cookie:

//清理cookie

NSArray*oldCookies = [[NSHTTPCookieStoragesharedHTTPCookieStorage].cookiescopy];

for(NSHTTPCookie*ckinoldCookies) {

if([ck.name isEqualToString:@"capToken"]) {

[[NSHTTPCookieStoragesharedHTTPCookieStorage] deleteCookie:ck];

        }

    }

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

推荐阅读更多精彩内容