iOS_bookmark

WKWebView的cookie共享问题:与native之间、多

2017-04-12  本文已影响416人  最晴天

这是两个不同的项目的总结:
项目一,只需多个webView之间共享cookie
项目二,在项目一的基础上,增加了与native之间cookie的共享问题。
没有耐心的同学,可以直接到文章末尾查看。
项目二中,共享cookie时,我最初没有注意到cookie去重的问题,导致加载时,始终提示未登录,仔细查找了问题,才发现是cookie重复,并且最后一个cookie值为undefined导致的。

因项目一需求,需要在app中,初次加载首页的webView时,先行做webView的登录,因为由首页跳转新的webView,打开新页面是需要网页上已经登录方可。
在使用了WKWebView后,这已经是我第三次在尝试处理这个问题了,各种尝试。
1.在loadRequest时,添加cookie信息

self.webRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:urlStr]];
    
    NSData *cookiesdata = [[NSUserDefaults standardUserDefaults] objectForKey:HHUserDefaultCookie];
    if([cookiesdata length]) {
        NSArray *cookies = [NSKeyedUnarchiver unarchiveObjectWithData:cookiesdata];
        NSHTTPCookie *cookie;
        for (cookie in cookies) {
            NSString *cookieStr = [NSString stringWithFormat:@"%@=%@",cookie.name,cookie.value];
            [self.webRequest addValue:cookieStr forHTTPHeaderField:@"Cookie"];
        }
        HHLog(@"请求时需要的Cookie %@",cookies);
    }
    [self.webView loadRequest:self.webRequest];

失败!

2.为webView的configuration设置cookie

HHWeakSelf(weakSlef);
    WKUserContentController *userCC = self.webView.configuration.userContentController;
    HHWKJSOCHandle *weakHandle = [[HHWKJSOCHandle alloc]initWithDelegate:weakSlef isActivity:NO];
    [userCC addScriptMessageHandler:weakHandle name:HHNativeCategorymoreMethod];
    [userCC addScriptMessageHandler:weakHandle name:HHNaviveCategoryMethod];
    [userCC addScriptMessageHandler:weakHandle name:HHNativeProductMethod];
    [userCC addScriptMessageHandler:weakHandle name:HHNativeActivityMethod];
    
    //        [userCC addScriptMessageHandler:weakSelf name:HHNativeShareMethod];
    
    NSData *cookiesdata = [[NSUserDefaults standardUserDefaults] objectForKey:HHUserDefaultCookie];
    NSArray *cookies = [NSKeyedUnarchiver unarchiveObjectWithData:cookiesdata];
    NSHTTPCookie *cookie;
    for (cookie in cookies) {
        NSString *cookieStr = [NSString stringWithFormat:@"document.cookie = '%@=%@';",cookie.name,cookie.value];
        WKUserScript * cookieScript = [[WKUserScript alloc] initWithSource: cookieStr injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO];
        [userCC addUserScript:cookieScript];
        
    }

失败!

3.网页加载完成后,设置cookie
在- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation方法中

  //取出cookie
    NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
    //js函数
    NSString *JSFuncString =
    @"function setCookie(name,value,expires)\
    {\
    var oDate=new Date();\
    oDate.setDate(oDate.getDate()+expires);\
    document.cookie=name+'='+value+';expires='+oDate+';path=/'\
    }\
    function getCookie(name)\
    {\
    var arr = document.cookie.match(new RegExp('(^| )'+name+'=({FNXX==XXFN}*)(;|$)'));\
    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];
    }
    
    HHLog(@"JS字符串Cookie:  %@",JSCookieString);
    //执行js
    [webView evaluateJavaScript:JSCookieString completionHandler:^(id obj, NSError * _Nullable error) {
        NSLog(@"执行js  %@",error);
    }];

失败!

包括这三种方式全部设置上,依然失败。
静下心来思考,确定出我的问题并不是让WKWebView和Native共享cookie,而是多个WKWebView之间如何共享cookie。
问题根源确定了,下一步就简单多了,只需要让多个WKWebView共用同一个WKProcessPool实例就可以。

那么,如何能满足WKWebView和Native共享cookie的同时,并且使得多个WKWebView共用同一个WKProcessPool实例。

很简单,只要两者相结合就好。

基于这个思路,我们是不是可以先行让webView加载一个网址,在加载之初就先行为webView的configuration设置cookie,如此,当第一个网址加载完毕之后,webView之间就可以共享cookie了呢。

基于这个设想,来实现。
使用示例,在WKWebViewontroller中

- (WKWebView *)webView{
    if (_webView == nil) {
        WKUserContentController *userCC = [WKUserContentController new];
        [userCC addScriptMessageHandler:self name:@"urlDirect"];
        
        WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc]init];
        //设置HTML5视频是否允许网页播放 设置为NO则会使用本地播放器
        config.allowsInlineMediaPlayback = YES;
        config.preferences.javaScriptCanOpenWindowsAutomatically = YES;
        config.userContentController = userCC;
        config.preferences.javaScriptEnabled = YES;
        //        config.suppressesIncrementalRendering = YES;
        config.processPool = [HHWKCookieSyncManager sharedCookieManager].processPool;
       
     
        _webView = [[WKWebView alloc]initWithFrame:ScreenBounds configuration:config];
        _webView.backgroundColor = [UIColor whiteColor];
        _webView.navigationDelegate = self;
        _webView.UIDelegate = self;
        _webView.scrollView.delegate = self;
        [_webView sizeToFit];
    
        if (@available(iOS 11.0, *)) {
            _webView.scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
        }
        [self.view addSubview:_webView];
        [_webView mas_makeConstraints:^(MASConstraintMaker *make) {
            make.edges.mas_equalTo(UIEdgeInsetsMake(0, 0, QSJSafeAreaBottomHeight, 0));
        }];
     
    }
    return _webView;
}

注意关键代码

  config.processPool = [HHWKCookieSyncManager sharedCookieManager].processPool;

加载网络请求时

- (void)refreshData{
    if (self.errView) {
        [self.errView removeFromSuperview];
    }
    //设置请求头
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:self.url]];
    if ([AccountManager shareInstance].isLogin) {
        [request setValue:[QSJUserDefaults objectForKey:QSJUserID] forHTTPHeaderField:@"userId"];
        [request setValue:[QSJUserDefaults objectForKey:QSJUserToken] forHTTPHeaderField:@"token"];
    }
    request.timeoutInterval = 10;

    HHWKCookieSyncManager *cookieManager = [HHWKCookieSyncManager sharedCookieManager];
//这行代码可以注释
    [cookieManager setWebViewCookie:self.webView];
    
    if (@available(iOS 11.0, *)) {
        [self.webView loadRequest:request];
    }else{
        @axc_weakify_self;
        cookieManager.loadAction = ^{
            @axc_strongify_self;
            [self.webView loadRequest:request];
        };
        cookieManager.loadFailure = ^(NSString *errMsg) {
            @axc_strongify_self;
            [self createErrViewWithMsg:errMsg];
        };
        [cookieManager syncCookieForURL:[NSURL URLWithString:self.url] loadAction:cookieManager.loadAction];
    }
    
}

下面是主要文件代码
HHWKCookieSyncManager.h文件

//
//  HHWKCookieSyncManager.h
//  GlobalTimes
//
//  Created by apple on 2017/4/11.
//  Copyright © 2017年 Hannah. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface HHWKCookieSyncManager : NSObject

@property (nonatomic, strong) WKProcessPool *processPool;
@property (nonatomic, strong) WKWebView *cookieWebview;
@property (nonatomic, copy) void(^loadAction)(void);
@property (nonatomic, copy) void(^loadFailure)(NSString *errMsg);

+ (instancetype)sharedCookieManager;
+ (NSString *)getCookieStr;
- (void)setWebViewCookie:(WKWebView *)webView;
- (void)syncCookieForURL:(NSURL *)url loadAction:(void(^)(void))loadAction;
- (void)shouldLoadRequestURL:(NSURL *)url scriptCallback:(void (^)(NSString *))scriptCallback ;
+ (void)removeCookieWithURL:(NSURL *)url;
@end

HHWKCookieSyncManager.m文件

//
//  HHWKCookieSyncManager.m
//  GlobalTimes
//
//  Created by apple on 2017/4/11.
//  Copyright © 2017年 Hannah. All rights reserved.
//

#import "HHWKCookieSyncManager.h"

@interface HHWKCookieSyncManager ()<WKNavigationDelegate,WKUIDelegate>

@property (nonatomic, strong) WKWebView *webView;
@property (nonatomic, strong) NSURL *testUrl;

@end

@implementation HHWKCookieSyncManager

static inline WKUserScript * WKCookieUserScript(NSString *cookieString) {
    if (!cookieString.length) {
        return nil;
    }
    WKUserScript *cookieScript = [[WKUserScript alloc] initWithSource:cookieString
                                                        injectionTime:WKUserScriptInjectionTimeAtDocumentStart
                                                     forMainFrameOnly:NO];
    return cookieScript;
}

+ (instancetype)sharedCookieManager{
    
    static HHWKCookieSyncManager *__manager = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        __manager = [[self alloc] init];
    });
    return __manager;
}


- (WKProcessPool *)processPool{
    if (_processPool == nil) {
        static dispatch_once_t onceToken;
        static WKProcessPool *processPool;
        dispatch_once(&onceToken, ^{
            processPool = [[WKProcessPool alloc] init];
        });
        _processPool = processPool;
    }
    return _processPool;
}

- (WKWebView *)cookieWebview {
    if (!_cookieWebview) {
        WKUserContentController *userContentController = WKUserContentController.new;
        WKWebViewConfiguration* webViewConfig = WKWebViewConfiguration.new;
        webViewConfig.userContentController = userContentController;
        webViewConfig.processPool = [HHWKCookieSyncManager sharedCookieManager].processPool;
        _cookieWebview = [[WKWebView alloc] initWithFrame:CGRectZero configuration:webViewConfig];
        _cookieWebview.UIDelegate = self;
        _cookieWebview.navigationDelegate = self;
    }
    return _cookieWebview;
}


+ (NSString *)getCookieStr{
    NSHTTPCookieStorage *cookieJar = [NSHTTPCookieStorage sharedHTTPCookieStorage];
    NSMutableString *cookieString = [[NSMutableString alloc] init];
    for (NSHTTPCookie *cookie in [cookieJar cookies]) {
//此处可进行cookie去重
        if ([cookie.value isEqualToString:@"undefined"] == NO && cookie.value != NULL && cookie.value.length > 0) {
            [cookieString appendFormat:@"document.cookie = '%@=%@';\n", cookie.name, cookie.value];
        }
    }
    
    //删除最后一个“;”
    //    [cookieString deleteCharactersInRange:NSMakeRange(cookieString.length - 1, 1)];
    return [cookieString copy];
}



- (void)setWebViewCookie:(WKWebView *)webView{
    NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
    
    
    for (NSHTTPCookie *cookie in cookieStorage.cookies) {
        if (@available(iOS 11.0, *)) {
            [webView.configuration.websiteDataStore.httpCookieStore setCookie:cookie completionHandler:nil];
        } else {
            // Fallback on earlier versions
            NSString *cookieString = QSJFormat(@"document.cookie = '%@=%@';\n", cookie.name, cookie.value);
            WKUserScript *script = [[WKUserScript alloc] initWithSource:cookieString injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO];
            [webView.configuration.userContentController addUserScript:script];
        }
    }
}

- (void)syncCookieForURL:(NSURL *)url loadAction:(void(^)(void))loadAction {
    [self shouldLoadRequestURL:url scriptCallback:^(NSString *cookieScript) {
        if (cookieScript.length) {
            [self.cookieWebview.configuration.userContentController removeAllUserScripts];
            [self.cookieWebview.configuration.userContentController addUserScript:WKCookieUserScript(cookieScript)];
            NSString *baseWebUrl = [NSString stringWithFormat:@"%@://%@", url.scheme,url.host];
            //如果需要加载cookie,则需要再cookie webview加载结束后再加载url,也就是在webView:(WKWebView *)webView didFinishNavigation方法中开始加载url
            [self.cookieWebview loadHTMLString:@"" baseURL:[NSURL URLWithString:baseWebUrl]];
        } else {
            //如果没有cookie需要加载,则直接加载url
            if (loadAction) {
                loadAction();
            }
        }
    }];
}

- (void)shouldLoadRequestURL:(NSURL *)url scriptCallback:(void (^)(NSString *))scriptCallback {
    if (!scriptCallback) {
        return;
    }
    //此处可根据url决定是否需要加载cookie等逻辑
    if (!url.host.length || [url.host isEqualToString:QSJHost] == NO) {
        scriptCallback(nil);
        return;
    }

    scriptCallback([HHWKCookieSyncManager getCookieStr]);
}

+ (void)removeCookieWithURL:(NSURL *)url{
    if (@available(iOS 9.0, *)) {
        NSSet *cookieTypeSet = [NSSet setWithObject:WKWebsiteDataTypeCookies];
        [[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:cookieTypeSet modifiedSince:[NSDate dateWithTimeIntervalSince1970:0] completionHandler:^{
            
        }];
    }
}

#pragma mark - WKNavigationDelegate
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{
    
    [webView evaluateJavaScript:[HHWKCookieSyncManager getCookieStr] completionHandler:^(id _Nullable response, NSError * _Nullable error) {
        
    }];
    if (self.loadAction) {
        self.loadAction();
    }
}

- (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error{

    if (error.code == NSURLErrorCancelled || error.domain == NSURLErrorDomain) {
        return;
    }
    if (self.loadFailure) {
        self.loadFailure(error.localizedDescription);
    }
}




@end

上一篇下一篇

猜你喜欢

热点阅读