40- WKWebView项目实践分享(五)- WKWebView如何加Cookie

字数 1414阅读 500
强烈建议如果你把下边的参考文章也快速看下,作为拓展和补充。

Cookie对webView的重要性不言而喻,也就是Cookie很大成都的影响我们是否使用WKWebView。iOS8.0-iOS11.0的时候,苹果并没有向WKWebView提类似UIWebView+NSHttpCookieStorage这样便利的方案。
关于为什么在长达三年的时间里,苹果没有理会开发者的抱怨推出客户端管理Cookie的类。CSDN作者请叫我马老师的一篇文章中,提出了猜想,苹果是想让服务端完全控制Cookie,毕竟客户端操作Cookie是有一定安全风险的。WKWebView设计出来,对安全性就有比较高的要求,比如说对跨域的限制。
那么客户端不传Cookie了,服务端怎么确认用户的身份信息呢?其中的一种方式,我们可以在首次请求的时候,将用户信息身份信息通过POST请求的方式放到requst中。服务端接收到首次请求,判断用户身份信息,如果身份信息有效,则在之后的请求中,有服务器主动设置Cookie保证后续的请求身份。如果身份信息无效,则跳转到登录页面。 这种方式Cookie就完全交给服务器管理了。

但是,实际开发中,我们还是免不了客户端设置Cookie。

WKWebView的Cookie方案。

在UIWebView中,可以通过NSHttpCookieStorage来管理Cookie。
在UIWebView中,如果添加Cookie到NSHttpCookieStorage中。这个Cookie在发起请求的时候自动添加到request Header中。并且是零延迟的,也就是说,你第一行代码添加Cookie到NSHttpCookieStorage中,第二行代码loadRequest:,发起的请求中是带有你设置的Cookie的。
关于零延迟这一点非常重要,因为你设置了Cookie,但是请求的时候,带不到服务器,这不是白设置嘛。

NSHttpCookieStorage对WKWebView而言,只能管理Cookie,但是没有了零延迟自动同步到request Header这一关键的特性。所以我们只能另找出路,下边需要版本适配,也是因为这个原因

截至置顶的iOS版本,需要适配iOS的三个版本,分别是iOS8iOS11.0、iOS11.0iOS11.2、iOS11.3。 下边看具体的方案:

Cookie版本适配方案

下边就针对每个适配版本来说一下具体操作方案。

适配iOS8~iOS11.0

首先很明确的说明下,这这里需要关注Cookie设置的地方有两处。
第一处是我们每次调用loadRequest:方法的时候。
另一处就是loadRequest:成功之后,在这个webView的进行操作,后续页面跳转的时候。

关于Cookie管理,我们还是使用NSHttpCookieStorage。

在每次调用loadRequest:方法的时候

我们把Cookie手动写入到request Header中。具体代码和步骤如下:

// 1. 清除NSHTTPCookieStorage中存储的Cookies,避免重复Cookie
NSHTTPCookieStorage *cookieJar = [NSHTTPCookieStorage sharedHTTPCookieStorage];
for (NSHTTPCookie *cookie in cookieJar.cookies) {
    if ([cookie.comment isEqualToString:WebCookieComment]) {
        [cookieJar deleteCookie:cookie];
    }
}

// 2. 重新设置Cookies
[[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie];
[[NSUserDefaults standardUserDefaults] synchronize];

// 3. 向Http Header中设置Cookie    
 NSDictionary *dict = [NSHTTPCookie requestHeaderFieldsWithCookies:[NSHTTPCookieStorage sharedHTTPCookieStorage].cookies];
 request.allHTTPHeaderFields = dict.copy;

// 4. 加载请求
[self.wkwebView loadRequest: request];

后续页面跳转

后续跳转,我们把Cookie添加到document.Cookie的方案。

//1. 添加自定义的cookie,后续请求Ajax页面用到
WKUserScript *newCookieScript = [[WKUserScript alloc] initWithSource:[WebViewCookieTool cookieJavaScriptString] injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO];
[contentController addUserScript:newCookieScript];
webConfiguration.userContentController = contentController;


//2. 获取Cookie的JS代码
+ (NSString *)cookieJavaScriptString
    NSMutableString *script = [NSMutableString string];
    for (NSHTTPCookie *cookie in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]) {
    if ([cookie.value rangeOfString:@"'"].location != NSNotFound) {
         continue;
    }
    [script appendFormat:@"document.cookie='%@'; \n", [self getCookieJSFromatString:cookie]];
}
    
    
+ (NSString *)getCookieJSFromatString:(NSHTTPCookie *)cookie
{
    NSString *cookieStr = [NSString stringWithFormat:@"%@=%@;domain=%@;path=%@",
                           cookie.name,
                           cookie.value,
                           cookie.domain,
                           cookie.path ?: @“/"];
    return cookieStr;
}

这种方案出现的问题

这种方案基本解决了适配版本中的Cookie问题,但是在实测中,发现将Cookie添加到request Header中会导致另一个问题出现。那就是服务器如果设置一些Cookie到header中,是不能正常设置的,导致网页无法正常显示。又是一个坑。针对这个问题,想到了两个解决方案:

解决服务器无法在Header中正常设置Cookie

所以,我项目里有一个区分是不是本公司请求的静态方法:

static inline BOOL checkURLIsNeedCookies(NSURL *url) {
    if ([url.host hasSuffix:@".myCompany.com") {
        return YES;
    }
    return NO;
}

适配iOS11.0~iOS11.2

iOS11.0之后的适配就爽啦,可能苹果了解到开发者的实际情况并且做出了妥协。在iOS11.0推出了WKHTTPCookieStore,这个类对WKWebView的作用和NSHttpCookieStorage对UIWebView的效果一样,并且也是零延迟。所以,只需要在你请求的时候,将Cookie设置到WKHTTPCookieStore就可以了。代码:

// 1. 设置Cookie
WKWebsiteDataStore *store = [WKWebsiteDataStore defaultDataStore];
[store.httpCookieStore setCookie:cookie completionHandler:^{
NSLog(@“cookie添加成功”);
}];

// 2. 加载请求
[self.wkwebView loadRequest: request];

适配iOS11.3

前几天升级iOS11.3之后发现需要Cookie的页面,第一次设置的时候,WKHTTPCookieStore中Cookie不同步到request请求中。然后,重新请求页面,WKHTTPCookieStore的作用才会恢复正常,有序请求其他的webView也不会出现问题。

解决方式是,我们在执行loadRequest之前主动设置一次Cookie。我这里是在didFinishLaunchOption:中设置的。设置完之后,其它的就和在iOS11.0~iOS11.2中一样了。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 
{
   WKWebsiteDataStore *store = [WKWebsiteDataStore defaultDataStore];
    [store.httpCookieStore setCookie:cookie completionHandler:^{
        NSLog(@“cookie添加成功”);
    }];
}

好了,关于WKWebView的Cookie设置就到此为止。

共享Web Content Process

我们之前说过,一个WKWebView创建出来之后,对应一个Web Content Process。这样就保证每个WKWebView的缓存、Cookie等信息是隔离的。但是这样会导致一个问题就是,当网页中出现a标签和target=_blank的时候,会好像浏览器那样新开一个tab页面,此时新开的webView不共享不到之前webView的Cookie的,导致新开的webView不能正常显示。 处理的方式是,设置WKWebView对应的processPool是唯一的。如下两种方式:

 1’:   
self.processPool = [[WKProcessPool alloc] init];
WKWebViewConfiguration *configuration1 = [[WKWebViewConfiguration alloc] init];
configuration1.processPool = self.processPool;
WKWebView *webView = [[WKWebView alloc] initWithFrame:CGRectZero configuration:configuration1];


2’:
- (WKProcessPool *)wkProcessPool
{
    static dispatch_once_t once;
    static WKProcessPool * __singleton__;
    dispatch_once( &once, ^{
        __singleton__ = [[WKProcessPool alloc] init];
        
    });
    return __singleton__;
}

 webConfiguration.processPool = self.wkProcessPool; 
 WKWebView *webView = [[WKWebView alloc] initWithFrame:CGRectZero configuration:configuration1];

参考

交流


希望能和大家交流技术
Blog:http://www.lilongcnc.cc


推荐阅读更多精彩内容