前言
在Hybrid项目不同容器间共享Cookie在UIWebview时代一直不是一个问题,但是在很多Hybrid框架迭代到WKWebview后就可以出现关于Cookie共享的一些问题,这些问题无外乎就两点:
- 原生请求(NSURLSessionDataTask、AFNetworking等和)WKWebview之间cookie的共享
- 不同WKWebview (包括webview内的ajax请求) 之间Cookie的共享
解决以上两个问题即可解决整个应用内绝大部分情况的Cookie共享问题。
产生这个问题的原因是什么?
关于Cookie的原理我不再做过多的解释,但是这是这篇文章的前提,必须在了解清楚之后才能合理控制其共享机制。
对于干iOS开发的小伙伴们大家可能对NSHTTPCookieStorage比较熟悉,NSHTTPCookieStorage是一个单例,用来管理整个项目的Cookie,包括UIWebview的Cookie也是由其管理,因此在使用UIWebview的情况下是没有任何问题的。但是WKWebview的Cookie信息并不存储在NSHTTPCookieStorage中,其由WKProcessPool管理。
多WKWebview间Cookie的共享
先来看WKWebview之间Cookie的共享问题如何解决
-
方法一
默认情况下,每一个WKWebview对象持有一个WKProcessPool对象,因此可以通过单例化WKProcessPool的方式解决WKWebview间Cookie共享的问题,但是存在的问题是WKProcessPool不会被持久化,应用被杀死后会导致Cookie丢失,对于需要长久保存的Cookie并不合适。 -
方法二
使用NSHTTPCookieStorage对象去手动存取Cookie信息并注入到WKWebview中
先来说怎么取,直接上代码:
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{
if (@available(iOS 12.0, *)) {
WKHTTPCookieStore *cookieStore = webView.configuration.websiteDataStore.httpCookieStore;
[cookieStore getAllCookies:^(NSArray* cookies) {
[self setCookie:cookies];
}];
}else {
NSHTTPURLResponse *response = (NSHTTPURLResponse *)navigationResponse.response;
NSArray *cookies =[NSHTTPCookie cookiesWithResponseHeaderFields:[response allHeaderFields] forURL:response.URL];
[self setCookie:cookies];
}
decisionHandler(WKNavigationResponsePolicyAllow);
}
- (void)setCookie:(NSArray *)cookies {
for (NSHTTPCookie *cookie in cookies) {
NSHTTPCookie *httpCookie = [self fixExpiresDateWithCookie:cookie];
[[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:httpCookie];
}
}
- (NSHTTPCookie *)fixExpiresDateWithCookie:(NSHTTPCookie *)cookie{
NSMutableDictionary *propertiesDic = [[cookie properties] mutableCopy];
if (![propertiesDic valueForKey:@"expiresDate"]) {
propertiesDic[NSHTTPCookieExpires] = [NSDate dateWithTimeIntervalSinceNow:60*60*24*7];
propertiesDic[NSHTTPCookieDiscard] = 0;
}
NSHTTPCookie *newCookie = [NSHTTPCookie cookieWithProperties:propertiesDic];
return newCookie;
}
代码比较简单,唯一需要注意的就是fixExpiresDateWithCookie这个方法的实现中修改了Cookie的过期时间NSHTTPCookieExpires与是否丢弃NSHTTPCookieDiscard字段,修改原因是因为后端不愿意改代码,喝喝,当然也可以理解,B/S架构中session-only=1这样的设置较为安全,也是常态化操作,但是对于客户端多个webview对象就很尴尬了。同时这个操作的确可能带了信息泄露的安全问题,安全风险一定需要注意。
接下来说怎么注入到WKWebview中去,有两种思路
第一种是通过WKUserScript去注入,但是注入时机会影响服务器可能拿不到Cookie。
第二种方法是在构造NSURLRequest的时候去修改请求头,也直接上代码了:
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:20.0];
NSMutableString *cookiesString = [NSMutableString string];
NSArray *tmpCookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:url];
for (NSHTTPCookie * cookie in tmpCookies) {
NSString *cookieString = [NSString stringWithFormat:@"%@=%@;",cookie.name,cookie.value];
[cookiesString appendString:cookieString];
}
[request setValue:cookiesString forHTTPHeaderField:@"Cookie"];
[self loadRequest:request];
原生请求与WKWebview之间Cookie的共享
获取和保存Cookie的方式与上面类似,再处理网络请求response的地方调用如下类似代码:
- (void)syncSwordCookies:(NSURLResponse *)response forURL:(NSURL *)url {
NSDictionary *respHeader = [(NSHTTPURLResponse *)response allHeaderFields];
NSArray *cookies = [NSHTTPCookie cookiesWithResponseHeaderFields:respHeader forURL:url];
NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
for (NSHTTPCookie *cookie in cookies) {
[cookieStorage setCookie:[self fixExpiresDateWithCookie:cookie]];
}
}
Cookie都保存在NSHTTPCookieStorage中后,原生请求自动共享Cookie,webview注入方式即与前面说的WKWebview一样的,就不用赘述了,但是唯一要注意的地方就是NSHTTPCookieStorage保存有一个耗时时间,大概零点几秒,不能在保存后立刻去同步到WKWebview中去,否则容易取不到Cookie(真坑...)
结语
WKWebview Cookie同步的坑我在全部踏完之后留下来上述结论,还有些低级或者麻烦的坑就没有一一列举,希望这篇文章可以给到大家帮助,如果有什么疑问可以留言讨论。
网友评论