Подтвердить что ты не робот

Получение всех файлов cookie из WKWebView

в то время как получение файлов cookie из UIWebView кажется простым, используя NSHTTPCookieStorage.sharedHTTPCookieStorage(), кажется, что WKWebView хранит файлы cookie в другом месте.

Я провел некоторое исследование, и я смог получить некоторые файлы cookie из захвата его из объекта 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)
      }
    }
  }
}

Как ни странно, существует также класс WKWebsiteDataStore в ios 9, который отвечает за управление куки в 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??
    }
  }
})

Есть ли способ обхода файлов cookie?

4b9b3361

Ответ 2

Cookies, используемые (создаваемые) WKWebView, на самом деле правильно сохранены в NSHTTPCookieStorage.sharedHTTPCookieStorage().

Проблема заключается в том, что WKWebView не сразу записывает файлы cookie. Я думаю, что это делается по собственному графику. Например, когда a WKWebView закрыт или может быть периодически.

Итак, в конце концов, они все в порядке, но когда это непредсказуемо.

Возможно, вы сможете принудительно "синхронизировать" с общим NSHTTPCookieStorage, закрыв WKWebView. Пожалуйста, сообщите нам, если это сработает.

Обновить. Я только что вспомнил, что в Firefox для iOS мы вынуждаем WKWebView очищать свои внутренние данные, включая файлы cookie, заменив его WKProcessPool на новый. Официального API нет, но я уверен, что это самый надежный обходной путь прямо сейчас.

Ответ 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>

Код

  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

Ответ 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)
}

Следуй за ответом Стефана Арента и Фенома.

Ответ 5

Я использовал WKHTTPCookieStore в Objective-C. Это помогло мне получить как постоянные, так и сеансовые куки, но работает только в 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);
        }
 }];

Принуждение WKWebView для очистки внутренних данных путем замены его WKProcessPool, как описано в ответе Stefan, работало для меня в iOS 10 и 11, но только для постоянных файлов cookie; кажется, что файлы cookie сеанса удаляются, как описано в J. Thoo

Ответ 6

Как упоминал Стефан, файлы cookie хранятся в NSHTTPCookieStorage.sharedHTTPCookieStorage()

Однако из моих экспериментов я обнаружил, что cookie сеанса, установленные сервером, не отображается NSHTTPCookieStorage.sharedHTTPCookieStorage().

Пока каждый WKWebView использует один и тот же экземпляр WKProcessPool, эти куки сеанса будут переданы на сервер для каждого запроса. Если вы измените пул процессов для WKWebView, вы фактически удалите файлы cookie сеанса для всех будущих запросов.

Ответ 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];
      }];
}

Ответ 8

Не тратьте свое время на извлечение файлов cookie с iOS 11 below device, шансов на успех у вас очень мало. Из-за некоторых причин безопасности извлечение файлов 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)
    }
}

Ответ 9

На практике, я нашел в методе "solvePolicyForNavigationResponse", вы можете использовать следующий способ получения файлов cookie, но печально, что это не полный/цельный список для сеанса.

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

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

Ответ 10

В NSHTTPCookie.cookiesWithResponseHeaderFields(headers, forURL: url), что произойдет, если URL-адрес, где установлены файлы cookie, не является URL-адресом ответа на навигацию (URL-адрес, который вызывает навигацию)? Я замечаю, что URL-адрес обратного вызова, где установлены файлы cookie, никогда не вызывается в resolPolicyFor navigationResponse.

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-адреса обратного вызова, так как сам обратный вызов не вызвал навигацию по страницам.

cookie (withResponseHeaderFields: для:)

Ответ 11

Моя проблема заключалась также в том, что getAllCookies() время от времени не стрелял. Это очень странно, но я бы сказал, что это происходит в 25% случаев. Это дает мне много проблем, потому что у меня есть токен в поле "Set-Cookie", перенаправленный запрос, который использовался для входа в социальные сети. Простой поиск файлов cookie здесь

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

не дает ничего, потому что поле маркера в этом случае по какой-то причине отсутствует.

Также не было токена, если бы я смотрел в словаре, как это

   func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) 
let response = navigationResponse.response as! NSHTTPURLResponse
        let headFields = response.allHeaderFields as! [String:String]

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

В файлах cookie не обнаружено токена

Итак, я, наконец, закончил с обходным способом, принимающим токен от ответа напрямую и резкой строки, подобной этой

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

        if let response = navigationResponse.response as? HTTPURLResponse, var token = response.allHeaderFields["Set-Cookie"] as? String {
            let tokenConst = "token="
            token = String(token.dropFirst(tokenConst.count))
            print(token)
            tokenOAuthReceived(token)
        }

        decisionHandler(.allow)

    }

И если у вас много переадресаций в веб-форме, например, в моем случае, вам может потребоваться проверить токен для правильного URL-адреса, прежде чем использовать его

let urlString = response.url?.absoluteString, urlString.contains(find: RestApi.default.baseURLString)

Ответ 12

Swift 5

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

    decisionHandler(.allow)
}

Ответ 13

Для iOS 11 без каких-либо расширений:

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

Ответ 14

Этот пост содержит полезную информацию о обработке файлов cookie с помощью WKWebView. В соответствии с этим вы должны иметь возможность устанавливать и извлекать файлы cookie с помощью стандартных NSURLCache и NSHTTPCookie. Он также ссылается на использование WKProccessPool согласно замечанию Стефана.