iPhone: NSHTTPCookie is not saved across app restarts

后端 未结 7 546
梦如初夏
梦如初夏 2020-12-01 05:14

In my iPhone app, I want to be able to reuse the same server-side session when my app restarts. A session on the server is identified by a cookie, which is sent on each requ

相关标签:
7条回答
  • 2020-12-01 05:45

    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)
    
    0 讨论(0)
  • 2020-12-01 05:46

    Session-only cookies will expire by their nature. You can store them manually in Keychain if you really want it. I prefer Keychain to saving in UserDefaults or archiving because cookies are better be secured, just like user's password.

    Unfortunately saving session-only cookies is not very helpful, the code below is just an illustration how to store cookies, but can't force the server to accept such cookies in any way (unless you can control the server).

    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)
                }
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-01 05:47

    Swift way

    Store:

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

    Restore:

    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)
                }
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-01 05:58

    You can save the cookie by saving its properties dictionary and then restoring as a new cookiebefore you go to re-connect.

    Save:

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

    Load:

    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];
    }
    
    0 讨论(0)
  • 2020-12-01 06:01

    Swift 5 version

    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)
    }
    
    0 讨论(0)
  • 2020-12-01 06:03

    I believe it's up to the server to decide whether or not the cookie is session-only, you can't do anything about it.

    0 讨论(0)
提交回复
热议问题