ios Swift:如何为进一步的 http 请求记住 cookie

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/31464785/
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 06:54:10  来源:igfitidea点击:

Swift: How to remember cookies for further http requests

iosswiftcookiesios9

提问by Himanshu Yadav

I am working on a login application. After successful login response comes back with cookie data.
How can I use/save this data for my future requests?
For starters I am trying to save it in NSHTTPCookieStorage. But that is also not working.
Login Method(partial):

我正在开发一个登录应用程序。成功登录后,响应返回 cookie 数据。
我如何使用/保存这些数据以备将来的请求?
首先,我试图将它保存在NSHTTPCookieStorage. 但这也行不通。
登录方式(部分):

let task = session.dataTaskWithRequest(request) { (data, responseData, error) -> Void in
            if let response = responseData as? NSHTTPURLResponse {
                statusCode = response.statusCode
                print("Response code: \(statusCode)")
            }

            var json: NSDictionary?
            do {
                json = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableLeaves) as? NSDictionary
            } catch {
                print(error)
                err = error
            }

            if(statusCode != 200) {

                let jsonStr = NSString(data: data!, encoding: NSUTF8StringEncoding)
                print("Error could not parse JSON: '\(jsonStr)'")
            }
            else {

                print("Everything Looks good: \(responseData)")
                self.setCookies(responseData!)
                self.shouldPerformSegueWithIdentifier("showHomeController", sender: self)

            }
        }

        task?.resume()

Save Cookie Method

保存 Cookie 方法

private func setCookies(response: NSURLResponse) {
        if let httpResponse = response as? NSHTTPURLResponse {
            let cookies = NSHTTPCookie.cookiesWithResponseHeaderFields(httpResponse.allHeaderFields, forURL: response.URL!) as! [NSHTTPCookie]
            NSHTTPCookieStorage.sharedHTTPCookieStorage().setCookies(cookies, forURL: response.URL!, mainDocumentURL: nil)
            for cookie in cookies {
                var cookieProperties = [String: AnyObject]()
                cookieProperties[NSHTTPCookieName] = cookie.name
                cookieProperties[NSHTTPCookieValue] = cookie.value()
                cookieProperties[NSHTTPCookieDomain] = cookie.domain
                cookieProperties[NSHTTPCookiePath] = cookie.path
                cookieProperties[NSHTTPCookieVersion] = NSNumber(integer: cookie.version)
                cookieProperties[NSHTTPCookieExpires] = NSDate().dateByAddingTimeInterval(31536000)

                let newCookie = NSHTTPCookie(properties: cookieProperties)
                NSHTTPCookieStorage.sharedHTTPCookieStorage().setCookie(newCookie!)

                println("name: \(cookie.name) value: \(cookie.value())")
            }
        }
    }

Error:

错误:

Cannot invoke 'cookiesWithResponseHeaderFields' with an argument list of type '([NSObject : AnyObject], forURL: NSURL)'

回答by Sandeep

If you realize the usage of cookie, the server has to send the header Set-Cookiein response to the client request. Just inspect the header in response and you will see Set-Cookieheader field with cookie in it.

如果您意识到 cookie 的使用,则服务器必须发送标头Set-Cookie以响应客户端请求。只需检查响应中的标头,您就会看到Set-Cookie其中包含 cookie 的标头字段。

https://en.wikipedia.org/wiki/HTTP_cookie#Setting_a_cookie

https://en.wikipedia.org/wiki/HTTP_cookie#Setting_a_cookie

If you use URLSession with default or background URLSessionConfiguration, you dont have to make any change to save cookie. If you look at documentation for default URLSessionConfiguration, which describes it like this,

如果您将 URLSession 与 default 或 background 一起使用,则无需URLSessionConfiguration进行任何更改即可保存 cookie。如果您查看 default 文档URLSessionConfiguration,它是这样描述的,

The default session configuration uses a persistent disk-based cache (except when the result is downloaded to a file) and stores credentials in the user's keychain. It also stores cookies (by default) in the same shared cookie store as the NSURLConnection and NSURLDownload classes.

默认会话配置使用基于磁盘的持久缓存(除非将结果下载到文件)并将凭据存储在用户的钥匙串中。它还在与 NSURLConnection 和 NSURLDownload 类相同的共享 cookie 存储中存储 cookie(默认情况下)。

Also, you can read further about this in URLSessionConfigurationdocumentation for property httpCookieStoragehere.

此外,您可以在此处URLSessionConfiguration的财产文档中进一步了解此内容。httpCookieStorage

Here is a small snippet of code that I will use further to test the cookie storage.

这是我将进一步用于测试 cookie 存储的一小段代码。

let sessionConfiguration = URLSessionConfiguration.ephemeral
sessionConfiguration.httpCookieAcceptPolicy = .never
let customSession = URLSession(configuration: sessionConfiguration)

enum Result {
    case success(HTTPURLResponse, Data)
    case failure(Error)
}

func readCookie(forURL url: URL) -> [HTTPCookie] {
    let cookieStorage = HTTPCookieStorage.shared
    let cookies = cookieStorage.cookies(for: url) ?? []
    return cookies
}

func deleteCookies(forURL url: URL) {
    let cookieStorage = HTTPCookieStorage.shared

    for cookie in readCookie(forURL: url) {
        cookieStorage.deleteCookie(cookie)
    }
}

func storeCookies(_ cookies: [HTTPCookie], forURL url: URL) {
    let cookieStorage = HTTPCookieStorage.shared
    cookieStorage.setCookies(cookies,
                             for: url,
                             mainDocumentURL: nil)
}


func executeURLRequest(url: URL, inSession session: URLSession = .shared, completion: @escaping (Result) -> Void) {
    let task = session.dataTask(with: url) { data, response, error in

        if let response = response as? HTTPURLResponse,
            let data = data {
            completion(.success(response, data))
            return
        }

        if let error = error {
            completion(.failure(error))
            return
        }

        let error = NSError(domain: "com.cookiesetting.test", code: 101, userInfo: [NSLocalizedDescriptionKey: "Unknown error occurred"])
        completion(.failure(error))
    }
    task.resume()
}

With the snippet above, we firstly test that default session saves the cookie.

使用上面的代码片段,我们首先测试默认会话是否保存了 cookie。

var cookies = readCookie(forURL: googleURL)
print("Cookies before request: ", cookies)

executeURLRequest(url: googleURL) { result in
    if case .success (let data) = result {
        cookies = readCookie(forURL: googleURL)
        print("Cookies after request: ", cookies)

        deleteCookies(forURL: googleURL)
        cookies = readCookie(forURL: googleURL)
        print("Cookies after deletion: ", cookies)
    }
}

And, here is what we get,

而且,这是我们得到的,

Cookies before request:  []
Cookies after request:  [<NSHTTPCookie
    version:0
    name:1P_JAR
    value:2018-09-26-15
    expiresDate:'2018-10-26 15:39:46 +0000'
    created:'2018-09-26 15:39:46 +0000'
    sessionOnly:FALSE
    domain:.google.com
    partition:none
    sameSite:none
    path:/
    isSecure:FALSE
 path:"/" isSecure:FALSE>, <NSHTTPCookie
    version:0
    name:NID
    value:139=E3g4bKNRGcYoeFuaECpfsx_Efp64xONmVwcJS7f7PuZe8LayS5ZkGuz3f7z6eq7zoBm2z-opTvzX8YPzn8v1ebjH6iyt5-6yDYm9RE6XhXwHCZWs98_j7nb11u2EPnHI
    expiresDate:'2019-03-28 15:39:46 +0000'
    created:'2018-09-26 15:39:46 +0000'
    sessionOnly:FALSE
    domain:.google.com
    partition:none
    sameSite:none
    path:/
    isSecure:FALSE
    isHTTPOnly: YES
 path:"/" isSecure:FALSE isHTTPOnly: YES>]
Cookies after deletion:  []

URLSessionConfigurationalso has a property httpCookieAcceptPolicy, which quotes the following:

URLSessionConfiguration还有一个属性httpCookieAcceptPolicy,它引用了以下内容:

This property determines the cookie accept policy for all tasks within sessions based on this configuration.

The default value is HTTPCookie.AcceptPolicy.onlyFromMainDocumentDomain. You can change it to any of the constants defined in the HTTPCookie.AcceptPolicy enumerated type.

If you want more direct control over what cookies are accepted, set this value to HTTPCookie.AcceptPolicy.never and then use the allHeaderFields and cookies(withResponseHeaderFields:for:) methods to extract cookies from the URL response object yourself.

此属性根据此配置确定会话中所有任务的 cookie 接受策略。

默认值为 HTTPCookie.AcceptPolicy.onlyFromMainDocumentDomain。您可以将其更改为 HTTPCookie.AcceptPolicy 枚举类型中定义的任何常量。

如果您想更直接地控制接受哪些 cookie,请将此值设置为 HTTPCookie.AcceptPolicy.never,然后使用 allHeaderFields 和 cookies(withResponseHeaderFields:for:) 方法自己从 URL 响应对象中提取 cookie。

So, if you wish to manipulate the cookie by yourself, you could set the httpCookieAcceptPolicyto never.

所以,如果你希望自己操纵的cookie,你可以设置httpCookieAcceptPolicynever

Following code shows, cookie not stored when using httpCookieAcceptPolicy to never,

以下代码显示,使用 httpCookieAcceptPolicy 时不会存储 cookie,从不,

var cookies = readCookie(forURL: googleURL)
print("Cookies before request: ", cookies)

executeURLRequest(url: googleURL, inSession: customSession) { result in
    if case .success (let data) = result {
        cookies = readCookie(forURL: googleURL)
        print("Cookies after request: ", cookies)

    }
}

Which logs the following;

记录以下内容;

Cookies before request:  []
Cookies after request:  []

You can see that using .never for httpCookieStoragePolicy, system wont store cookie to shared cookie storage.

您可以看到,将 .never 用于 httpCookieStoragePolicy,系统不会将 cookie 存储到共享 cookie 存储中。

You can also store the cookie yourself, which would look like this,

你也可以自己存储cookie,看起来像这样,

Storing the cookie by ourselves

自行储存cookie

deleteCookies(forURL: googleURL)
var cookies = readCookie(forURL: googleURL)
print("Cookies before request: ", cookies)
executeURLRequest(url: googleURL, inSession: customSession) { result in
    if  case let .success  (response, data) = result {
        guard let cookiesResponseHeader = response.allHeaderFields["Set-Cookie"] else {
            return
        }

        cookies = readCookie(forURL: googleURL)
        print("Cookies after request: ", cookies)

        let responseCookies = HTTPCookie.cookies(withResponseHeaderFields: response.allHeaderFields as! [String: String], for: googleURL)
        storeCookies(responseCookies, forURL: googleURL)
        cookies = readCookie(forURL: googleURL)
        print("Cookies after storing: ", cookies)

    }
}

And, here is what the code above prints to console,

而且,这是上面的代码打印到控制台的内容,

Cookies before request:  []
Cookies after request:  []
Cookies after storing:  [<NSHTTPCookie
    version:0
    name:1P_JAR
    value:2018-09-26-18
    expiresDate:'2018-10-26 18:35:23 +0000'
    created:'2018-09-26 18:35:23 +0000'
    sessionOnly:FALSE
    domain:.google.com
    partition:none
    sameSite:none
    path:/
    isSecure:FALSE
 path:"/" isSecure:FALSE>, <NSHTTPCookie
    version:0
    name:NID
    value:139=D7GTUazWfeaB5Bcu1wN5I_Il2k6xALNiRZDX_DN9totQbnrP31gE0GzlsjCHDISUv8ulPq9G8Yu1p-GsZcVRw2fnrBROih-vtAVBic5UXFKUkG_ZbFQYKFprr4MPHDGS
    expiresDate:'2019-03-28 18:35:23 +0000'
    created:'2018-09-26 18:35:23 +0000'
    sessionOnly:FALSE
    domain:.google.com
    partition:none
    sameSite:none
    path:/
    isSecure:FALSE
    isHTTPOnly: YES
 path:"/" isSecure:FALSE isHTTPOnly: YES>]

The code above uses .neverHTTPCookieAcceptPolicy to URLSessionConfiguration but we create cookie from response and store it to the cookie store by ourselves.

上面的代码使用.neverHTTPCookieAcceptPolicy 到 URLSessionConfiguration 但我们从响应中创建 cookie 并将其存储到 cookie 存储中。

回答by Edward Ashak

class func cookiesWithResponseHeaderFields(_ headerFields: [String : String], forURL URL: NSURL) -> [NSHTTPCookie]

class func cookiesWithResponseHeaderFields(_ headerFields: [String : String], forURL URL: NSURL) -> [NSHTTPCookie]

Note that headerFields is [String: String] Dictionary and the compiler is complaining that you're passing [NSObject : AnyObject]

请注意 headerFields 是 [String: String] 字典并且编译器抱怨您正在传递 [NSObject : AnyObject]

回答by iman kazemayni

for URLSession connection you can use this(after you received your first response) :

对于 URLSession 连接,您可以使用它(在您收到第一个回复后):

var cookies =  URLSession.shared.configuration.httpCookieStorage?.cookies

and you can use these cookies like this:

您可以像这样使用这些 cookie:

var session = URLSession.self
session.shared.configuration.httpCookieStorage?.setCookies(cookies, for: baseurl, mainDocumentURL: baseurl)
    let task = session.shared.dataTask(with: url)