ios 从 WKWebView 获取所有 cookie

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/33156567/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 08:01:05  来源:igfitidea点击:

Getting all cookies from WKWebView

iosswiftuiwebviewwebkitwkwebview

提问by aporat

while getting cookies from UIWebViewseems straightforward by using NSHTTPCookieStorage.sharedHTTPCookieStorage(), it seems WKWebViewstores the cookies somewhere else.

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

I did some research, and I was able to get some cookies from the grabbing it from NSHTTPURLResponseobject. this, however, does not contain all the cookies used by WKWebView:

我做了一些研究,我能够通过从NSHTTPURLResponse对象中抓取它来获得一些饼干。但是,这不包含以下使用的所有 cookie WKWebView

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)
      }
    }
  }
}

Strangely, there's also a class WKWebsiteDataStorein ios 9 that responsible for managing cookies in WKWebView, however, the class does not contain a public method to retrieve the cookies data:

奇怪的是WKWebsiteDataStore,ios 9 中还有一个类负责管理 cookie 中的 cookie WKWebView,但是,该类不包含检索 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??
    }
  }
})

Is there a workaround for getting the cookie data?

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

采纳答案by Tualatrix Chou

Finally, httpCookieStorefor WKWebsiteDataStorelanded in iOS 11.

最后,httpCookieStore对于WKWebsiteDataStore登陆 iOS 11。

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

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

回答by Stefan Arentz

Cookies used (created) by the WKWebVieware actually correctly stored in the NSHTTPCookieStorage.sharedHTTPCookieStorage().

使用(创建)的 CookieWKWebView实际上正确存储在NSHTTPCookieStorage.sharedHTTPCookieStorage().

The problem is that the WKWebViewdoes not write back the cookies immediately. I think it does this on its own schedule. For example when a WKWebViewis closed or maybe periodically.

问题是WKWebView不会立即写回 cookie。我认为它按照自己的时间表执行此操作。例如,当 aWKWebView关闭或可能定期时。

So eventually they do end up in there, but whenis unpredictable.

所以最终他们最终会在那里,但什么时候是不可预测的。

You may be able to force a 'sync' to the shared NSHTTPCookieStorageby closing your WKWebView. Please let us know if this works.

您可以NSHTTPCookieStorage通过关闭WKWebView. 请让我们知道这是否有效。

Update: I just remembered that in Firefox for iOSwe force the WKWebViewto flush its internal data, including cookies, by replacing its WKProcessPoolwith a new one. There is no official API, but I am pretty sure that is the most reliable workaround right now.

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

回答by Vasily Bodnarchuk

Details

细节

  • Xcode 9.2, Swift 4
  • Xcode 10.2 (10E125), Swift 5
  • Xcode 9.2,斯威夫特 4
  • Xcode 10.2 (10E125),Swift 5

Solution

解决方案

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)
        }
    }
}

Usage

用法

// 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)
}

Full sample

完整样品

Info.plist

信息表

add in your Info.plist transport security setting

添加您的 Info.plist 传输安全设置

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

Code

代码

  1. Do not forget to add the solution code here
  2. ViewController has embed view controller
  1. 不要忘记在此处添加解决方案代码
  2. 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)) }
}

enter image description here

在此处输入图片说明

回答by November Rain

I know this is a very old question, and we have a solution but work only on iOS 11 and upper. For those one who are dealing with iOS 10 and lower (like me), you may consider this method. It works perfectly to me:

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

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

--> this only work on real device.

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

  • For simulator, you should add:
  • 对于模拟器,您应该添加:
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)
}

Follow to the answer of Stefan Arentz and Phenom.

按照 Stefan Arentz 和 Phenom 的回答。

回答by Jorge Duque

I used WKHTTPCookieStore in Objective-C, This worked for me to get both persistent and session cookies, but it only works in iOS 11+

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

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

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);
        }
 }];

Forcing the WKWebView to flush its internal data by replacing its WKProcessPool as described by Stefan's answer worked for me in iOS 10 and 11 but only for persistent cookies; it seems like session cookies get removed, as J. Thoo described

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

回答by Vivek

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];
      }];
}

回答by J.Thoo

As Stefan mentioned, cookies are stored in NSHTTPCookieStorage.sharedHTTPCookieStorage()

正如 Stefan 提到的,cookies 存储在 NSHTTPCookieStorage.sharedHTTPCookieStorage()

However, from my experiments, I found that Session cookies set by the server are not visible to NSHTTPCookieStorage.sharedHTTPCookieStorage().

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

As long as each WKWebViewshare the same instance of WKProcessPool, those Session cookies will be passed back to the server for each request. If you change the process pool for a WKWebView, you are essentially removing the session cookies for all future requests.

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

回答by Shubham Mishra

Don't waste you time in extracting cookies from iOS 11 below device, there are very less chances of getting succeeded. Cookie extraction may get blocked due some security reasons.

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

Refer these logs:

请参阅这些日志:

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

Try this code which is build for below iOS 11 devices:

试试这个为低于 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)
    }

The above code will give you empty cookie array, as cookies extraction are being blocked due to some security reasons.

上面的代码将为您提供空的 cookie 数组,因为出于某些安全原因,cookie 提取被阻止。

I would recommend you to try following which is meant for iOS 11 and above:

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

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

回答by Den

Swift 5

斯威夫特 5

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

    decisionHandler(.allow)
}

回答by Zeero0

For iOS 11, without any extensions:

对于iOS 11,没有任何扩展:

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