iOS大咖程序员iOS Developer

WKWebView和UIWebView的cookie的sessi

2017-03-22  本文已影响2723人  HelloAda

前言

首先这篇文章所描述的仅仅只是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的问题。

具体解决过程如下:

        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];
}

WKCookieSyncManager *cookiesManager = [WKCookieSyncManager sharedWKCookieSyncManager];

WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
configuration.processPool = cookiesManager.processPool;
_wkWebView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:configuration];
在使用一个月后暂时未发现什么问题,如果有问题,欢迎告诉我,一起完善解决方法。
上一篇下一篇

猜你喜欢

热点阅读