在iOS8之后苹果推出了WKWebView控件,用来显示网页,以逐渐淘汰之前的UIWebView控件。虽然在加载速度和内存使用等方面WKWebView远远胜过了UIWebView,但是WKWebView在加载网页的时候,给网页发送Cookie的时候总容易丢失,就会造成网页无法拿到移动端发送过去的Cookie,从而会引发bug。经过多种途径的实验,最终采用GGWkCookie第三方来发送Cookie。
Cookie是什么?
Cookie是由服务器保存在客户端上的一块数据。它包含着相关用户的信息,比如果用户的登陆状态、用户标识等等。
Cookie有什么作用?
cookie的作用主要体现在以下的三个方面:
1.会话状态管理(如用户登录状态、购物车);
2.个性化设置(如用户自定义设置);
3.浏览器行为跟踪(如跟踪分析用户行为)。
Cookie的处理步骤:
1.服务器向客户端发送Cookie;
2.通常使用HTTP协议规定的Set-Cookie头操作;
3.规范规定Cookie的格式为 name = value 格式,且必须包含这部分;
4.客户端将Cookie保存;
5.每次请求客户端都会将Cookie发送给服务器。
Cookie长什么样子?
当服务器收到HTTP请求时,可以在响应头里面增加一个Set-Cookie头部。客户端收到响应之后会取出Cookie信息并保存,之后对该服务器每一次请求中都通过Cookie请求头部将Cookie信息发送给服务器。大概长的都是这个格式:
Set-Cookie: =
所以一个简单的Cookie就如下所示:
language=zh_CN; expires=Sat, 05-Aug-2017 08:21:16 GMT; Max-Age=2592000; path=/; domain=192.75.17.211:6603
当在设置Cookie的时候回用到下面的几个类:
- NSHTTPCookieStorage:这个类就是一个单例,它的主要任务就是管理 Cookie,用来做增删改查等各种操作的。
- NSURLRequest:这个类是HTTP请求协议URL资源的消息对象Request;
- NSHTTPURLResponse:这个类是HTTP协议请求URL资源的响应消息对象。这个对象将HTTP协议的序列化了,可以很方便的获得状态码(statusCode),消息报头(allHeaderFields)等信息。
在代码中可以使用以下的两种方式来获取Cookie:
1.从NSHTTPURLResponse获取服务器发给我们的Cookie。这种方式获取的是Headers中的Cookie。
NSHTTPURLResponse* response = (NSHTTPURLResponse* )task.response;
NSDictionary *allHeaderFieldsDic = response.allHeaderFields;
NSString *setCookie = allHeaderFieldsDic[@"Set-Cookie"];
if (setCookie != nil)
{
NSString *cookie = [[setCookie componentsSeparatedByString:@";"] objectAtIndex:0];
NSLog(@"cookie : %@", cookie); // 这里可对cookie进行存储
}
2.从 NSHTTPCookieStorage 获取想要的Cookie。此种获取方式是获取的cookies中的。
NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:[NSURL URLWithString:self.webUrlStr]];
NSLog(@"cookies = %@", cookies);
for (NSHTTPCookie *tempCookie in cookies)
{
NSLog(@"tempCookie = %@", tempCookie);
NSLog(@"cookieName = %@, cookieValue = %@", tempCookie.name, tempCookie.value);
}
清除Cookie:
NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
NSArray *_tmpArray = [NSArray arrayWithArray:[cookieStorage cookies]];
for (id obj in _tmpArray)
{
[cookieStorage deleteCookie:obj];
}
在自己的项目中利用WKWebView来加载网页,需要在加载的时候向网页传递Cookie,以便网页端拿到Cookie之后做一些其他的操作,所以就用了如下的两种方式向网页传递Cookie:
1.直接设置方式:
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:self.webUrlStr]];
//获取Cookie
NSString *cookieString = [EHLCookieUtils getCookieString];
NSLog(@"cookieString = %@", cookieString);
[request addValue:cookieString forHTTPHeaderField:@"Cookie"];
[self.webView loadRequest:request];
利用Charles截包工具在加载网页的时候查看,没有将Cookie发送出去,猜测可能是在发送的时候丢失了Cookie。
2.JS注入方式:
//获取Cookie
NSString *cookieString = [EHLCookieUtils getCookieString];
NSLog(@"cookieString = %@", cookieString);
NSString *cookieStr = [NSString stringWithFormat:@"document.cookie ='%@';",cookieString];
NSLog(@"cookieStr = %@", cookieStr);
WKUserScript *cookieScript = [[WKUserScript alloc] initWithSource: cookieStr injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO];
[_userContentController addUserScript:cookieScript];
在利用Charles截包工具在加载网页的时候查看,没有将Cookie发送出去,猜测可能是在发送的时候丢失了Cookie。
在上述方式都失败的情况下选择使用GGWkCookie第三方来发送Cookie,经实验发送成功。
WKWebview支持的插入脚本的方式,在每次页面渲染前,通过插入的Js脚本检测Cookie是否存在,如不存在,将Cookie重新种入的思路。
GGWkCookie github地址:https://github.com/GaoGuohao/GGWkCookie
在GGWkCookie的代理方法中撰写如下的代码:
#pragma mark -------------- GGWkWebViewDelegate --------------
- (NSDictionary *)webviewSetAppCookieKeyAndValue
{
//从 NSHTTPCookieStorage 获取想要的Cookie,此种获取方式是获取的cookies中的
NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:[NSURL URLWithString:self.webUrlStr]];
NSLog(@"cookies = %@", cookies);
NSHTTPCookie *cookie = nil;
for (NSHTTPCookie *tempCookie in cookies)
{
NSLog(@"tempCookie = %@", tempCookie);
NSLog(@"cookieName = %@, cookieValue = %@", tempCookie.name, tempCookie.value);
cookie = tempCookie;
}
NSDictionary *requestDic = [NSHTTPCookie requestHeaderFieldsWithCookies:cookies];
NSLog(@"requestDic = %@", requestDic);
NSString *cookie1 = [requestDic objectForKey:@"Cookie"];
NSLog(@"cookie1 = %@", cookie1);
NSDictionary *cookieDic = [NSDictionary dictionaryWithObject:cookie.value forKey:cookie.name];
NSLog(@"cookieDic = %@", cookieDic);
return cookieDic;
}
在本人的项目中这个代理方法会被调用两次。根据GGWkCookie的原理,在每次页面渲染前,通过插入的Js脚本检测Cookie是否存在,如不存在,则将cookie重新种入。第一次的时候没有种入成功,所以就再次进行了调用,再次种入,这就是调用两次的原因。
再看运行结果:
2020-09-26 02:05:24.872077+0800 葫芦[20198:281532] cookies = (
""
)
一开始获取到的cookies是由NSHTTPCookie类的对象(cookie)所组成的数组,所以叫cookies。
2020-09-26 02:05:24.872257+0800 葫芦[20198:281532] tempCookie =
这个cookies数组里面只有一个元素,即:NSHTTPCookie类型的对象(cookie),把这个cookie对象打印出来可以知道,这个NSHTTPCookie类有好多种属性,其中最重要的是name属性和value属性。
2020-09-26 02:05:24.872343+0800 葫芦[20198:281532] cookieName = SESSION, cookieValue = ZjIzMmE2ODMtMzY0My00MjEwLTljMmEtMGU1NzJkNWM2ZjFh
2020-09-26 02:05:24.872477+0800 葫芦[20198:281532] requestDic = {
Cookie = "SESSION=ZjIzMmE2ODMtMzY0My00MjEwLTljMmEtMGU1NzJkNWM2ZjFh";
}
2020-09-26 02:05:24.872558+0800 葫芦[20198:281532] cookie1 = SESSION=ZjIzMmE2ODMtMzY0My00MjEwLTljMmEtMGU1NzJkNWM2ZjFh
最后在这个代理方法中要返回的字典对象为:
2020-09-26 02:05:24.872729+0800 葫芦[20198:281532] cookieDic = {
SESSION = ZjIzMmE2ODMtMzY0My00MjEwLTljMmEtMGU1NzJkNWM2ZjFh;
}
整个过程结束,网页可以直接拿到客户端发过来的Cookie,然后进行接下来的操作。
迷思释惑
1.在GGWkCookie的代理方法中用如下的方式获取Cookie:
NSArray *cookies2 = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];
#pragma mark -------------- GGWkWebViewDelegate --------------
- (NSDictionary *)webviewSetAppCookieKeyAndValue
{
NSArray *cookies2 = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];
for (NSHTTPCookie *c in cookies2)
{
NSLog(@"$$$$$$$$$$$$$c = %@", c);
}
// NSString *cookieString = [EHLCookieUtils getCookieString];
// NSLog(@"cookieString = %@", cookieString);
//从 NSHTTPCookieStorage 获取想要Cookie,此种获取方式是获取的cookies中的
NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:[NSURL URLWithString:self.webUrlStr]];
NSLog(@"cookies = %@", cookies);
NSHTTPCookie *cookie = nil;
for (NSHTTPCookie *tempCookie in cookies)
{
NSLog(@"tempCookie = %@", tempCookie);
NSLog(@"cookieName = %@, cookieValue = %@", tempCookie.name, tempCookie.value);
cookie = tempCookie;
}
NSDictionary *requestDic = [NSHTTPCookie requestHeaderFieldsWithCookies:cookies];
NSLog(@"requestDic = %@", requestDic);
NSString *cookie1 = [requestDic objectForKey:@"Cookie"];
NSLog(@"cookie1 = %@", cookie1);
NSDictionary *cookieDic = [NSDictionary dictionaryWithObject:cookie.value forKey:cookie.name];
NSLog(@"cookieDic = %@", cookieDic);
return cookieDic;
}
逐个打印用上述方式获取到的Cookies数组里面的元素可以知道,一共有三个NSHTTPCookie的对象,打印结果为:
2020-09-26 02:29:16.344962+0800 葫芦[20696:306124] $$$$$$$$$$$$$c =
2020-09-26 02:29:17.187079+0800 葫芦[20696:306124] $$$$$$$$$$$$$c =
2020-09-26 02:29:18.012728+0800 葫芦[20696:306124] $$$$$$$$$$$$$c =
其实真正有效的Cookie是第一个。因为每个NSHTTPCookie对象中都有一个isSecure属性,当这个属性为TRUE的时候,则意味着此 cookie 在HTTP中是无效的,而在HTTPS中才有效。当这个属性为FALSE的时候,则意味着此Cookie不管在HTTP还是在HTTPS中都是无效的。所以第二个和第三个Cookie是无效的。
2.在GGWkCookie的代理方法中用如下的方式获取Cookie:
NSString *cookieString = [EHLCookieUtils getCookieString];
NSLog(@"cookieString : %@", cookieString);
上面的getCookieString方法是本项目中其他人写的一个获取Cookies的方法。
#pragma mark -------------- GGWkWebViewDelegate --------------
- (NSDictionary *)webviewSetAppCookieKeyAndValue
{
// NSArray *cookies2 = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];
// for (NSHTTPCookie *c in cookies2)
// {
// NSLog(@"$$$$$$$$$$$$$c = %@", c);
// }
NSString *cookieString = [EHLCookieUtils getCookieString];
NSLog(@"cookieString : %@", cookieString);
//从 NSHTTPCookieStorage 获取想要Cookie,此种获取方式是获取的cookies中的
NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:[NSURL URLWithString:self.webUrlStr]];
NSLog(@"cookies = %@", cookies);
NSHTTPCookie *cookie = nil;
for (NSHTTPCookie *tempCookie in cookies)
{
NSLog(@"tempCookie = %@", tempCookie);
NSLog(@"cookieName = %@, cookieValue = %@", tempCookie.name, tempCookie.value);
cookie = tempCookie;
}
NSDictionary *requestDic = [NSHTTPCookie requestHeaderFieldsWithCookies:cookies];
NSLog(@"requestDic = %@", requestDic);
NSString *cookie1 = [requestDic objectForKey:@"Cookie"];
NSLog(@"cookie1 = %@", cookie1);
NSDictionary *cookieDic = [NSDictionary dictionaryWithObject:cookie.value forKey:cookie.name];
NSLog(@"cookieDic = %@", cookieDic);
return cookieDic;
}
打印结果为:
2020-09-26 02:50:56.894266+0800 葫芦[21040:325891] cookieString : SESSION=ZjIzMmE2ODMtMzY0My00MjEwLTljMmEtMGU1NzJkNWM2ZjFh; SESSION=ZjVhYzUyZWYtZGQ1NS00NDVhLTgyMDUtOGVkZGM2ZjZmOWYw
从而可以看出来,用getCookieString方法获取到了两个Cookie,其中有一个也是无效的Cookie。
上述就是本人对WKWebView传递Cookie的心得,希望能够帮助到大家。
参考文献:https://www.jianshu.com/p/f61834025588