美文网首页iOS_bookmark
WKWebView的cookie共享问题:与native之间、多

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

作者: 最晴天 | 来源:发表于2017-04-12 16:58 被阅读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
    
    

    相关文章

      网友评论

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

        本文链接:https://www.haomeiwen.com/subject/dibmattx.html