通常情况下,我们用NSUserDefaults存储数据信息,但是对于一些私密信息,比如密码、证书等等,就需要使用更为安全的keychain了。keychain里保存的信息不会因App被删除而丢失,在用户重新安装App后依然有效,数据还在。

使用苹果官方发布的KeychainItemWrapper或者SFHFKeychainUtils很方便,后来看到 iphone使用keychain来存取用户名和密码 一文,觉得对了解keychain有很大的帮助,于是ARC控也尝试了一把。

需要导入Security.framework



ios swift写一个可以自动换行的布局 ios swiftkey_用户名



@implementation WQKeyChain 
   
 + (NSMutableDictionary *)getKeychainQuery:(NSString *)service { 
   
return [NSMutableDictionary dictionaryWithObjectsAndKeys: 
   
         (__bridge_transfer id)kSecClassGenericPassword,(__bridge_transfer id)kSecClass, 
   
         service, (__bridge_transfer id)kSecAttrService, 
   
         service, (__bridge_transfer id)kSecAttrAccount, 
   
         (__bridge_transfer id)kSecAttrAccessibleAfterFirstUnlock,(__bridge_transfer id)kSecAttrAccessible, 
   
         nil]; 
   
 } 
   

 + ( 
   void)save:(NSString *)service data:(id)data { 
   
      
   // 
   Get search dictionary 
   
    NSMutableDictionary *keychainQuery = [self getKeychainQuery:service]; 
   
      
   // 
   Delete old item before add new item 
   
    SecItemDelete((__bridge_retained CFDictionaryRef)keychainQuery); 
   
      
   // 
   Add new object to search dictionary(Attention:the data format) 
   
    [keychainQuery setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(__bridge_transfer id)kSecValueData]; 
   
      
   // 
   Add item to keychain with the search dictionary 
   
    SecItemAdd((__bridge_retained CFDictionaryRef)keychainQuery, NULL); 
   
 } 
   

 + (id)load:(NSString *)service { 
   
     id ret = nil; 
   
     NSMutableDictionary *keychainQuery = [self getKeychainQuery:service]; 
   
      
   // 
   Configure the search setting 
   
    [keychainQuery setObject:(id)kCFBooleanTrue forKey:(__bridge_transfer id)kSecReturnData]; 
   
     [keychainQuery setObject:(__bridge_transfer id)kSecMatchLimitOne forKey:(__bridge_transfer id)kSecMatchLimit]; 
   
     CFDataRef keyData = NULL; 
   
      
   if (SecItemCopyMatching((__bridge_retained CFDictionaryRef)keychainQuery, (CFTypeRef *)&keyData) == noErr) { 
   
         @ 
   try { 
   
             ret = [NSKeyedUnarchiver unarchiveObjectWithData:(__bridge_transfer NSData *)keyData]; 
   
         } @ 
   catch (NSException *e) { 
   
             NSLog(@"Unarchive of %@ failed: %@", service, e); 
   
         } @ 
   finally { 
   
         } 
   
     } 
   
      
   return ret; 
   
 } 
   

 + ( 
   void)delete:(NSString *)service { 
   
     NSMutableDictionary *keychainQuery = [self getKeychainQuery:service]; 
   
     SecItemDelete((__bridge_retained CFDictionaryRef)keychainQuery); 
   
 } 
   
 @end


ios swift写一个可以自动换行的布局 ios swiftkey_用户名



比如,保存密码



ios swift写一个可以自动换行的布局 ios swiftkey_用户名



interface WQUserDataManager : NSObject 
   

/** 
   
  *    @brief    存储密码
  *
  *     
   @param 
        password     密码内容
   
   */ 
   
 +( 
   void)savePassWord:(NSString *)password; 
   

/** 
   
  *    @brief    读取密码
  *
  *     
   @return 
       密码内容
   
   */ 
   
 +(id)readPassWord; 
   

/** 
   
  *    @brief    删除密码数据
   
   */ 
   
 +( 
   void)deletePassWord; 
   

 @end


ios swift写一个可以自动换行的布局 ios swiftkey_用户名



ios swift写一个可以自动换行的布局 ios swiftkey_用户名



" 
   WQUserDataManager.h 
   " 
   

 @implementation WQUserDataManager 
   

static NSString *  
   const KEY_IN_KEYCHAIN =  
   @" 
   com.wuqian.app.allinfo 
   "; 
   
static NSString *  
   const KEY_PASSWORD =  
   @" 
   com.wuqian.app.password 
   "; 
   

 +( 
   void)savePassWord:(NSString *)password 
   
 { 
   
     NSMutableDictionary *usernamepasswordKVPairs = [NSMutableDictionary dictionary]; 
   
     [usernamepasswordKVPairs setObject:password forKey:KEY_PASSWORD]; 
   
     [WQKeyChain save:KEY_IN_KEYCHAIN data:usernamepasswordKVPairs]; 
   
 } 
   

 +(id)readPassWord 
   
 { 
   
     NSMutableDictionary *usernamepasswordKVPair = (NSMutableDictionary *)[WQKeyChain load:KEY_IN_KEYCHAIN]; 
   
      
   return [usernamepasswordKVPair objectForKey:KEY_PASSWORD]; 
   
 } 
   

 +( 
   void)deletePassWord 
   
 { 
   
     [WQKeyChain delete:KEY_IN_KEYCHAIN]; 
   
 } 
   
 @end

ios swift写一个可以自动换行的布局 ios swiftkey_用户名



实现一个简单的界面,把设定的密码存起来,然后立即读取显示出来看看效果



-(IBAction)btnAciton:(id)sender 
   
 { 
   
     [WQUserDataManager savePassWord:self.textfield.text]; 
   
     self.label.text = [WQUserDataManager readPassWord]; 
   
 }



ios swift写一个可以自动换行的布局 ios swiftkey_用户名_07

ios swift写一个可以自动换行的布局 ios swiftkey_App_08





使用keychain保存用户名和密码等敏感信息 KeychainItemWrapper和SFHFKeychainUtils。

iOS的keychain服务提供了一种安全的保存私密信息(密码,序列号,证书等)的方式,每个ios程序都有一个独立的keychain存储。相对于NSUserDefaults、文件保存等一般方式,keychain保存更为安全,而且keychain里保存的信息不会因App被删除而丢失,所以在重装App后,keychain里的数据还能使用。从ios 3。0开始,跨程序分享keychain变得可行。

如何需要在应用里使用使用keyChain,我们需要导入Security.framework ,keychain的操作接口声明在头文件SecItem.h里。直接使用SecItem.h里方法操作keychain,需要写的代码较为复杂,为减轻咱们程序员的开发,我们可以使用一些已经封装好了的工具类,下面我会简单介绍下我用过的两个工具类:KeychainItemWrapper和SFHFKeychainUtils。

KeychainItemWrapper是apple官方例子“GenericKeychain”里一个访问keychain常用操作的封装类,在官网上下载了GenericKeychain项目后,只需要把“KeychainItemWrapper.h”和“KeychainItemWrapper.m”拷贝到我们项目,并导入Security.framework 。KeychainItemWrapper的用法:



/** 初始化一个保存用户帐号的KeychainItemWrapper */
 
     
*wrapper  
      =  
      [ 
      [KeychainItemWrapper alloc 
      ] initWithIdentifier 
      :@ 
      "Account Number"
 
     
:@ 
      "YOUR_APP_ID_HERE.com.yourcompany.AppIdentifier" 
      ] 
      ;
 
     

         
     
 
     
//保存帐号
 
     
[wrapper setObject 
      :@ 
      "<帐号>" forKey 
      : 
      (id 
      )kSecAttrAccount 
      ] 
      ;
 
     

         
     
 
     
//保存密码
 
     
[wrapper setObject 
      :@ 
      "<帐号密码>" forKey 
      : 
      (id 
      )kSecValueData 
      ] 
      ;
 
     

         
     
 
     
//从keychain里取出帐号密码
 
     
*password  
      =  
      [wrapper objectForKey 
      : 
      (id 
      )kSecValueData 
      ] 
      ;
 
     

         
     
 
     
//清空设置
 
     
[wrapper resetKeychainItem 
      ] 
      ;



其中方法“- (void)setObject:(id)inObject forKey:(id)key;”里参数“forKey”的值应该是Security.framework 里头文件“SecItem.h”里定义好的key,用其他字符串做key程序会崩溃!

SFHFKeychainUtils是另一个封装了KeyChain简单操作的第三方类库,使用上比KeychainItemWrapper要简单点,SFHFKeychainUtils只提供了获取、保存和删除三个方法:



<UIKit 
      /UIKit. 
      h 
      >
 
     

         
     
 
     
interface SFHFKeychainUtils  
      : NSObject  
      {
 
     

         
     
 
     
}
 
     

         
     
 
     
/** 从Keychain里获取用户密码
 
     
 *param username 用户名
 
     
 *param serviceName 服务名
 
     
 *return NSString 用户名对应的密码
 
     
 */
 
     
+  
      (NSString  
      * 
      ) getPasswordForUsername 
      :  
      (NSString  
      * 
      ) username andServiceName 
      :  
      (NSString  
      * 
      ) serviceName error 
      :  
      (NSError  
      ** 
      ) error 
      ;
 
     

         
     
 
     
/**
 
     
 *把用户的密码保存到Keychain里
 
     
 *@param username 用户名
 
     
 *@param password 要保存的密码
 
     
 *@param serviceName 本条keychains所属的服务(组)
 
     
 *return BOOL 是否存储成功
 
     
 */
 
     
+  
      (BOOL 
      ) storeUsername 
      :  
      (NSString  
      * 
      ) username andPassword 
      :  
      (NSString  
      * 
      ) password forServiceName 
      :  
      (NSString  
      * 
      ) serviceName updateExisting 
      :  
      (BOOL 
      ) updateExisting error 
      :  
      (NSError  
      ** 
      ) error 
      ;
 
     

         
     
 
     
/**
 
     
 * 删除某个用户信息
 
     
 *param username 用户名
 
     
 *param serviceName 用户所属的服务(组)
 
     
 *return BOOL 是否删除成功
 
     
 */
 
     
+  
      (BOOL 
      ) deleteItemForUsername 
      :  
      (NSString  
      * 
      ) username andServiceName 
      :  
      (NSString  
      * 
      ) serviceName error 
      :  
      (NSError  
      ** 
      ) error 
      ;
 
     

         
     
 
     

       @end



使用方法:



"com.mycompany.yourAppServiceName"
 
     

         
     
 
     
*error 
      ;
 
     
*userName  
      = @ 
      "<用户名>" 
      ;
 
     
*password  
      = @ 
      "<用户密码>" 
      ;
 
     

         
     
 
     
/** 保存用户的密码*/
 
     
=  
      [SFHFKeychainUtils storeUsername 
      :userName 
     
 
     
:password 
     
 
     
:ServiceName 
     
 
     
:YES 
     
 
     
:&error  
      ] 
      ;
 
     
if  
      ( 
      !saved 
      )  
      {
 
     
(@ 
      "保存密码时出错:%@", error 
      ) 
      ;
 
     
}
 
     

         
     
 
     
= nil 
      ;