iOS 資料持久化之KeyChain(Swift Demo),ioskeychain
原創blog,轉載請註明出處
blog.csdn.net/hello_hwc?viewmode=list
前言:前兩篇持久化分別講到了
- NSUserDefaults儲存Settings資訊
- Plist儲存簡單的結構化資訊
本文講解如何儲存需要加密的資訊。絕大多數情況下都是儲存密碼。少數情況下需要儲存認證等資訊。本文以密碼為例,講解如何用iOS SDK原生API來進行KeyChain的操作。
實際開發的過程中,建議使用一些Github的整合庫,或者自己寫一個KeyChain的庫,很簡單
原始碼提供Swift版本,完整工程下載
CSDN下載
http://download.csdn.net/detail/hello_hwc/8663811
GitHub
https://github.com/wenchenhuang/SwiftKeyChainDemo
Demo效果
四個按鍵對應添加,更新,擷取,刪除
Demo的password沒有顯示黑點,是為了方便查看。
四種操作
---Get----
KeyChain簡介
KeyChain是一個加密的容器,通常用來儲存密碼,認證,和一些需要加密的key。對於iOS來說,每個App有獨立的keyChain,每個app只能訪問自己的keyChain.
注意:keyChain的存取權限依賴於provisioning file。所以,如果要在應用程式更新的時候,仍然能夠訪問之前儲存的密碼,要保證provisioning file是同一個檔案。
KeyChain描述
keyChain是通過字典來描述的,是一組key-value的對。用來描述這個keyChain是為什麼樣的應用儲存什麼樣的資料,有什麼樣的存取權限等等。
一個典型的字典
其中
- kSecClass 表示儲存的是密碼
- kSecAttrAccount 表示的是為IamUser這個帳號儲存的密碼
- kSecAttrService 表示是為App Store儲存的帳號
- 其餘兩個在查詢的時候使用,知道如果要查詢都設為ture就可以了
所有的keys可以從以下連結擷取
https://developer.apple.com/library/ios/documentation/Security/Reference/keychainservices/index.html
手把手教你建立Demo App建立一個基於Swift的工程,然後在storyboard上拖拽控制項
並且拖拽outlet和action,然後實現UITextFieldDelegate,保證我們點擊Return的時候,鍵盤會消失。這時候的代碼如下
import Securityclass ViewController: UIViewController,UITextFieldDelegate{ @IBOutlet weak var usernameTextfield: UITextField! @IBOutlet weak var passwordTextField: UITextField! @IBAction func addKeyChainItem(sender: AnyObject) { } @IBAction func updateKeyChainItem(sender: AnyObject) { } @IBAction func getKeyChainItem(sender: AnyObject) { } @IBAction func deleteKeyChainItem(sender: AnyObject) { } override func viewDidLoad() { super.viewDidLoad() usernameTextfield.delegate = self passwordTextField.delegate = self // Do any additional setup after loading the view, typically from a nib. } func textFieldShouldReturn(textField: UITextField) -> Bool { textField.resignFirstResponder() return true }}
然後,添加幾個個輔助方法,減少我們的代碼量
建立預設的描述字典
func createDefaultKeyChainItemDic()->NSMutableDictionary{ var keyChainItem = NSMutableDictionary() keyChainItem.setObject(kSecClassInternetPassword as NSString, forKey: kSecClass as NSString) keyChainItem.setObject("blog.csdn.net/hello_hwc", forKey: kSecAttrServer as NSString) keyChainItem.setObject(self.usernameTextfield.text, forKey: kSecAttrAccount as NSString) return keyChainItem }
用UIAlertController提示資訊
func alertWithMessage(message:String){ var alertController = UIAlertController(title:"Info", message: message, preferredStyle: UIAlertControllerStyle.Alert) alertController.addAction(UIAlertAction(title:"OK", style: UIAlertActionStyle.Cancel, handler:nil)) self.presentViewController(alertController, animated: true, completion: nil) } func alertWithStatus(status:OSStatus){ if(status == 0){ self.alertWithMessage("Success") }else{ self.alertWithMessage("Fail ErrorCode is\(status)") } }
添加KeyChain
- 這裡用函數SecItemCopyMatching來尋找這個keyChain是否存在。兩個參數,第一個是描述字典,第二個是尋找結果拷貝到的字典,通常只有在擷取的時候才會用到,這裡為nil即可。
- kSecValueData這個key是實際要儲存的密碼,要先轉換成NSData
- SecItemAdd這個函數來添加keyChain,傳回值是OSStatus類型,錯誤類型較多,可以Google。這裡知道0是沒有錯誤就可以了。
@IBAction func addKeyChainItem(sender: AnyObject) { var keyChainItem = self.createDefaultKeyChainItemDic() if SecItemCopyMatching(keyChainItem,nil) == noErr{ self.alertWithMessage("User name already exits") }else{ keyChainItem.setObject(self.passwordTextField.text.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion:true)!, forKey: kSecValueData as String) var status = SecItemAdd(keyChainItem, nil) self.alertWithStatus(status) } }
更新KeyChain
SecItemUpdate函數用來更新KeyChain,兩個參數,第一個參數是描述字典,第二個是包含更新資料的字典
@IBAction func updateKeyChainItem(sender: AnyObject) { var keyChainItem = self.createDefaultKeyChainItemDic() if SecItemCopyMatching(keyChainItem,nil) == noErr{ var updateDictionary = NSMutableDictionary() updateDictionary.setObject(self.passwordTextField.text.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion:true)!, forKey:kSecValueData as String) var status = SecItemUpdate(keyChainItem,updateDictionary) self.alertWithStatus(status) }else{ self.alertWithMessage("The keychain doesnot exist") } }
刪除keyChain
SecItemDelete函數用來刪除
@IBAction func deleteKeyChainItem(sender: AnyObject) { var keyChainItem = self.createDefaultKeyChainItemDic() if SecItemCopyMatching(keyChainItem,nil) == noErr{ let status = SecItemDelete(keyChainItem) self.alertWithStatus(status) }else{ self.alertWithMessage("The keychain doesnot exist") } }
擷取KeyChain
SecItemCopyMatching第二個參數包含了擷取到的字典資訊。轉換方式有點複雜。
@IBAction func getKeyChainItem(sender: AnyObject) { var keyChainItem = self.createDefaultKeyChainItemDic() keyChainItem.setObject(kCFBooleanTrue, forKey: kSecReturnData as String) keyChainItem.setObject(kCFBooleanTrue, forKey: kSecReturnAttributes as String) var queryResult: Unmanaged<AnyObject>? let status = SecItemCopyMatching(keyChainItem,&queryResult) let opaque = queryResult?.toOpaque() var contentsOfKeychain: NSString? if let op = opaque { let retrievedData = Unmanaged<NSDictionary>.fromOpaque(op).takeUnretainedValue() let passwordData = retrievedData.objectForKey(kSecValueData) as! NSData let passwordString = NSString(data: passwordData, encoding: NSUTF8StringEncoding)! self.alertWithMessage("Password: \(passwordString)") }else{ self.alertWithMessage("The keychain doesnot exist") } }
總結
簡單來講,一共就是四個函數
歡迎關注我的iOS詳解專欄,這裡我會講解絕大部分iOS常用的技術
http://blog.csdn.net/column/details/huangwenchen-ios-sdk.html
我的部落格iOS部分目錄
http://blog.csdn.net/hello_hwc/article/details/45365385