• iOS:iOS开发中用户密码保存位置


    原文来自简书:http://www.jianshu.com/p/4af3b8179136/comments/1294203

    如果要实现自动登录,不必每次打开应用都去登录,我们势必要把密码保存到本地。
    一般我们的操作是:
    每次打开应用后,如果存在密码,直接进入界面,然后再进行后台密码验证。如果没网络,我们可以跳过验证;如果有网络,我们可以后台去验证帐号密码的正确性,并根据服务器的response做一些操作。

    为什么直接把密码存储在NSUserDefaults中不安全?

     iOS中沙盒有哪几个文件夹,都是用来干吗的

    默认情况下,每个沙盒含有3个文件夹:Documents, Library 和 tmp。因为应用的沙盒机制,应用只能在几个目录下读写文件

    • Documents:苹果建议将程序中建立的或在程序中浏览到的文件数据保存在该目录下,iTunes备份和恢复的时候会包括此目录
    • Library:存储程序的默认设置或其它状态信息;
    • Library/Caches:存放缓存文件,iTunes不会备份此目录,此目录下文件不会在应用退出删除
    • tmp:提供一个即时创建临时文件的地方。

    获取到沙盒Library路径

    复制代码
    //获取Library目录路径
    -  (void)getLibraryPath
     {
        NSArray * pathArray = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,NSUserDomainMask, YES);
       NSString * libraryStrPath = [pathArray objectAtIndex:0];
        NSLog(@"LibraryPath:%@“,libraryStrPath);
     }
    复制代码

    如图就是NSUserDefaults对应的plist文件在sandbox中的位置

                    蓝色部分为plist文件

    如果sandbox被破解,或者你的手机被越狱,那么就能轻松拿到这个文件。
    那么就能轻松读到存储的信息,密码就会不安全:

    如何删除NSUserDefaults对应的plist文件?

    其实就是删除plist文件中所有的键值对。

    NSUserDefaults *userDefatluts = [NSUserDefaults standardUserDefaults];
    NSDictionary *dictionary = [userDefaults dictionaryRepresentation];
    for(NSString* key in [dictionary allKeys]){
         [userDefaults removeObjectForKey:key];
         [userDefaults synchronize];
    }

    如何解决“直接把密码存储在NSUserDefaults中不安全”的问题?

    把密码加密后再存储到NSUserDefaults中

    iOS中提供了很多种加密算法,对于存储密码,可以使用不可逆的MD5加密。
    使用MD5加密需要导入头文件:
    ''#import <CommonCrypto/CommonDigest.h>

    复制代码
    ##### 简单的MD5加密
    + ( NSString *)md5String:( NSString *)str
    
    {
    
    const char *myPasswd = [str UTF8String ];
    
    unsigned char mdc[ 16 ];
    
    CC_MD5 (myPasswd, ( CC_LONG ) strlen (myPasswd), mdc);
    
    NSMutableString *md5String = [ NSMutableString string ];
    
    for ( int i = 0 ; i< 16 ; i++) {
    
    [md5String appendFormat : @"%02x" ,mdc[i]];
    
    }
    
    return md5String;
    
    }
    
    ##### 复杂一些的MD5加密
    + ( NSString *)md5String:( NSString *)str
    
     {
    
     const char *myPasswd = [str UTF8String ];
    
     unsigned char mdc[ 16 ];
    
     CC_MD5 (myPasswd, ( CC_LONG ) strlen (myPasswd), mdc);
    
     NSMutableString *md5String = [ NSMutableString string ];
    
     [md5String appendFormat : @"%02x" ,mdc[ 0 ]];
    
     for ( int i = 1 ; i< 16 ; i++) {
    
     [md5String appendFormat : @"%02x" ,mdc[i]^mdc[ 0 ]];
    复制代码

    不使用NSUserDefaults保存密码,使用keyChain来保存密码

    更加保险的方法是把密码保存在iOS提供的keychina中,并且删除应用后,密码不会删除,下载安装还能使用。iOS系统提供了一些方法,进行一些简单的封装之后,就可以很方便的使用。

     

    Github-chenhuaizhe-iOS-keychain
    你也可以在这里直接下载,更多交流可以关注我的微博:@陈怀哲

    下面是封装代码,使用时需要先导入Security.framework:

    PassWordTool.h

    复制代码
    #import <Foundation/Foundation.h>
    
    @interface PassWordTool : NSObject
    /**
     *    @brief    存储密码
     *
     *    @param     password     密码内容
     */
    +(void)savePassWord:(NSString *)password;
    
    /**
     *    @brief    读取密码
     *
     *    @return    密码内容
     */
    +(id)readPassWord;
    
    /**
     *    @brief    删除密码数据
     */
    +(void)deletePassWord;
    
    @end
    复制代码

    PassWordTool.m

    复制代码
    import "PassWordTool.h"
     import "KeychainTool.h"
    
     @implementation PassWordTool
     static NSString * const KEY_IN_KEYCHAIN = @"com.chenyuan.app.userid";
     static NSString * const KEY_PASSWORD = @"com.chenyuan.app.password";
    
    +(void)savePassWord:(NSString *)password
     {
         NSMutableDictionary *usernamepasswordKVPairs = [NSMutableDictionary dictionary];
         [usernamepasswordKVPairs setObject:password forKey:KEY_PASSWORD];
         [KeychainTool save:KEY_IN_KEYCHAIN data:usernamepasswordKVPairs];
     }
    
    +(id)readPassWord
     {
         NSMutableDictionary *usernamepasswordKVPair = (NSMutableDictionary *)[KeychainTool load:KEY_IN_KEYCHAIN];
         return [usernamepasswordKVPair objectForKey:KEY_PASSWORD];
     }
    
    +(void)deletePassWord
     {
         [KeychainTool delete:KEY_IN_KEYCHAIN];
     }
     @end
    复制代码

    KeychainTool.h

    复制代码
    #import <Foundation/Foundation.h>
    
    @interface KeychainTool : NSObject
    
    + (NSMutableDictionary *)getKeychainQuery:(NSString *)service ;
    
    + (void)save:(NSString *)service data:(id)data ;
    
    + (id)load:(NSString *)service ;
    
    + (void)delete:(NSString *)service ;
    
    @end
    复制代码

    KeychainTool.m

    复制代码
    ```
    # import "KeychainTool.h"
    
     @implementation KeychainTool
    + (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
    复制代码

    服务器密码验证登录请求

    验证请求时,最好是不直接把明文密码包含在请求里面。
    可以根据一系列字符串生成MD5加密后的签名,根据user-id 和 签名来验证登录。
    比如:

    NSString *sourceStr = [NSString stringWithFormat:@"attach=iOS&chartset=utf-8&format=json&partner=google&userid=%@&password=%@”,userid,password];
    NSString *signStr = [NSString md5String:sourceStr];

    这样得到的signStr和userid再作为网络请求的参数传给服务器做验证。

    其他参考:

    ios 利用钥匙串保存密码和获取密码

    直接使用Security框架读写钥匙串,参考:http://useyourloaf.com/blog/2010/03/29/simple-iphone-keychain-access.html
     
    我们使用第三方类SFHFKeychainUtils来操作钥匙串 ( GitHub代码下载 )

    使用方法如下:

    1、引入Security.framework框架。

    2、引入头文件:#import"SFHFKeychainUtils.h"

    3、存密码:   

       NSString *SERVICE_NAME=@"demo";
        [SFHFKeychainUtils storeUsername:@"dd"
                             andPassword:@"aa"
                          forServiceName:SERVICE_NAME
                          updateExisting:1
                                   error:nil];

    4、取密码:

        NSString *passWord =  [SFHFKeychainUtils getPasswordForUsername:@"dd"
                                                         andServiceName:SERVICE_NAME
                                                                  error:nil];
        NSLog(@"%@",passWord);

    5、删除用户:

    [SFHFKeychainUtils deleteItemForUsername:@"dd" andServiceName:SERVICE_NAME error:nil];
    对上述的代码,有任何疑问,可以在下方留言。 也可以给我发邮件咨询:673658917@qq.com 或者是直接加qq:673658917 转载请注明出处,谢谢合作。 睡觉舒服,那是给死人准备的,加油吧,一年后你会感谢现在的自己的。
  • 相关阅读:
    头指针与头结点的异同
    C:Users用户名AppData里面的文件可以删除吗
    mac os x在PC上安装
    迷宫问题
    对称自反传递
    判断
    离散实验模板
    离散实验二
    tp.5.0.21抛出错误
    php复制整个文件夹,此方法可过滤掉.svn文件夹
  • 原文地址:https://www.cnblogs.com/lishanshan/p/5366593.html
Copyright © 2020-2023  润新知