WKWebview共享Cookie

前言

在Hybrid项目不同容器间共享Cookie在UIWebview时代一直不是一个问题,但是在很多Hybrid框架迭代到WKWebview后就可以出现关于Cookie共享的一些问题,这些问题无外乎就两点:

  1. 原生请求(NSURLSessionDataTask、AFNetworking等和)WKWebview之间cookie的共享
  2. 不同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同步的坑我在全部踏完之后留下来上述结论,还有些低级或者麻烦的坑就没有一一列举,希望这篇文章可以给到大家帮助,如果有什么疑问可以留言讨论。

你可能感兴趣的:(WKWebview共享Cookie)