原文来自简书: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];

iOS:iOS开发中用户密码保存位置的更多相关文章

  1. iOS 应用开发,用户密码存储技术--KeyChain

    文/清雪飘香(简书作者)原文链接:http://www.jianshu.com/p/c41525172aee著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”. 这次的Xcode 事件,让我 ...

  2. 简单讲解iOS应用开发中的MD5加密的相关使用<转>

    这篇文章主要介绍了iOS应用开发中的MD5加密的相关使用,示例代码基于传统的Objective-C,需要的朋友可以参考下 一.简单说明 1.说明 在开发应用的时候,数据的安全性至关重要,而仅仅用POS ...

  3. 简单讲解iOS应用开发中的MD5加密的相关使用

      简单讲解iOS应用开发中的MD5加密的相关使用   作者:文顶顶 字体:[增加 减小] 类型:转载 时间:2015-12-19 我要评论 这篇文章主要介绍了iOS应用开发中的MD5加密的相关使用, ...

  4. Subversion——密码保存位置

    Subversion——密码保存位置 摘要:本文主要说明了Subversion在电脑上保存密码的位置. 起因 在向本地电脑上的文件夹里下载程序代码的时候,发现输入了地址之后就能直接下载了,并没有提示输 ...

  5. iOS项目开发中的知识点与问题收集整理①(Part 一)

    前言部分 注:本文并非绝对原创 大部分内容摘自 http://blog.csdn.net/hengshujiyi/article/details/20943045 文中有些方法可能已过时并不适用于现在 ...

  6. iOS项目开发中的知识点与问题收集整理①

    前言部分 注:本文并非绝对原创 大部分内容摘自 http://blog.csdn.net/hengshujiyi/article/details/20943045 文中有些方法可能已过时并不适用于现在 ...

  7. IOS程序开发中-跳转到 发送短信界面 实现发短信

    前言:我发现我标题取的不好,谁帮我取个承接上下文的标题?评论一下,我改 项目需求:在程序开发中,我们需要在某个程序里面发送一些短信验证(不是接收短信验证,关于短信验证,传送门:http://www.c ...

  8. Python开发之用户密码存储

    在各种线上应用中,用户名密码是用户身份认证的关键,它的重要性不言而喻.一方面,作为保护用户敏感数据的钥匙来说,一旦被破解,系统将敞开大门完全不设防.另一方面,密码这把钥匙本身就是非常敏感的数据:大多数 ...

  9. node.js中用户密码的加密

    crypro实现用户密码的加密 在实际的项目中,只要涉及到用户的信息,就是十分重要的.设想一下数据库里面存放的用户的密码是明文的形式,后果是有多严重.所以今天给大家分享一下express中怎样实现用户 ...

随机推荐

  1. 动态规划(计数DP):JLOI 2016 成绩比较

    Description G系共有n位同学,M门必修课.这N位同学的编号为0到N-1的整数,其中B神的编号为0号.这M门必修课编号为0到M- 1的整数.一位同学在必修课上可以获得的分数是1到Ui中的一个 ...

  2. HDU-1233 还是畅通工程 (prim 算法求最小生成树)

    prim 算法求最小生成树 还是畅通工程 Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Oth ...

  3. php中的variables_order

    PHP中的$_ENV是一个包含服务器端环境变量的数组.它是PHP中一个超级全局变量,我们可以在PHP 程序的任何地方直接访问它.     $_ENV只是被动的接受服务器端的环境变量并把它们转换为数组元 ...

  4. GPU 的硬件基本概念,Cuda和Opencl名词关系对应

    GPU 的硬件基本概念 Nvidia的版本: 实际上在 nVidia 的 GPU 里,最基本的处理单元是所谓的 SP(Streaming Processor),而一颗 nVidia 的 GPU 里,会 ...

  5. Linux,实时获取磁盘空间

    #include <iostream> #include <stdlib.h> #include <stdio.h> #include <sys/statfs ...

  6. dnspod-sr内网轻量级DNS首选方案 - 运维生存时间

    dnspod-sr内网轻量级DNS首选方案 - 运维生存时间 undefined

  7. 【转】开始使用Mac OS X——写给Mac新人

    写这篇文档的原因有两个,一.身边使用Mac的朋友越来越多,经常会有人来咨询一些常见的使用问题,比如:“为什么把界面右上角的红色按钮叉掉,程序还没有关闭?”之类.而且我也不愿意看到很多人使用OSX时仅仅 ...

  8. 【转】漫谈ANN(2):BP神经网络

    上一次我们讲了M-P模型,它实际上就是对单个神经元的一种建模,还不足以模拟人脑神经系统的功能.由这些人工神经元构建出来的网络,才能够具有学习.联想.记忆和模式识别的能力.BP网络就是一种简单的人工神经 ...

  9. Creating InetAddress object in Java

    I am trying to convert an address specified by an IP number or a name, both in String (i.e. localhos ...

  10. 仿path菜单button的实现

    path刚出来时.其菜单button也算是让大多数人感到了惊艳,如今看来事实上是非常easy的就是动画的结合. watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQ ...