我可以设置一个WKWebView使用的cookie吗?
我试图从UIWebView切换到WKWebView现有的应用程序。 当前的应用程序pipe理Web视图之外的用户login/会话,并将身份validation所需的cookie设置到NSHTTPCookieStore中。 不幸的是,新的WKWebView不使用NSHTTPCookieStorage中的cookie。 有没有另一种方法来实现这一目标?
如果您需要在初始加载请求中设置您的Cookie,则可以在NSMutableURLRequest上设置它们。 由于cookie只是一个特殊格式的请求头,因此可以这样来实现:
WKWebView * webView = /*set up your webView*/ NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://example.com/index.html"]]; [request addValue:@"TeskCookieKey1=TeskCookieValue1;TeskCookieKey2=TeskCookieValue2;" forHTTPHeaderField:@"Cookie"]; // use stringWithFormat: in the above line to inject your values programmatically [webView loadRequest:request];
如果您需要页面上的后续AJAX请求设置Cookie,可以通过简单地使用WKUserScript在文档启动时通过JavaScript以编程方式设置值,如下所示:
WKUserContentController* userContentController = WKUserContentController.new; WKUserScript * cookieScript = [[WKUserScript alloc] initWithSource: @"document.cookie = 'TeskCookieKey1=TeskCookieValue1';document.cookie = 'TeskCookieKey2=TeskCookieValue2';" injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO]; // again, use stringWithFormat: in the above line to inject your values programmatically [userContentController addUserScript:cookieScript]; WKWebViewConfiguration* webViewConfig = WKWebViewConfiguration.new; webViewConfig.userContentController = userContentController; WKWebView * webView = [[WKWebView alloc] initWithFrame:CGRectMake(/*set your values*/) configuration:webViewConfig];
结合这两种技术应该可以为您提供足够的工具来将cookie值从Native App Land传输到Web View Land。 如果你需要一些更高级的cookies,你可以在mozilla的页面上findcookie javascript api的更多信息。
是的,苹果并不支持UIWebView的许多优点 。 不知道他们是否会支持他们,但希望他们能很快得到这一点。 希望这可以帮助!
玩这个答案后 (这是非常有帮助:)我们不得不做一些改变:
- 我们需要Web视图来处理多个域,而不会在这些域之间泄漏私有cookie信息
- 我们需要它来兑现安全的cookies
- 如果服务器更改cookie值,我们希望我们的应用程序在
NSHTTPCookieStorage
了解它 - 如果服务器更改了一个cookie值,我们不希望我们的脚本在您关注链接/ AJAX等时将其重置为原始值
所以我们修改了代码
创build一个请求
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值的服务器。 这意味着添加另一个脚本来callback我们正在创build的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的地方。 我们现在可以使用这个代码作为帮手来创build我们的网页浏览,并为我们透明地更新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; }
为我工作
func webView(webView: WKWebView, decidePolicyForNavigationAction navigationAction: WKNavigationAction, decisionHandler: (WKNavigationActionPolicy) -> Void) { let headerFields = navigationAction.request.allHTTPHeaderFields var headerIsPresent = contains(headerFields?.keys.array as! [String], "Cookie") if headerIsPresent { decisionHandler(WKNavigationActionPolicy.Allow) } else { let req = NSMutableURLRequest(URL: navigationAction.request.URL!) let cookies = yourCookieData let values = NSHTTPCookie.requestHeaderFieldsWithCookies(cookies) req.allHTTPHeaderFields = values webView.loadRequest(req) decisionHandler(WKNavigationActionPolicy.Cancel) } }
这里是我的Swift版本的Mattrs解决scheme,用于注入NSHTTPCookieStorage中的所有cookie。 这主要是为了注入authenticationcookie来创build用户会话。
public func setupWebView() { let userContentController = WKUserContentController() if let cookies = NSHTTPCookieStorage.sharedHTTPCookieStorage().cookies { let script = getJSCookiesString(cookies) let cookieScript = WKUserScript(source: script, injectionTime: WKUserScriptInjectionTime.AtDocumentStart, forMainFrameOnly: false) userContentController.addUserScript(cookieScript) } let webViewConfig = WKWebViewConfiguration() webViewConfig.userContentController = userContentController self.webView = WKWebView(frame: self.webViewContainer.bounds, configuration: webViewConfig) } ///Generates script to create given cookies public func getJSCookiesString(cookies: [NSHTTPCookie]) -> String { var result = "" let dateFormatter = NSDateFormatter() dateFormatter.timeZone = NSTimeZone(abbreviation: "UTC") dateFormatter.dateFormat = "EEE, d MMM yyyy HH:mm:ss zzz" for cookie in cookies { result += "document.cookie='\(cookie.name)=\(cookie.value); domain=\(cookie.domain); path=\(cookie.path); " if let date = cookie.expiresDate { result += "expires=\(dateFormatter.stringFromDate(date)); " } if (cookie.secure) { result += "secure; " } result += "'; " } return result }
设置cookie
self.webView.evaluateJavaScript("document.cookie='access_token=your token';domain='your domain';") { (data, error) -> Void in self.webView.reload() }
删除cookie
self.webView.evaluateJavaScript("document.cookie='access_token=';domain='your domain';") { (data, error) -> Void in self.webView.reload() }
Swift 3更新:
func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) { if let urlResponse = navigationResponse.response as? HTTPURLResponse, let url = urlResponse.url, let allHeaderFields = urlResponse.allHeaderFields as? [String : String] { let cookies = HTTPCookie.cookies(withResponseHeaderFields: allHeaderFields, for: url) HTTPCookieStorage.shared.setCookies(cookies , for: urlResponse.url!, mainDocumentURL: nil) decisionHandler(.allow) } }
在iOS 11中,您现在可以pipe理Cookie了,请参阅此会话: https : //developer.apple.com/videos/play/wwdc2017/220/