浅谈用WKWebview加载网页时发送Cookie的方法

在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名称>=<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的时候回用到下面的几个类:

  1. NSHTTPCookieStorage:这个类就是一个单例,它的主要任务就是管理 Cookie,用来做增删改查等各种操作的。
  2. NSURLRequest:这个类是HTTP请求协议URL资源的消息对象Request;
  3. 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 <NSHTTPCookie *>*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 <NSHTTPCookie *>*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 = (
    "<NSHTTPCookie\n\tversion:0\n\tname:SESSION\n\tvalue:ZjIzMmE2ODMtMzY0My00MjEwLTljMmEtMGU1NzJkNWM2ZjFh\n\texpiresDate:'(null)'\n\tcreated:'2020-09-25 13:53:23 +0000'\n\tsessionOnly:TRUE\n\tdomain:service-bak.hulucc.com\n\tpartition:none\n\tsameSite:none\n\tpath:/\n\tisSecure:TRUE\n path:\"/\" isSecure:TRUE>"
)

一开始获取到的cookies是由NSHTTPCookie类的对象(cookie)所组成的数组,所以叫cookies。

2020-09-26 02:05:24.872257+0800 葫芦[20198:281532] tempCookie = <NSHTTPCookie
    version:0
    name:SESSION
    value:ZjIzMmE2ODMtMzY0My00MjEwLTljMmEtMGU1NzJkNWM2ZjFh
    expiresDate:'(null)'
    created:'2020-09-25 13:53:23 +0000'
    sessionOnly:TRUE
    domain:service-bak.hulucc.com
    partition:none
    sameSite:none
    path:/
    isSecure:TRUE
 path:"/" isSecure:TRUE>

这个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<NSHTTPCookie *> *cookies2 = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];
#pragma mark -------------- GGWkWebViewDelegate --------------
- (NSDictionary *)webviewSetAppCookieKeyAndValue
{
    NSArray<NSHTTPCookie *> *cookies2 = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];
    for (NSHTTPCookie *c in cookies2)
    {
        NSLog(@"$$$$$$$$$$$$$c = %@", c);
    }
    
//    NSString *cookieString = [EHLCookieUtils getCookieString];
//    NSLog(@"cookieString = %@", cookieString);
    
    //从 NSHTTPCookieStorage 获取想要Cookie,此种获取方式是获取的cookies中的
    NSArray <NSHTTPCookie *>*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 = <NSHTTPCookie
    version:0
    name:SESSION
    value:ZjIzMmE2ODMtMzY0My00MjEwLTljMmEtMGU1NzJkNWM2ZjFh
    expiresDate:'(null)'
    created:'2020-09-25 13:53:23 +0000'
    sessionOnly:TRUE
    domain:service-bak.hulucc.com
    partition:none
    sameSite:none
    path:/
    isSecure:TRUE
 path:"/" isSecure:TRUE>
2020-09-26 02:29:17.187079+0800 葫芦[20696:306124] $$$$$$$$$$$$$c = <NSHTTPCookie
    version:0
    name:JSESSIONID
    value:149591F5BF45FD9689768C475437BD49
    expiresDate:'(null)'
    created:'2020-09-25 18:29:06 +0000'
    sessionOnly:TRUE
    domain:service-bak.hulucc.com
    partition:none
    sameSite:none
    path:/calabash
    isSecure:FALSE
    isHTTPOnly: YES
 path:"/calabash" isSecure:FALSE isHTTPOnly: YES>
2020-09-26 02:29:18.012728+0800 葫芦[20696:306124] $$$$$$$$$$$$$c = <NSHTTPCookie
    version:0
    name:SESSION
    value:ZjVhYzUyZWYtZGQ1NS00NDVhLTgyMDUtOGVkZGM2ZjZmOWYw
    expiresDate:'(null)'
    created:'2020-09-25 13:53:23 +0000'
    sessionOnly:TRUE
    domain:service-bak.hulucc.com
    partition:none
    sameSite:lax
    path:/gateway
    isSecure:FALSE
    isHTTPOnly: YES
 path:"/gateway" isSecure:FALSE isHTTPOnly: YES>

其实真正有效的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<NSHTTPCookie *> *cookies2 = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];
//    for (NSHTTPCookie *c in cookies2)
//    {
//        NSLog(@"$$$$$$$$$$$$$c = %@", c);
//    }
    
    NSString *cookieString = [EHLCookieUtils getCookieString];
    NSLog(@"cookieString : %@", cookieString);
    
    
    //从 NSHTTPCookieStorage 获取想要Cookie,此种获取方式是获取的cookies中的
    NSArray <NSHTTPCookie *>*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

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