从 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的主要内容,如果未能解决你的问题,请参考以下文章