【问题标题】:iPhone: NSHTTPCookie is not saved across app restartsiPhone:NSHTTPCookie 不会在应用重新启动时保存
【发布时间】:2010-04-18 14:27:38
【问题描述】:

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

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

【问题讨论】:

    标签: iphone cookies


    【解决方案1】:

    您可以通过保存 cookie 的属性字典来保存 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];
    }
    

    【讨论】:

    【解决方案2】:

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

    “您需要设置一个过期日期,否则cookie被假定为仅会话。”

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

    如果您可以控制服务器并且可以要求它在将来将“Expires”标头设置为某些内容,则您不需要在 NSUserDefaults 中存储和恢复 cookie。如果您无法控制服务器或不希望覆盖服务器的行为,则可以通过在其中更改 expiresDate 来“欺骗”您的应用:

    当您重新打开应用时,您会注意到 cookie 并未被删除。

    【讨论】:

    • 从服务器端的安全方法来看,这应该是公认的答案。
    【解决方案3】:

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

    很遗憾,保存 session-only 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)
                }
            }
        }
    }
    

    【讨论】:

      【解决方案4】:

      我相信由服务器来决定 cookie 是否仅限会话,您对此无能为力。

      【讨论】:

      • 不,实际上是客户端(iPhone 应用程序)丢弃了会话 cookie。会话仍然存在于服务器上。
      • 对,但是服务器会告诉 iPhone 应用 cookie 只是会话。
      • 是的,那是通过 HTTP。因此,我正在寻找从服务器发送的正确标头,以告诉 iPhone 应用它可以将 cookie 保留更长时间。
      • 你需要设置一个过期日期,否则cookie被认为是session only。
      • 很多人解释的原因是没有设置有效期。
      【解决方案5】:

      快捷方式

      商店:

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

      【讨论】:

      • 此答案(以纯文本形式存储您的烹饪)不安全。请使用钥匙串,而不是像这个答案那样的 NSUserDefaults。
      【解决方案6】:

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

      【讨论】:

        【解决方案7】:

        SWIFT 3

        保存:

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

        获取:

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

        【讨论】:

          猜你喜欢
          • 2019-09-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多