为了账号安全,请及时绑定邮箱和手机立即绑定

我可以设置WKWebView使用的cookie吗?

/ 猿问

我可以设置WKWebView使用的cookie吗?

慕盖茨4494581 2019-07-23 17:59:19

我可以设置WKWebView使用的cookie吗?

我正在尝试将现有应用切换UIWebViewWKWebView。当前的应用程序管理用户登录/会话以外的用户,webview并将cookies身份验证所需的内容设置为NSHTTPCookieStore。不幸的是,新的WKWebView不使用cookies来自NSHTTPCookieStorage。还有另一种方法来实现这一目标吗?



查看完整描述

3 回答

?
忽然笑

在玩了这个答案后(这非常有用:)我们不得不做一些改变:

  • 我们需要Web视图来处理多个域,而不会泄漏这些域之间的私有cookie信息

  • 我们需要它来兑现安全的cookie

  • 如果服务器更改了cookie值,我们希望我们的应用程序了解它 NSHTTPCookieStorage

  • 如果服务器更改了cookie值,我们不希望我们的脚本在您按照链接/ AJAX等时将其重置为原始值。

所以我们修改了我们的代码;

创建请求

NSMutableURLRequest *request = [originalRequest mutableCopy];NSString *validDomain = request.URL.host;const BOOL requestIsSecure = [request.URL.scheme isEqualToString:@"https"];NSMutableArray *array = [NSMutableArray array];for (NSHTTPCookie *cookie in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]) {
    // Don't even bother with values containing a `'`
    if ([cookie.name rangeOfString:@"'"].location != NSNotFound) {
        NSLog(@"Skipping %@ because it contains a '", cookie.properties);
        continue;
    }

    // Is the cookie for current domain?
    if (![cookie.domain hasSuffix:validDomain]) {
        NSLog(@"Skipping %@ (because not %@)", cookie.properties, validDomain);
        continue;
    }

    // Are we secure only?
    if (cookie.secure && !requestIsSecure) {
        NSLog(@"Skipping %@ (because %@ not secure)", cookie.properties, request.URL.absoluteString);
        continue;
    }

    NSString *value = [NSString stringWithFormat:@"%@=%@", cookie.name, cookie.value];
    [array addObject:value];}NSString *header = [array componentsJoinedByString:@";"];[request setValue:header forHTTPHeaderField:@"Cookie"];// Now perform the request...

这可以确保第一个请求设置了正确的cookie,而不从共享存储中发送任何用于其他域的cookie,也不会将任何安全cookie发送到不安全的请求中。

处理进一步的请求

我们还需要确保其他请求设置了cookie。这是使用在文档加载上运行的脚本来完成的,该脚本检查是否存在cookie集,如果没有,则将其设置为值NSHTTPCookieStorage

// Get the currently set cookie names in javascriptland[script appendString:@"var cookieNames = document.cookie.split('; ').map(function(cookie) { return cookie.split('=')[0] } );\n"];for (NSHTTPCookie *cookie in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]) {
    // Skip cookies that will break our script
    if ([cookie.value rangeOfString:@"'"].location != NSNotFound) {
        continue;
    }

    // Create a line that appends this cookie to the web view's document's cookies
    [script appendFormat:@"if (cookieNames.indexOf('%@') == -1) { document.cookie='%@'; };\n", cookie.name, cookie.wn_javascriptString];}WKUserContentController *userContentController = [[WKUserContentController alloc] init];WKUserScript *cookieInScript = [[WKUserScript alloc] initWithSource:script
                                                      injectionTime:WKUserScriptInjectionTimeAtDocumentStart
                                                   forMainFrameOnly:NO];[userContentController addUserScript:cookieInScript];

...

// Create a config out of that userContentController and specify it when we create our web view.WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];config.userContentController = userContentController;self.webView = [[WKWebView alloc] initWithFrame:webView.bounds configuration:config];

处理cookie更改

我们还需要处理服务器更改cookie的值。这意味着添加另一个脚本来回调我们正在创建的Web视图以更新我们的NSHTTPCookieStorage

WKUserScript *cookieOutScript = [[WKUserScript alloc] initWithSource:@"window.webkit.messageHandlers.updateCookies.postMessage(document.cookie);"
                                                       injectionTime:WKUserScriptInjectionTimeAtDocumentStart
                                                    forMainFrameOnly:NO];[userContentController addUserScript:cookieOutScript];[userContentController addScriptMessageHandler:webView
                                          name:@"updateCookies"];

并实现委托方法来更新任何已更改的cookie,确保我们只更新当前域中的cookie!

- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
    NSArray<NSString *> *cookies = [message.body componentsSeparatedByString:@"; "];
    for (NSString *cookie in cookies) {
        // Get this cookie's name and value
        NSArray<NSString *> *comps = [cookie componentsSeparatedByString:@"="];
        if (comps.count < 2) {
            continue;
        }

        // Get the cookie in shared storage with that name
        NSHTTPCookie *localCookie = nil;
        for (NSHTTPCookie *c in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:self.wk_webView.URL]) {
            if ([c.name isEqualToString:comps[0]]) {
                localCookie = c;
                break;
            }
        }

        // If there is a cookie with a stale value, update it now.
        if (localCookie) {
            NSMutableDictionary *props = [localCookie.properties mutableCopy];
            props[NSHTTPCookieValue] = comps[1];
            NSHTTPCookie *updatedCookie = [NSHTTPCookie cookieWithProperties:props];
            [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:updatedCookie];
        }
    }}

这似乎解决了我们的cookie问题,而我们不必处理我们使用WKWebView的每个地方。我们现在可以使用此代码作为帮助程序来创建我们的Web视图,并NSHTTPCookieStorage为我们透明地更新。


编辑:结果我在NSHTTPCookie上使用了一个私人类别 - 这是代码:

- (NSString *)wn_javascriptString {
    NSString *string = [NSString stringWithFormat:@"%@=%@;domain=%@;path=%@",
                        self.name,
                        self.value,
                        self.domain,
                        self.path ?: @"/"];

    if (self.secure) {
        string = [string stringByAppendingString:@";secure=true"];
    }

    return string;}


查看完整回答
反对 回复 2019-07-23
?
喵喔喔

必须在WKWebView创建之前在配置上设置cookie 。否则,即使WKHTTPCookieStoresetCookie完成处理,饼干就不能可靠地同步到网络视图。这又回到了这条线从文档WKWebViewConfiguration

@NSCopying var configuration: WKWebViewConfiguration { get }

@NSCopying有点像深刻的副本。实施超出了我的要求,但最终的结果是,除非您在初始化webview之前设置cookie,否则您不能指望那里的cookie。这会使app体系结构复杂化,因为初始化视图会变成异步过程。你最终会得到这样的东西

extension WKWebViewConfiguration {
    /// Async Factory method to acquire WKWebViewConfigurations packaged with system cookies    static func cookiesIncluded(completion: @escaping (WKWebViewConfiguration?) -> Void) {
        let config = WKWebViewConfiguration()
        guard let cookies = HTTPCookieStorage.shared.cookies else {
            completion(config)
            return
        }
        // Use nonPersistent() or default() depending on if you want cookies persisted to disk        // and shared between WKWebViews of the same app (default), or not persisted and not shared        // across WKWebViews in the same app.        let dataStore = WKWebsiteDataStore.nonPersistent()
        let waitGroup = DispatchGroup()
        for cookie in cookies {
            waitGroup.enter()
            dataStore.httpCookieStore.setCookie(cookie) { waitGroup.leave() }
        }
        waitGroup.notify(queue: DispatchQueue.main) {
            config.websiteDataStore = dataStore
            completion(config)
        }
    }}

然后使用类似的东西

override func loadView() {
    view = UIView()
    WKWebViewConfiguration.cookiesIncluded { [weak self] config in
        let webView = WKWebView(frame: .zero, configuration: webConfiguration)
        webView.load(request)
        self.view = webView    }}

上面的示例将视图创建推迟到最后一刻,另一个解决方案是提前创建配置或webview并在创建视图控制器之前处理异步性质。

最后一点:一旦你创建了这个webview,你已经将它放到了野外,你不能在不使用本答案中描述的方法的情况下添加更多的cookie 。但是,您可以使用WKHTTPCookieStoreObserverapi至少观察cookie发生的变化。因此,如果会话cookie在Webview中更新,您可以HTTPCookieStorage根据需要使用此新cookie 手动更新系统。

有关这方面的更多信息,请跳至2017年WWDC会议自定义Web内容加载中的 18:00 。在本课程开始时,有一个欺骗性的代码示例,它省略了应该在完成处理程序中创建webview的事实。

cookieStore.setCookie(cookie!) {
    webView.load(loggedInURLRequest)}

18:00的现场演示澄清了这一点。

编辑至少从Mojave Beta 7和iOS 12 Beta 7开始,我发现使用cookie的行为更加一致。该setCookie(_:)方法甚至似乎允许WKWebView在创建后设置cookie 。我发现它虽然很重要,要不要触摸processPool变量都没有。如果没有创建其他池并且该属性保持单独,则cookie设置功能最有效。我认为可以说我们因WebKit中的一些错误而遇到问题。


查看完整回答
反对 回复 2019-07-23

添加回答

回复

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信