WKWebView和UIWebView的cookie的session问题

前言

首先这篇文章所描述的仅仅只是cookie中的Session字段的问题。

我理解的Session 对象是存储特定的用户会话所需的配置信息。当用户在Web页面跳转的时候,Session不会丢失,会一直存在下去。用户请求到Web页面时,如果用户还没有会话,则服务器将自动创建一个,它将被存储于cookie中。

我开发中所遇到的问题是,某些H5业务需要验证登录后才可以使用,并且还要带一个特殊的cookie字段表示来源。这就要求Session在WebView中可以一直保持着,否则没有Session,服务器每次都需要在验证一次用户信息。

WKWebView

最开始我使用WKWebView,毕竟效率高了很多,但是在cookie这块确实是很坑。

Cookie并不会再加载后自动保存到NSHTTPCookieStorage中,也就是说,发起请求的时候不会自动带上存储于NSHTTPCookieStorage容器中的Cookie,内存根本不用UIWebView那套了。可能是有自己的私有存储吧(不太清楚)。

这里看到,可以使用同一个的WKProcessPool实例来使不同的WKWebView拥有相同的Cookie。

/**
构建一个单例用来保存WKProcessPool
*/
+ (instancetype)shareInstance{
    static WKProcesspoolManager *instance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[WKProcesspoolManager alloc] init];
    });
    return instance;
}

- (instancetype)init {
    self.wkProcessPool = [[WKProcessPool alloc] init];
}

我经过这样尝试后发现,还是不能解决不同WKWebView中session保持的问题,于是我就想看看在WKWebView中的Cookie到底保存了什么字段。

- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{
    
    NSHTTPURLResponse * response = (NSHTTPURLResponse *)navigationResponse.response;
    NSArray *cookies = [NSHTTPCookie cookiesWithResponseHeaderFields:response.allHeaderFields forURL:[NSURL URLWithString:_urlStr]];
    for (NSHTTPCookie *cookie in cookies) {
        NSLog(@"cookie = %@",cookie);
    }
    decisionHandler(WKNavigationResponsePolicyAllow);
}

拿到Cookie后发现,里面竟然只能获取到我自己在请求时设置的字段。其他的根本就没有。而Sessionid这个Cookie 可能是被服务器设置了HttpOnly属性了,WKWebView将它保护起来。所以想要修改它,JS是不可能的。只能通过WKWebView的接口入手了,可是我并没有发现这样的接口。所以我只能换回UIWebView了。

UIWebView

换回来之后,其实一切都很简单了。
UIWebView中的请求之后Cookie都保存在NSHTTPCookieStorage中了,并且sessionid也存在。

- (void)webViewDidFinishLoad:(UIWebView *)webView {
    NSHTTPCookieStorage *myCookie = [NSHTTPCookieStorage sharedHTTPCookieStorage];
    for (NSHTTPCookie *cookie in [myCookie cookies]) {
        NSLog(@"%@", cookie);
        [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie];
    }
}

这样可以将完整的Cookie保存下来,以便自动登录。接下来就是将保存起来的cookis设置在request中就可以了

NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:[NSURL
URLWithString:HOST]]; // HOST就是你web服务器的域名地址
  // 设置header
for (NSHTTPCookie *cookie in cookies){
      // cookiesWithResponseHeaderFields方法,需要为URL设置一个cookie为NSDictionary类型的header,注意NSDictionary里面的forKey需要是@"Set-Cookie"
     NSString *str = [[NSString alloc] initWithFormat:@"%@=%@",[cookie name],[cookie value]];
     NSDictionary *dic = [NSDictionary dictionaryWithObject:str forKey:@"Set-Cookie"];
     NSArray *headeringCookie = [NSHTTPCookie cookiesWithResponseHeaderFields:dic forURL:[NSURL URLWithString:HOST]];
     // 设置Cookie,只要访问URL为HOST的网页时,会自动附带上设置的header
     [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookies:headeringCookie
                                                        forURL:[NSURL URLWithString:HOST]
                                               mainDocumentURL:nil];
 }

这样就可以在不同的UIWebView中共享同一个sessionid了。

最后

在换回UIWebView后,我测试了一下。在第一个页面设置sessionid
UIWebView写入sessionid

然后第二个页面读取sessionid,是可以读取到的。

UIWebView读取sessionid

然后我又使用了WKWebView测试发现,还是获取不到。

WKWebView写入sessionid
WKWebView读取sessionid

我又想到微信的WebView内核好像全是WKWebView了,于是我在微信中打开测试了一下,发现竟然可以获取到到sessionid。

微信中打开写入sessionid
微信中打开读取sessionid

也就是说微信做到了。果然还是有方法的。只是自己水平不足罢了。希望有大牛能提点我一下,也希望这篇文章给予有需要的人一点帮助。

2017-09-05 更新

-----------------------------------------------------------------------------------------------

上个月,全面使用了WKWebView了,也解决sessionid的问题。

具体解决过程如下:

  • 首先在登录的时候,在网络请求结束的地方用一个单例来保存这次cookie。大概代码:
        WKCookieSyncManager *cookiesManager = [WKCookieSyncManager sharedWKCookieSyncManager];
        [cookiesManager setCookie];

@interface WKCookieSyncManager () <WKNavigationDelegate>

@property (nonatomic, strong) WKWebView *webView;

///用来测试的url这个url是不存在的
@property (nonatomic, strong) NSURL *testUrl;


@end

@implementation WKCookieSyncManager

+ (instancetype)sharedWKCookieSyncManager {
    static WKCookieSyncManager *sharedWKCookieSyncManagerInstance = nil;
    static dispatch_once_t predicate;
    dispatch_once(&predicate, ^{
        sharedWKCookieSyncManagerInstance = [[self alloc] init];
    });
    return sharedWKCookieSyncManagerInstance;
}

- (void)setCookie {
    //判断系统是否支持wkWebView
    Class wkWebView = NSClassFromString(@"WKWebView");
    if (!wkWebView) {
        return;
    }
    WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
    config.processPool = self.processPool;
    self.webView = [[WKWebView alloc] initWithFrame:CGRectZero configuration:config];
    NSURLRequest *request = [[NSURLRequest alloc] initWithURL:self.testUrl];
    self.webView.navigationDelegate = self;
    [self.webView loadRequest:request];
}

#pragma - get
- (WKProcessPool *)processPool {
    if (!_processPool) {
        static dispatch_once_t predicate;
        dispatch_once(&predicate, ^{
            _processPool = [[WKProcessPool alloc] init];
        });
    }
    
    return _processPool;
}

- (NSURL *)testUrl {
    if (!_testUrl) {
        NSURLComponents *urlComponents = [NSURLComponents new];
        urlComponents.host = @"tm.lilanz.com";
        urlComponents.scheme = @"http";
        urlComponents.path = @"a.aspx";
//        NSLog(@"测试url=%@", urlComponents.URL);
        return urlComponents.URL;
    }
    
    return _testUrl;
}

#pragma mark - WKNavigationDelegate
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
    //取出cookie
    NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
    for (NSHTTPCookie *cookie in cookieStorage.cookies) {
        NSLog(@"name = %@ value = %@",cookie.name,cookie.value);
    }
    //js函数
    NSString *JSFuncString =
    @"function setCookie(name,value,expires)\
    {\
        var oDate=new Date();\
        oDate.setDate(oDate.getDate()+expires);\
        document.cookie=name+'='+value+';expires='+oDate;\
    }\
    function getCookie(name)\
    {\
        var arr = document.cookie.match(new RegExp('(^| )'+name+'=([^;]*)(;|$)'));\
        if(arr != null) return unescape(arr[2]); return null;\
    }\
    function delCookie(name)\
    {\
        var exp = new Date();\
        exp.setTime(exp.getTime() - 1);\
        var cval=getCookie(name);\
        if(cval!=null) document.cookie= name + '='+cval+';expires='+exp.toGMTString();\
    }";
    
    //拼凑js字符串
    NSMutableString *JSCookieString = JSFuncString.mutableCopy;
    for (NSHTTPCookie *cookie in cookieStorage.cookies) {
        NSString *excuteJSString = [NSString stringWithFormat:@"setCookie('%@', '%@', 1);", cookie.name, cookie.value];
        [JSCookieString appendString:excuteJSString];
    }
    //执行js
    [webView evaluateJavaScript:JSCookieString completionHandler:nil];
}

  • 注:或者在其它有网络请求的地方,但是这个地方必须先执行于你要使用的网页之前。

  • 然后在加载WKWebView的地方使用即可。

WKCookieSyncManager *cookiesManager = [WKCookieSyncManager sharedWKCookieSyncManager];

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

推荐阅读更多精彩内容