2010-04-18 55 views
25

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

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

回答

41

您可以通过保存其属性字典来保存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

你需要同步nsuserdefaults吗? – ninjaneer 2012-01-10 08:52:48

+1

如果您需要立即保存,则只需要同步。否则,他们会在稍后的中间时间保存。以下是文档页面:http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSUserDefaults_Class/Reference/Reference.html#//apple_ref/occ/instm/NSUserDefaults/synchronize – 2012-01-10 18:44:41

2

我相信这取决于服务器来决定cookie是否仅限会话,您无法对此做任何事情。

+0

不,它确实是抛出会话cookie的客户端(iPhone应用程序)。会话仍然存在于服务器上。 – 2010-04-18 16:59:09

+3

没错,但服务器会告诉iPhone应用程序该cookie只是会话。 – 2010-04-18 17:00:40

+1

是的,那是通过HTTP。因此,我正在寻找从服务器发送的正确头文件,告诉iPhone应用程序它可以将Cookie保留更长时间。 – 2010-04-18 17:14:03

18

我已经upvoted @ TomIrving的答案,因为很多用户不会看到很重要的评论我阐述在这里,他说:

”您需要设置到期日期,否则该cookie将被视为仅限会话。“

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

如果您可以控制服务器并且可以要求它将“Expires”标头设置为将来某些内容,则无需在NSUserDefaults之间存储和恢复Cookie。如果你没有对服务器的控制,或不希望重写你的服务器的行为,你可以“绝招”你的应用程序由内它改变expiresDate

当您重新打开您的应用程序,你会发现该Cookie尚未删除新创建的cookie。

+2

从服务器端安全方法来看,这应该是被接受的答案。 – MandisaW 2015-10-22 20:48:19

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

阶段Cookie将根据其性质到期。如果您真的想要,您可以将它们手动存储在钥匙串中。我更喜欢Keychain保存在UserDefaults或归档中,因为Cookie更像是用户的密码一样安全。

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

夫特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) 
      } 
     } 
    } 
} 
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) 
相关问题