从 WKWebView 获取所有 cookie

Posted

技术标签:

【中文标题】从 WKWebView 获取所有 cookie【英文标题】:Getting all cookies from WKWebView 【发布时间】:2016-01-14 09:15:00 【问题描述】:

虽然使用NSHTTPCookieStorage.sharedHTTPCookieStorage()UIWebView 获取cookie 似乎很简单,但WKWebView 似乎将cookie 存储在其他地方。

我做了一些研究,我能够从NSHTTPURLResponse 对象中获取一些 cookie。但是,这并不包含 WKWebView 使用的所有 cookie:

func webView(webView: WKWebView, decidePolicyForNavigationResponse navigationResponse: WKNavigationResponse, decisionHandler: (WKNavigationResponsePolicy) -> Void) 

  if let httpResponse = navigationResponse.response as? NSHTTPURLResponse 
    if let headers = httpResponse.allHeaderFields as? [String: String], url = httpResponse.URL 
      let cookies = NSHTTPCookie.cookiesWithResponseHeaderFields(headers, forURL: url)

      for cookie in cookies 
        logDebug(cookie.description)

        logDebug("found cookie " + cookie.name + " " + cookie.value)
      
    
  

奇怪的是,在 ios 9 中还有一个类 WKWebsiteDataStore 负责管理 WKWebView 中的 cookie,但是,该类不包含检索 cookie 数据的公共方法:

let storage = WKWebsiteDataStore.defaultDataStore()

storage.fetchDataRecordsOfTypes([WKWebsiteDataTypeCookies], completionHandler:  (records) -> Void in
  for record in records 
    logDebug("cookie record is " + record.debugDescription)

    for dataType in record.dataTypes 
      logDebug("data type is " + dataType.debugDescription)

      // get cookie data??
    
  
)

是否有获取 cookie 数据的解决方法?

【问题讨论】:

值得注意的是,WebKit 团队似乎正在研究一种访问 WKWebView 的 cookie 存储的正确方法:bugs.webkit.org/show_bug.cgi?id=140191 @aporat 你有没有找到任何解决方案,我几个月来一直在努力,但还没有找到任何解决方案:( ***.com/questions/39772007/… @aporat 你还没有提到获取 cookie 数据 :) 【参考方案1】:

WKWebView 使用(创建)的 Cookie 实际上正确存储在 NSHTTPCookieStorage.sharedHTTPCookieStorage() 中。

问题是WKWebView 没有立即写回cookies。我认为它是按照自己的时间表进行的。例如,当 WKWebView 关闭或可能定期关闭时。

所以最终他们确实会在那里结束,但 何时 是不可预测的。

您可以通过关闭您的WKWebView 来强制“同步”到共享的NSHTTPCookieStorage。请让我们知道这是否有效。

更新:我只记得在Firefox for iOS 中,我们通过将WKProcessPool 替换为新的WKWebView 来强制刷新其内部数据,包括cookies。没有官方 API,但我很确定这是目前最可靠的解决方法。

【讨论】:

谢谢。我会检查一下。您是否引用了执行该 WKProcessPool 开关的函数?我只是用一个新的替换池吗? 有人体验过此答案中描述的解决方法吗? “关闭WKWebView”是什么意思? removeFromSuperview 并将其设置为 nil? 我需要 WKWebView cookie 的帮助。 对于那些看到这个并感到困惑的人,我确实做到了:self.webView.configuration.processPool = [[WKProcessPool alloc] init]; 它可以刷新 cookie,因此它们可以在NSHTTPCookieStorage.sharedHTTPCookieStorage() 中使用,但是它只适用于我的设备,而不是模拟器.【参考方案2】:

终于,httpCookieStore for WKWebsiteDataStore 登陆 iOS 11。

https://developer.apple.com/documentation/webkit/wkwebsitedatastore?changes=latest_minor

【讨论】:

iOS 10 或更低版本怎么样【参考方案3】:

详情

Xcode 9.2,Swift 4 Xcode 10.2 (10E125)、Swift 5

解决方案

extension WKWebView 

    private var httpCookieStore: WKHTTPCookieStore   return WKWebsiteDataStore.default().httpCookieStore 

    func getCookies(for domain: String? = nil, completion: @escaping ([String : Any])->())  
        var cookieDict = [String : AnyObject]()
        httpCookieStore.getAllCookies  cookies in
            for cookie in cookies 
                if let domain = domain 
                    if cookie.domain.contains(domain) 
                        cookieDict[cookie.name] = cookie.properties as AnyObject?
                    
                 else 
                    cookieDict[cookie.name] = cookie.properties as AnyObject?
                
            
            completion(cookieDict)
        
    

用法

// get cookies for domain
webView.getCookies(for: url.host)  data in
      print("=========================================")
      print("\(url.absoluteString)")
      print(data)


// get all cookies
webView.getCookies()  data in
      print("=========================================")
      print("\(url.absoluteString)")
      print(data)

完整样本

Info.plist

添加 Info.plist 传输安全设置

 <key>NSAppTransportSecurity</key>
 <dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
 </dict>

代码

    别忘了在此处添加解决方案代码 ViewController 已嵌入视图控制器
import UIKit
import WebKit

class ViewController: UIViewController 

    private lazy var url = URL(string: "https://google.com")!
    private weak var webView: WKWebView?

    func initWebView(configuration: WKWebViewConfiguration) 
        if webView != nil  return 
        let webView = WKWebView(frame: UIScreen.main.bounds, configuration: configuration)
        webView.navigationDelegate = self
        webView.uiDelegate = self
        view.addSubview(webView)
        self.webView = webView
    

    override func viewWillAppear(_ animated: Bool) 
        super.viewWillAppear(animated)
        if webView == nil  initWebView(configuration: WKWebViewConfiguration()) 
        webView?.load(url: url)
    


extension ViewController: WKNavigationDelegate 

    func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) 
        decisionHandler(.allow)
    

    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) 
        if let url = webView.url 
            webView.getCookies(for: url.host)  data in
                print("=========================================")
                print("\(url.absoluteString)")
                print(data)
            
        
    


extension ViewController: WKUIDelegate 

    func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? 
        // push new screen to the navigation controller when need to open url in another "tab"
        if let url = navigationAction.request.url, navigationAction.targetFrame == nil 
            let viewController = ViewController()
            viewController.initWebView(configuration: configuration)
            viewController.url = url
            DispatchQueue.main.async  [weak self] in
                self?.navigationController?.pushViewController(viewController, animated: true)
            
            return viewController.webView
        
        return nil
    


extension WKWebView 

    func load(urlString: String) 
        if let url = URL(string: urlString)  load(url: url) 
    

    func load(url: URL)  load(URLRequest(url: url)) 

【讨论】:

我可以得到完整的项目吗? @ShahidGhafoor 当然,dropbox.com/s/kft7ue4zgn4p5hl/***-33156567.zip?dl=0【参考方案4】:

我知道这是一个非常古老的问题,我们有一个解决方案,但仅适用于 iOS 11 及更高版本。对于那些正在处理 iOS 10 及更低版本的人(如我),您可以考虑这种方法。它对我来说非常有效:

强制重置进程池:
extension WKWebView 
    func refreshCookies() 
        self.configuration.processPool = WKProcessPool()
        // TO DO: Save your cookies,...
    

-->这仅适用于真实设备。

对于模拟器,您应该添加:
func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) 
    if let response = navigationResponse.response as? HTTPURLResponse,
       let allHttpHeaders = response.allHeaderFields as? [String: String],
       let responseUrl = response.url 
        let cookies = HTTPCookie.cookies(withResponseHeaderFields: allHttpHeaders, for: responseUrl)

        for cookie in cookies 
            HTTPCookieStorage.shared.setCookie(cookie)
        
    

    decisionHandler(.allow)

跟随 Stefan Arentz 和 Phenom 的回答。

【讨论】:

您是否忘记了实际复制 cookie 的方式? 何时调用refreshCookies()?我想在我们需要它来检索cookie之前?它仍然不适用于我正在使用的网站,在运行 iOS 13.1.2 的实际设备上进行了测试。是因为会话/持久性 cookie 吗? 我回来投票了,以获得我添加的 cookie self.configuration.websiteDataStore.httpCookieStore.getAllCookies (cookies) in /* your own code */。到目前为止仅在设备上进行了测试(iOS 13)。 我总是得到空的“cookies”数组。请帮帮我【参考方案5】:

对于 iOS 11,无需任何扩展:

func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) 
    self.webView.configuration.websiteDataStore.httpCookieStore.getAllCookies  cookies in
        for cookie in cookies 
            //...
        
    

【讨论】:

我是一名android开发者,对IOS完全陌生,只需要解决这个问题!所以我的问题是你什么时候调用getAllCookies?在 webview.loadUrl 之后?还是以前?请给我一些指点谢谢! 需要在Webview完全加载网页时调用。 非常感谢!我实际上解决了这个问题,一开始对我来说有点困难。【参考方案6】:

我在 Objective-C 中使用了 WKHTTPCookieStore,这对我来说可以同时获取持久性和会话 cookie,但它仅适用于 iOS 11+

https://developer.apple.com/documentation/webkit/wkhttpcookiestore?changes=latest_minor&language=objc

 if (@available(iOS 11.0, *)) 
     WKHTTPCookieStore *cookieStore = _webView.configuration.websiteDataStore.httpCookieStore;
     [cookieStore getAllCookies:^(NSArray* cookies) 
        NSHTTPCookie *cookie;
        for(cookie in cookies)
            NSLog(@"cookie: %@", cookie);
        
 ];

如 Stefan 的回答所述,通过替换其 WKProcessPool 来强制 WKWebView 刷新其内部数据在 iOS 10 和 11 中对我有用,但仅适用于持久性 cookie;正如 J. Thoo 所描述的,似乎会话 cookie 被删除了

【讨论】:

嗨 jorge,你的 iOS 13.0 及更高版本的解决方案是什么?【参考方案7】:
if (@available(iOS 11.0, *)) 
  [webView.configuration.websiteDataStore.httpCookieStore
      getAllCookies:^(NSArray<NSHTTPCookie *> *_Nonnull cookies) 
        NSURLRequest *request =
            [[NSURLRequest alloc] initWithURL:self.URL]; //your URL
        NSURLSession *session = [NSURLSession sharedSession];
        NSURLSessionDataTask *task = [session
            dataTaskWithRequest:request
              completionHandler:^(NSData *responseData, NSURLResponse *response,
                                  NSError *error) 
                //Do Something
              ];
        [task resume];
        [session.configuration.HTTPCookieStorage storeCookies:cookies forTask:task];
      ];

【讨论】:

感谢您的回答对我有帮助,但它适用于 iOS 11.0 或更高版本。我想为 iOS 10 或更低版本执行相同的操作。请帮我。意味着我需要获取 iOS 10 中的所有 cookie。【参考方案8】:

斯威夫特 5

func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) 
    webView.configuration.websiteDataStore.httpCookieStore.getAllCookies  cookies in
        debugPrint(cookies.debugDescription)
    

    decisionHandler(.allow)

【讨论】:

【参考方案9】:

正如 Stefan 所说,cookie 存储在 NSHTTPCookieStorage.sharedHTTPCookieStorage()

但是,从我的实验中,我发现服务器设置的 Session cookie 对NSHTTPCookieStorage.sharedHTTPCookieStorage() 是不可见的。

只要每个WKWebView 共享同一个WKProcessPool 实例,这些会话cookie 就会针对每个请求传回服务器。如果您更改 WKWebView 的进程池,您实际上是在删除所有未来请求的会话 cookie。

【讨论】:

是的,可以说cookie存储在NSHTTPCookieStorage.sharedHTTPCookieStorage()中。但是,问题是:如果用户已经登录 UIWebView,它不会自动登录另一个 WKWebView,副签证。所以我的信念是:即使它们共享相同的cookie,它们背后的原理却大不相同【参考方案10】:

不要浪费时间从iOS 11 below device 中提取cookie,成功的机会非常少。由于某些安全原因,Cookie 提取可能会被阻止。

参考这些日志:

2019-02-07 00:05:45.548880+0530 MyApp[2278:280725] [BoringSSL] nw_protocol_boringssl_get_output_frames(1301) [C8.1:2][0x10fd776f0] get output frames failed, state 8196

2019-02-07 00:05:45.550915+0530 MyApp[2278:280725] TIC Read Status [8:0x0]: 1:57

试试这个为 iOS 11 以下设备构建的代码:

func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) 
        let cookieValue = HTTPCookieStorage.shared.cookies(for: navigationResponse.response.url!)
        print(cookieValue!)
        let response = navigationResponse.response as! HTTPURLResponse
        let headFields = response.allHeaderFields as! [String:String]

        let cookies = HTTPCookie.cookies(withResponseHeaderFields: headFields, for: response.url!)
        for cookie in cookies 
            print("name: \(cookie.name) value: \(cookie.value)")
        
        decisionHandler(.allow)
    

上面的代码会给你一个空的 cookie 数组,因为出于某些安全原因,cookie 提取被阻止。

我建议您尝试以下适用于 iOS 11 及更高版本的内容:

WKWebsiteDataStore.default().httpCookieStore.getAllCookies  (cookies) in
    for cookie in cookies 
        print(cookie)
    

【讨论】:

【参考方案11】:

在实践中,我发现在“decidePolicyForNavigationResponse”的方法中,你可以使用以下方式来获取cookie,但遗憾的是它不是一个完整/完整的会话列表。

let response = navigationResponse.response as! NSHTTPURLResponse
        let headFields = response.allHeaderFields as! [String:String]

        let cookies = NSHTTPCookie.cookiesWithResponseHeaderFields(headFields, forURL: response.URL!)

【讨论】:

【参考方案12】:

NSHTTPCookie.cookiesWithResponseHeaderFields(headers, forURL: url) 中,如果设置 cookie 的 url 不是导航响应 url(导致导航的 url)会发生什么?我注意到在决定策略导航响应中从未调用过设置 cookie 的回调 url。

func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) 
    let response = navigationResponse.response as! HTTPURLResponse
    let cookies  = HTTPCookie.cookies(withResponseHeaderFields: response.allHeaderFields as! [String : String], for: response.url!) 

由于回调本身不会导致页面导航,因此永远不会为回调 url 执行上述委托。

cookies(withResponseHeaderFields:for:)

【讨论】:

【参考方案13】:

This post 提供了有关使用 WKWebView 处理 cookie 的有用信息。据此,您应该能够使用标准的 NSURLCache 和 NSHTTPCookie 设置和检索 cookie。根据 Stephan 的评论,他还提到了使用 WKProccessPool。

【讨论】:

以上是关于从 WKWebView 获取所有 cookie的主要内容,如果未能解决你的问题,请参考以下文章

IOS - 如何从 WKWebView 获取缓存的资源?

如何从 WKWbView 完成加载中获取标题

如何从 WKWebView 启用和获取 cookie?

从 WKWebView 获取 favicon.co

使用 evaluateJavaScript 从 WKWebView 获取标题

从WkWebView获取JSON