WKWebView Cookie 设置

从UIWebview换到WKWebView之后,会发现管理Cookie是很麻烦事,经常出现 App自定义Cookie的值丢失 或 更新不及时 的情况。苹果iOS11之后也提供了WKWebView的Cookie API WKHTTPCookieStore,但是目前大多数App最低版本不可能设置最低版本到iOS11,所以我们只能想别的办法。

什么是Cookie?

Cookie是一种早期的客户端存储机制,起初是针对服务端脚本设计使用的。以Cookie存储的数据,不论服务端是否需要,每次HTTP请求都会把这些数据传送到服务端。

Cookie是指Web浏览器存储的少量数据,同时它是与具体的Web页面或者站点相关。Cookie最早是设置为被服务端所用的,从最底层来看,作为http协议的一种拓展实现它。

Cookie数据会自动在Web浏览器和Web服务器之间传输,因此服务端脚本就可以读写存储在客户端的cookie值。

Cookie的属性

除了 name 与 value,Cookie其他属性:

domain:作用域名

举例:
设置domain为 bj.jiehun.com.cn,则在app.jiehun.com.cn 等子域名不会生效;
如设置domain为 .jiehun.com.cn,则在jiehun.com.cn 的所有子域名生效。
注意:IP地址特殊,直接设置原地址即可。

path:作用路径

举例:
如在 www.jiehun.com.cn,设置 path=/m,则只在 www.jiehun.com.cn/m 或 /m子目录下生效,如 www.jiehun.com.cn/app 就不会生效;
不设置默认为当前URL默认路径;我们通常会设置 “/” 代表所有目录。

expires:过期时间,格林威治时间 (GMT)字符串格式,设置过期时间后将会存储在一个文件直到过期。

// 打印当前时间
var date = new Date();
date.setTime(date.getTime());
console.log(date.toGMTString());
// 打印结果为:Mon, 25 Feb 2019 15:35:03 GMT

// 设置cookie时间
document.cookie='name=value;expires=' + date.toGMTString();

max-age:有效时长,单位秒。

// 设置 cookie 有效期 10秒
document.cookie='name=value;max-age=10;

Cookie默认的有效期很短暂,只能维持在Web浏览器的回话期间,一旦用户关闭浏览器,Cookie保存的数据就全部丢失。
如果想要延长Cookie的有效期,可以通过设置max-age或expires属性。一旦设置有效期,浏览器就会将Cookie数据存储在一个文件中,并且直到过了指定的有效期才会删除该文件。

secure: Bool值,是否为安全传输,如设置true,在Https或其他网络安全协议才会传输。

演示

我们可以随便找一个浏览器,如Safari,打开后快捷键 command + alt + c 打开调试窗口:


image.png

读取Cookie

document.cookie

保存Cookie

document.cookie='testName=testValue;path=/;max-age=60*60';

因为Cookie的名和值中不允许包含分号、逗号和空格,因此,在存储前可以采用全局函数 encodeURIComponent()对值进行编码。相应的,读取cookie值的时候采用decodeURIComponent() 函数解码。

删除Cookie

相同的名字随意设置一个值,并将max-age指定为0,相当于删除Cookie。

document.cookie='testName=;domain=.jiehun.com.cn;path=/;max-age=0';

Cookie的局限性

个数限制300(现在已经可以超越),每个Cookie数据即名字和值的总量不能超过4KB。

WKWebView 管理Cookie

上面了解了Cookie的基本知识,我们如何应用到iOS开发中?这里主要介绍WKWebview的使用方法。

因为JavaScript只能设置浏览器本地的cookie,往往客户端第一次请求由开发者创建Request对象请求:

[webview loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"url"]]];

所以第一次请求需要手动设置请求头中的Cookie,可以继承WKWebView重写 loadRequest 方法,或者直接通过runtime Swizzling loadRequest 方法:

/**重写 loadRequest 方法,在请求头中添加自定义到cookie*/
- (nullable WKNavigation *)loadRequest:(NSURLRequest *)request{
    
    NSMutableURLRequest *newRequeset = [request mutableCopy];
    
    // 自定义cookie值
    NSDictionary *customCookieDic =@{
                                     @"testName1":@"value1",
                                     @"testName2":@"value2"
                                     };
    
    // 拼接cookie字符
    NSString *cookie = @"";
    for (NSString *key in customCookieDic.allKeys) {
        NSString *keyValue = [NSString stringWithFormat:@"%@=%@;",key,[customCookieDic objectForKey:key]];
        cookie = [cookie stringByAppendingString:keyValue];
    }
    
    // 设置到请求头
    [newRequeset setValue:cookie forHTTPHeaderField:@"Cookie"];
    
    return [super loadRequest:newRequeset];
}

此时请求发出,服务端可以收到我们请求头中的cookie值,且请求头的cookie会自动保存到浏览器。

但我们发现请求头中自动保存到浏览器的cookie并不可靠,因为没有设置域名和目录的作用区,往往浏览器内二次跳转就会丢失,我们如何来保证浏览器的Cookie永不丢失?

WKUserScript

Webkit支持通过 WKUserScript 向网页中注入js脚本:

// 设置代码块
// @Source 脚本代码
// @injectionTime 执行时机,网页渲染前或渲染后
// @MainFrameOnly Bool值,YES只注入主帧,NO所有帧
WKUserScript *cookieInScript = [[WKUserScript alloc] initWithSource:@"js脚本代码"
                                                              injectionTime:WKUserScriptInjectionTimeAtDocumentStart
                                                           forMainFrameOnly:NO];
// 插入脚本
[webview.configuration.userContentController addUserScript:cookieInScript];

所以,保证Cookie不丢失可以通过此方法注入设置cookie js代码,在页面每次渲染都会设置,从而保证不会丢失。

每个cookie值可以设置为一个代码块,为了表示每个代码块的唯一,我们可以在注入脚本的最后添加 注释标示;其实就是一句注释掉的代码,来确定cookie代码块的唯一性,为以后删除某个代码块做铺垫。
举例设置某个cookie值的 js代码:

document.cookie ='cityId=10010;domain=.jiehun.com.cn;path=/';
// The cookie code  identified is cityId (代码块标示)

为了避免Cookie重复问题,可以直接把cookie设置在根域名。

删除代码块

每次调用 addUserScript方法插入脚本块,代码块会保存在WKUserContentController类的 userScripts数组属性中:

@interface WKUserContentController : NSObject <NSSecureCoding>

/*! @abstract The user scripts associated with this user content
 controller.
*/
@property (nonatomic, readonly, copy) NSArray<WKUserScript *> *userScripts;

因为 userScripts 为只读权限,我们并不能修改,所以修改某个某一个cookie代码块的时候,需要先全部清除,再把不需要删除的代码块重新添加进去,这里就需要用到前面所说的代码块注释的唯一标示:

/**
 删除某个代码片段
 @param tag 片段标示,//The cookie code  identified is cookieName
 */
- (void)deleteUserSciptWithTag:(NSString *)tag {
    
    if (tag) {
        WKUserContentController *userContentController = webView.configuration.userContentController;
        NSMutableArray<WKUserScript *> *array = [userContentController.userScripts mutableCopy];
        int i = 0;
        BOOL isHave = NO;
        for (WKUserScript* wkUScript in userContentController.userScripts) {
            // 通过注释标示判断代码块是否为某个cookie
            if ([wkUScript.source containsString:tag]) {
                [array removeObjectAtIndex:i];
                isHave = YES;
                continue;
            }
            i ++;
        }
        
        if (isHave) {
            ///没法修改数组 只能移除全部 再将不需要删除的cookie重新添加
            [userContentController removeAllUserScripts];
            for (WKUserScript* wkUScript in array) {
                [userContentController addUserScript:wkUScript];
            }
        }
    }
}

通过这个Webkit可注入脚本的逻辑,我们基本可以保证使用cookie在WKWebview准确性和不丢失。

下面也写了一个Demo来演示如何通过一个代理方法来管理WKWebView所有Cookie:
github地址:https://github.com/GaoGuohao/GGWkCookie

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

推荐阅读更多精彩内容