目前我们存储账号密码,一般存在 偏好设置表里,如果明文存储,安全上又有很大隐患,所以今天给大家推荐一种更安全的密码存储方式。
不知道大家是否注意过,当我们使用百度系列的产品时,比如说:我登录上了百度糯米app后,然后我又下载了一个百度云盘,当我打开百度云盘app的时候,我居然自动登录了!这里就是用到了keyChain来保存账号密码,并通过应用组的方式在 应用间共享一套账户密码。
iOS设备中的keyChain是一个安全的存储容器,可以用来为不同应用保存敏感信息(用户名,密码,网络密码等)。同时,keyChain是一个相对独立的空间,当应用替换或删除时并不会删除keyChain的内容,目前看来,使用keyChain来保存用户名和用户密码是最优的解决方案。
本例本着简单易用的原则,提供了一些利用keyChain对账号信息进行增、删、改、查的功能。
先看一下使用:
#import "ViewController.h"
#import "GZKeyChain.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//保存或更新
[GZKeyChain saveUserName:@"张三" pwd:@"asdfghjkl"];
[GZKeyChain saveUserName:@"lisi" pwd:@"asrtyui"];
[GZKeyChain saveUserName:@"张三" pwd:@"rtghnm,loi"];//会覆盖先前密码
//查询所有用户的账号密码
NSDictionary *dic = [GZKeyChain loadAccountInfo];
NSLog(@"dic = %@",dic);
//查询某一用户的账号密码
NSString *pwd = [GZKeyChain loadPwdForUserName:@"lisi"];
NSLog(@"userPwd = %@",pwd);
//删除某一用户的账号密码
[GZKeyChain removeForUserName:@"张三"];
NSLog(@"dic1 = %@",[GZKeyChain loadAccountInfo]);
//删除所有用户的账号密码
[GZKeyChain removeAll];
NSLog(@"dic2 = %@",[GZKeyChain loadAccountInfo]);
}
@end
工具实现
// 需要引入 Security 框架
.h
#import <Foundation/Foundation.h>
#import <Security/Security.h>
@interface GZKeyChain : NSObject
/**
保存或更新 用户名、密码
*/
+ (void)saveUserName:(NSString *)userName pwd:(NSString *)pwd;
+ (void)saveUserName:(NSString *)userName pwd:(NSString *)pwd service:(NSString *)service;
/**
加载所有 用户名、密码 信息 。 key:用户名, value:密码
*/
+ (NSDictionary *)loadAccountInfo;
+ (NSDictionary *)loadAccountInfoWithService:(NSString *)service;
/**
加载某一用户的密码
*/
+ (NSString *)loadPwdForUserName:(NSString *)userName;
+ (NSString *)loadPwdForUserName:(NSString *)userName service:(NSString *)service;
/**
移除所有账号信息
*/
+ (void)removeAll;
+ (void)removeAllWithService:(NSString *)service;
/**
移除某一账号的密码信息
*/
+ (void)removeForUserName:(NSString *)userName;
+ (void)removeForUserName:(NSString *)userName service:(NSString *)service;
@end
.m
#import "GZKeyChain.h"
@implementation GZKeyChain
+ (NSString *)defaultService
{
return [[NSBundle mainBundle] bundleIdentifier] ? : @"";
}
+ (NSMutableDictionary *)getKeychainQuery:(NSString *)service
{
if (!service) service = self.defaultService;
return [NSMutableDictionary dictionaryWithObjectsAndKeys:
(id)kSecClassGenericPassword,(id)kSecClass,
service, (id)kSecAttrService,
service, (id)kSecAttrAccount,
(id)kSecAttrAccessibleAfterFirstUnlock,(id)kSecAttrAccessible,
nil];
}
#pragma mark 写入
+ (void)saveUserName:(NSString *)userName pwd:(NSString *)pwd
{
[self saveUserName:userName pwd:pwd service:nil];
}
+ (void)saveUserName:(NSString *)userName pwd:(NSString *)pwd service:(NSString *)service
{
if (![userName isKindOfClass:[NSString class]]) return;
if (!userName || !pwd) return;
NSMutableDictionary *query = [self getKeychainQuery:service];
NSDictionary *results = [self loadAccountInfoWithService:service];
SecItemDelete((CFDictionaryRef)query);
NSMutableDictionary *dataDic = [NSMutableDictionary dictionaryWithDictionary:results ?:@{}];
[dataDic setObject:pwd forKey:userName];
[query setObject:[NSKeyedArchiver archivedDataWithRootObject:dataDic] forKey:(id)kSecValueData];
SecItemAdd((CFDictionaryRef)query, NULL);
}
#pragma mark 读取
+ (NSDictionary *)loadAccountInfo
{
return [self loadAccountInfoWithService:nil];
}
+ (NSDictionary *)loadAccountInfoWithService:(NSString *)service {
id ret = nil;
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
[keychainQuery setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData];
[keychainQuery setObject:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit];
CFDataRef keyData = NULL;
if (SecItemCopyMatching((CFDictionaryRef)keychainQuery, (CFTypeRef *)&keyData) == noErr) {
@try {
ret = [NSKeyedUnarchiver unarchiveObjectWithData:(__bridge NSData *)keyData];
} @catch (NSException *e) {
NSLog(@"Unarchive of %@ failed: %@", service, e);
} @finally {
}
}
if (keyData)
CFRelease(keyData);
return ret;
}
+ (NSString *)loadPwdForUserName:(NSString *)userName
{
return [self loadPwdForUserName:userName service:nil];
}
+ (NSString *)loadPwdForUserName:(NSString *)userName service:(NSString *)service
{
if (!userName) return nil;
NSDictionary *results = [self loadAccountInfoWithService:service];
if (![results isKindOfClass:[NSDictionary class]]) return nil;
return [results objectForKey:userName];
}
#pragma mark 删除
+ (void)removeAll
{
[self removeAllWithService:nil];
}
+ (void)removeAllWithService:(NSString *)service
{
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
SecItemDelete((CFDictionaryRef)keychainQuery);
}
+ (void)removeForUserName:(NSString *)userName
{
[self removeForUserName:userName service:nil];
}
+ (void)removeForUserName:(NSString *)userName service:(NSString *)service
{
NSDictionary *results = [self loadAccountInfoWithService:service];
if (!results) return;
NSMutableDictionary *dataDic = [NSMutableDictionary dictionaryWithDictionary:results];
[dataDic removeObjectForKey:userName];
NSMutableDictionary *query = [self getKeychainQuery:service];
SecItemDelete((CFDictionaryRef)query);
[query setObject:[NSKeyedArchiver archivedDataWithRootObject:dataDic] forKey:(id)kSecValueData];
SecItemAdd((CFDictionaryRef)query, NULL);
}
@end
觉得对你有用就给个 star 支持一下吧!详细demo下载
如有问题,欢迎评论交流!