在我的iPhone应用程序中,我希望能够在我的应用程序重新启动时重用相同的服务器端会话。服务器上的会话由cookie标识,cookie在每个请求上发送。当我重新启动应用程序时,该cookie已经消失,我不能再使用相同的会话了。
当我使用NSHTTPCookieStorage
查找从服务器获取的Cookie时,我注意到[cookie isSessionOnly]
返回YES
。我得到的印象是,这就是为什么不会在重新启动应用时保存Cookie的原因。我需要做些什么才能使我的cookie不仅仅是会话?我必须从服务器发送哪些HTTP标头?
答案 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
来确定您的应用:
[[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]
NSMutableDictionary
,将"Expires"
值更改为将来的日期。[NSHTTPCookie.cookieWithProperties:]
NSMutableDictionary
创建新Cookie
[[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie newCookie]
当您重新打开应用时,您会注意到该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)
}