iPhone:NSHTTPCookie未在应用重启期间保存

时间:2010-04-18 14:27:39

标签: iphone cookies

在我的iPhone应用程序中,我希望能够在我的应用程序重新启动时重用相同的服务器端会话。服务器上的会话由cookie标识,cookie在每个请求上发送。当我重新启动应用程序时,该cookie已经消失,我不能再使用相同的会话了。

当我使用NSHTTPCookieStorage查找从服务器获取的Cookie时,我注意到[cookie isSessionOnly]返回YES。我得到的印象是,这就是为什么不会在重新启动应用时保存Cookie的原因。我需要做些什么才能使我的cookie不仅仅是会话?我必须从服务器发送哪些HTTP标头?

7 个答案:

答案 0 :(得分:43)

您可以通过保存其属性字典来保存Cookie,然后在重新连接之前将其还原为新的Cookie。

保存:

NSArray* allCookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:[NSURL URLWithString:URL]];
for (NSHTTPCookie *cookie in allCookies) {
    if ([cookie.name isEqualToString:MY_COOKIE]) { 
        NSMutableDictionary* cookieDictionary = [NSMutableDictionary dictionaryWithDictionary:[[NSUserDefaults standardUserDefaults] dictionaryForKey:PREF_KEY]];
        [cookieDictionary setValue:cookie.properties forKey:URL];
        [[NSUserDefaults standardUserDefaults] setObject:cookieDictionary forKey:PREF_KEY];
    }
 }

负载:

NSDictionary* cookieDictionary = [[NSUserDefaults standardUserDefaults] dictionaryForKey:PREF_KEY];
NSDictionary* cookieProperties = [cookieDictionary valueForKey:URL];
if (cookieProperties != nil) {
    NSHTTPCookie* cookie = [NSHTTPCookie cookieWithProperties:cookieProperties];
    NSArray* cookieArray = [NSArray arrayWithObject:cookie];
    [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookies:cookieArray forURL:[NSURL URLWithString:URL] mainDocumentURL:nil];
}

答案 1 :(得分:22)

我赞成@ TomIrving的回答并在此详细阐述,因为许多用户不会看到他所说的非常重要的评论:

"您需要设置过期日期,否则假定cookie仅限会话。"

基本上,当您关闭应用程序时,cookie将被删除,除非cookie将来有一个到期日期。

如果您可以控制服务器并且可以要求它设置" Expires"您不需要在NSUserDefaults中存储和恢复Cookie。在将来的标题。如果您无法控制服务器或者不希望覆盖服务器的行为,则可以欺骗'通过更改其中的expiresDate来确定您的应用:

当您重新打开应用时,您会注意到该Cookie尚未删除。

答案 2 :(得分:5)

仅限会话的Cookie将按其性质过期。如果您真的需要,可以手动将它们存储在钥匙串中。我更喜欢使用Keychain来保存UserDefaults或归档,因为cookie可以更好地保护,就像用户的密码一样。

不幸的是,保存仅限会话的cookie不是很有帮助,下面的代码只是一个例子来说明如何存储cookie,但是不能强迫服务器以任何方式接受这样的cookie(除非你可以控制服务器)

Swift 2.2

// Saving into Keychain
if let cookies = NSHTTPCookieStorage.sharedHTTPCookieStorage().cookies {
    let cookiesData: NSData = NSKeyedArchiver.archivedDataWithRootObject(cookies)
    let userAccount = "some unique string to identify the item in Keychain, in my case I use username"
    let domain = "some other string you can use in combination with userAccount to identify the item"           
    let keychainQuery: [NSString: NSObject] = [
                        kSecClass: kSecClassGenericPassword,
                        kSecAttrAccount: userAccount + "cookies", 
                        kSecAttrService: domain,
                        kSecValueData: cookiesData]
    SecItemDelete(keychainQuery as CFDictionaryRef) //Trying to delete the item from Keychaing just in case it already exists there
    let status: OSStatus = SecItemAdd(keychainQuery as CFDictionaryRef, nil)
    if (status == errSecSuccess) {
        print("Cookies succesfully saved into Keychain")
    }
}

// Getting from Keychain
let userAccount = "some unique string to identify the item in Keychain, in my case I use username"
let domain = "some other string you can use in combination with userAccount to identify the item"
let keychainQueryForCookies: [NSString: NSObject] = [
                             kSecClass: kSecClassGenericPassword,
                             kSecAttrService: domain, // we use JIRA URL as service string for Keychain
                             kSecAttrAccount: userAccount + "cookies",
                             kSecReturnData: kCFBooleanTrue,
                             kSecMatchLimit: kSecMatchLimitOne]
var rawResultForCookies: AnyObject?
let status: OSStatus = SecItemCopyMatching(keychainQueryForCookies, &rawResultForCookies)
if (status == errSecSuccess) {
    let retrievedData = rawResultForCookies as? NSData
    if let unwrappedData = retrievedData {
        if let cookies = NSKeyedUnarchiver.unarchiveObjectWithData(unwrappedData) as? [NSHTTPCookie] {
            for aCookie in cookies {
                NSHTTPCookieStorage.sharedHTTPCookieStorage().setCookie(aCookie)
            }
        }
    }
}

答案 3 :(得分:3)

我认为由服务器决定cookie是否仅限会话,你无法做任何事情。

答案 4 :(得分:1)

迅捷的方式

商店:

static func storeCookies() {
    let cookiesStorage = NSHTTPCookieStorage.sharedHTTPCookieStorage()
    let userDefaults = NSUserDefaults.standardUserDefaults()

    let serverBaseUrl = "http://yourserverurl.com"
    var cookieDict = [String : AnyObject]()

    for cookie in cookiesStorage.cookiesForURL(NSURL(string: serverBaseUrl)!)! {
        cookieDict[cookie.name] = cookie.properties
    }

    userDefaults.setObject(cookieDict, forKey: cookiesKey)
}

还原:

static func restoreCookies() {
    let cookiesStorage = NSHTTPCookieStorage.sharedHTTPCookieStorage()
    let userDefaults = NSUserDefaults.standardUserDefaults()

    if let cookieDictionary = userDefaults.dictionaryForKey(cookiesKey) {

        for (cookieName, cookieProperties) in cookieDictionary {
            if let cookie = NSHTTPCookie(properties: cookieProperties as! [String : AnyObject] ) {
                cookiesStorage.setCookie(cookie)
            }
        }
    }
}

答案 5 :(得分:0)

SWIFT 3

SAVE:

if let httpResponse = response as? HTTPURLResponse, let fields = httpResponse.allHeaderFields as? [String : String] {
            let cookies = HTTPCookie.cookies(withResponseHeaderFields: fields, for: response.url!)
            HTTPCookieStorage.shared.setCookies(cookies, for: response.url!, mainDocumentURL: nil)
            for cookie in cookies {
                if cookie.name == cookieName{
                    if cookieName == Constants.WS.COOKIES.COOKIE_SMS {
                        UserDefaults.standard.set(NSKeyedArchiver.archivedData(withRootObject: cookie), forKey: Constants.SHARED_DEFAULT.COOKIE_SMS)
                        UserDefaults.standard.synchronize()
                    }

                    return cookie.value
                }
            }
        }

GET:

let cookie: HTTPCookie = NSKeyedUnarchiver.unarchiveObject(with: UserDefaults.standard.object(forKey: Constants.SHARED_DEFAULT.COOKIE_SMS) as! Data) as! HTTPCookie
        HTTPCookieStorage.shared.setCookie(cookie)

答案 6 :(得分:0)

Swift 5版本

func storeCookies() {

    guard let serverBaseUrl = URL(string: Constants.baseURL) else {
        return
    }

    let cookiesStorage: HTTPCookieStorage = .shared

    var cookieDict: [String: Any] = [:]

    cookiesStorage.cookies(for: serverBaseUrl)?.forEach({ cookieDict[$0.name] = $0.properties })

    let userDefaults = UserDefaults.standard
    userDefaults.set(cookieDict, forKey: Constants.cookiesKey)
}

func restoreCookies() {

    let cookiesStorage: HTTPCookieStorage = .shared

    let userDefaults = UserDefaults.standard

    guard let cookieDictionary = userDefaults.dictionary(forKey: Constants.cookiesKey) else {
        return
    }

    let cookies = cookieDictionary
        .compactMap({ $0.value as? [HTTPCookiePropertyKey: Any] })
        .compactMap({ HTTPCookie(properties: $0) })

    cookiesStorage.setCookies(cookies, for: URL(string: Constants.baseURL), mainDocumentURL: nil)
}