2016-05-16 44 views
6

我使用ios鑰匙串(keychainItemWrapper/SSKeychain)來存儲我的應用程序的登錄令牌並保持登錄狀態。目前,我在包含我的令牌,令牌過期和刷新令牌的鑰匙串中存儲簡單的NSDictionary。我將它序列化爲NSData並使用kSecValueData進行存儲。我還設置了kSecAttrAccountkSecAttrService,但不要將這些用於身份驗證。ios在KeyChain中存儲登錄令牌無法檢索,很少和隨機,但始終如一

這個工程的時候很大,約95%。問題在於,隨機地,不可預知地和偶爾地,當我請求它檢索令牌時,鑰匙串不返回數據。通常在離開應用程序一段適中的時間後,重新打開它。它不一定是來自後臺,或者經過任何特定的延遲。

它在下面要求我的NSData並返回<>而不是<ABCD EFGH IJKL ....>時特別失敗。我認爲這是零。因此,代碼認爲用戶沒有登錄並立即將其放在我的應用程序的註冊/登錄登錄頁面上,沒有註銷錯誤,令牌過期錯誤等。如果我最小化應用程序,然後重新打開,它幾乎總是得到正確的鑰匙串信息和用戶再次登錄。

的時候遇到這將創建一個令人困惑的經歷。這也意味着用戶無法保持這種真正的100%登錄狀態,偶爾會被隨機註銷。我一直無法預測它或調試它,並改變鑰匙串庫,如下所示,並沒有爲我解決它。它發生在我和幾個TestFlight用戶以及我們的產品應用程序當前。

任何建議如何維持的時間鑰匙扣完整性和加載100%?我們準備在令牌上實現NSUserDefaults備份存儲以用於這些情況,這是我真的不想要存儲身份驗證令牌的。

儲存:

// load keychain 
KeychainItemWrapper *keychainItem = [KeychainItemWrapper keyChainWrapperForKeyID:kcIdentifier]; 
NSString *firstLaunch = [keychainItem objectForKey: (__bridge id)(kSecAttrAccount)]; 
if (firstLaunch == nil){ 
    // initialize if needed 
    [keychainItem setObject:email forKey: (__bridge id)(kSecAttrAccount)]; 
    [keychainItem setObject:kcIdentifier forKey: (__bridge id)kSecAttrService]; 
    [keychainItem setObject:(id)kSecAttrAccessibleAfterFirstUnlock forKey:(id)kSecAttrAccessible]; 
} 

// serialize "auth" NSDictionary into NSData and store 
NSString *error; 
NSData *dictionaryData = [NSPropertyListSerialization dataFromPropertyList:auth format:NSPropertyListXMLFormat_v1_0 errorDescription:&error]; 
[keychainItem setObject:dictionaryData forKey:(id)kSecValueData]; 

加載:

// after similar KeychainItemWrapper initialization as above 
NSData *dictionaryData = [keychainItem objectForKey:(id)kSecValueData]; 
NSString *error; 

NSDictionary *auth = [NSPropertyListSerialization propertyListFromData:dictionaryData mutabilityOption:NSPropertyListImmutable format:nil errorDescription:&error]; 
NSString *token = auth[@"access_token"]; 

我也使用SSKeychain庫CocoaPod就是廣泛使用,和周圍的鑰匙鏈邏輯的包裝嘗試。這是一個更清潔的訪問,但沒有同樣的問題。在這裏,我只是存儲NSString值,因爲沒有直接的方式將NSData存儲在lib中。

// store in keychain 
[SSKeychain setAccessibilityType:kSecAttrAccessibleAfterFirstUnlock]; 
[SSKeychain setPassword:auth[@"access_token"] forService:SSKEYCHAIN_SERVICE account:SSKEYCHAIN_TOKEN]; 
[SSKeychain setPassword:auth[@"expires_at"] forService:SSKEYCHAIN_SERVICE account:SSKEYCHAIN_EXPIRES_AT]; 
[SSKeychain setPassword:auth[@"refresh_token"] forService:SSKEYCHAIN_SERVICE account:SSKEYCHAIN_REFRESH_TOKEN]; 

// load from keychain 
[SSKeychain setAccessibilityType:kSecAttrAccessibleAfterFirstUnlock]; 
NSString *token = [SSKeychain passwordForService:SSKEYCHAIN_SERVICE account:SSKEYCHAIN_TOKEN]; 
NSString *expires_at = [SSKeychain passwordForService:SSKEYCHAIN_SERVICE account:SSKEYCHAIN_EXPIRES_AT]; 
NSString *refresh_token = [SSKeychain passwordForService:SSKEYCHAIN_SERVICE account:SSKEYCHAIN_REFRESH_TOKEN]; 
+0

如果鑰匙串失敗,您應該得到一個錯誤代碼。它是什麼? – Segev

+0

我有同樣的問題,其中檢索隨機失敗。這裏有一個輔助方法 - 鑰匙串錯誤代碼字符串:https://gist.github.com/inorganik/f9971e65f71e037650b39b5f182e157e – inorganik

+0

我會嘗試記錄錯誤。它可能發生在我使用的庫內部,它不暴露&錯誤參數。 – Miro

回答

3

鑰匙串目前確實存在問題,並且確實有一段時間。這聽起來就像通常在打破一種力量時一樣輕鬆 - 要退出應用程序才能恢復生機。

有一兩件事可以幫助是隻訪問一次的鑰匙串上的第一個請求,然後將結果緩存在內存中,如果它已經在內存中,然後剛剛從那裏返回。

如果發生此情況,然後陷阱並重試,或如一些不幸的應用程序的當前情況下,你可以觀察到一個特定的錯誤,終止該應用。殺死應用程序實際上是蘋果當前的指導,如果你籌集技術票據與他們討論問題。

唯一的其他的解決方法就是對數據進行加密,並將其存儲在一個文件中,但你必須用加密密鑰的問題,所以這是不是對敏銳的攻擊者混淆更好一點。

+0

是鑰匙串,通常在多個線程中是線程安全的,或者我應該擔心@synchronizing接入點?我似乎記得閱讀它已被處理,至少可以通過SSKeychain處理。 – Miro

+0

它本身是線程安全的,SSKeychain更多的是一種數據類型和效率封裝 – Wain