iOS 使用Keychain 保存 用户名和密码到 本地

之前曾把一些简单的数据保存在了plist,文件,及NsuserDefault里面,

但是如果要保存密码之类的,保存在本地就很不安全了;

但是利用 Keychain 我们可以很安全的把 用户密码等信息保存在本地

1:使用苹果官方封装好的Keychain操作类

   GenericKeychain

2:使用 开源的keychain操作

   sskeychain

#import <XCTest/XCTest.h>
#import "SSKeychain.h"

static NSString *const kSSKeychainServiceName = @"SSToolkitTestService";
static NSString *const kSSKeychainAccountName = @"SSToolkitTestAccount";
static NSString *const kSSKeychainPassword = @"SSToolkitTestPassword";
static NSString *const kSSKeychainLabel = @"SSToolkitLabel";

@interface SSKeychainTests : XCTestCase
@end

@implementation SSKeychainTests

- (void)testNewItem {
    // New item
    SSKeychainQuery *query = [[SSKeychainQuery alloc] init];
    query.password = kSSKeychainPassword;
    query.service = kSSKeychainServiceName;
    query.account = kSSKeychainAccountName;
    query.label = kSSKeychainLabel;

    NSError *error;
    XCTAssertTrue([query save:&error], @"Unable to save item: %@", error);

    // Look up
    query = [[SSKeychainQuery alloc] init];
    query.service = kSSKeychainServiceName;
    query.account = kSSKeychainAccountName;
    query.password = nil;

    XCTAssertTrue([query fetch:&error], @"Unable to fetch keychain item: %@", error);
    XCTAssertEqualObjects(query.password, kSSKeychainPassword, @"Passwords were not equal");

    // Search for all accounts
    query = [[SSKeychainQuery alloc] init];
    NSArray *accounts = [query fetchAll:&error];
    XCTAssertNotNil(accounts, @"Unable to fetch accounts: %@", error);
    XCTAssertTrue([self _accounts:accounts containsAccountWithName:kSSKeychainAccountName], @"Matching account was not returned");

    // Check accounts for service
    query.service = kSSKeychainServiceName;
    accounts = [query fetchAll:&error];
    XCTAssertNotNil(accounts, @"Unable to fetch accounts: %@", error);
    XCTAssertTrue([self _accounts:accounts containsAccountWithName:kSSKeychainAccountName], @"Matching account was not returned");

    // Delete
    query = [[SSKeychainQuery alloc] init];
    query.service = kSSKeychainServiceName;
    query.account = kSSKeychainAccountName;
    XCTAssertTrue([query deleteItem:&error], @"Unable to delete password: %@", error);
}


- (void)testPasswordObject {
    SSKeychainQuery *query = [[SSKeychainQuery alloc] init];
    query.service = kSSKeychainServiceName;
    query.account = kSSKeychainAccountName;

    NSDictionary *dictionary = @{@"number": @42, @"string": @"Hello World"};
    query.passwordObject = dictionary;

    NSError *error;
    XCTAssertTrue([query save:&error], @"Unable to save item: %@", error);

    query = [[SSKeychainQuery alloc] init];
    query.service = kSSKeychainServiceName;
    query.account = kSSKeychainAccountName;
    query.passwordObject = nil;
    XCTAssertTrue([query fetch:&error], @"Unable to fetch keychain item: %@", error);
    XCTAssertEqualObjects(query.passwordObject, dictionary, @"Passwords were not equal");
}

- (void)testMissingInformation {
    SSKeychainQuery *query = [[SSKeychainQuery alloc] init];
    query.service = kSSKeychainServiceName;
    query.account = kSSKeychainAccountName;

    NSError *error;
    XCTAssertFalse([query save:&error], @"Function should return NO as not all needed information is provided: %@", error);
    
    query = [[SSKeychainQuery alloc] init];
    query.password = kSSKeychainPassword;
    query.account = kSSKeychainAccountName;
    XCTAssertFalse([query save:&error], @"Function should return NO as not all needed information is provided: %@", error);

    query = [[SSKeychainQuery alloc] init];
    query.password = kSSKeychainPassword;
    query.service = kSSKeychainServiceName;
    XCTAssertFalse([query save:&error], @"Function save should return NO if not all needed information is provided: %@", error);
}

- (void)testDeleteWithMissingInformation {
    SSKeychainQuery *query = [[SSKeychainQuery alloc] init];
    query.account = kSSKeychainAccountName;

    NSError *error;
    XCTAssertFalse([query deleteItem:&error], @"Function deleteItem should return NO if not all needed information is provided: %@", error);

    query = [[SSKeychainQuery alloc] init];
    query.service = kSSKeychainServiceName;
    XCTAssertFalse([query deleteItem:&error], @"Function deleteItem should return NO if not all needed information is provided: %@", error);
    
    // check if fetch handels missing information correctly
    query = [[SSKeychainQuery alloc] init];
    query.account = kSSKeychainAccountName;
    XCTAssertFalse([query fetch:&error], @"Function fetch should return NO if not all needed information is provided: %@", error);
    
    query = [[SSKeychainQuery alloc] init];
    query.service = kSSKeychainServiceName;
    XCTAssertFalse([query fetch:&error], @"Function fetch should return NO if not all needed information is provided: %@", error);

    query = [[SSKeychainQuery alloc] init];
    query.service = kSSKeychainServiceName;
    XCTAssertFalse([query fetch:NULL], @"Function fetch should return NO if not all needed information is provided and error is NULL");
}


- (void)testSynchronizable {
    SSKeychainQuery *query = [[SSKeychainQuery alloc] init];
    query.service = kSSKeychainServiceName;
    query.account = kSSKeychainAccountName;
    query.password = kSSKeychainPassword;
    query.synchronizationMode = SSKeychainQuerySynchronizationModeYes;

    NSError *error;
    XCTAssertTrue([query save:&error], @"Unable to save item: %@", error);

    query = [[SSKeychainQuery alloc] init];
    query.service = kSSKeychainServiceName;
    query.account = kSSKeychainAccountName;
    query.password = nil;
    query.synchronizationMode = SSKeychainQuerySynchronizationModeNo;
    XCTAssertFalse([query fetch:&error], @"Fetch should fail when trying to fetch an unsynced password that was saved as synced: %@", error);
    XCTAssertFalse([query fetch:NULL], @"Fetch should fail when trying to fetch an unsynced password that was saved as synced. error == NULL");

    XCTAssertNotEqualObjects(query.password, kSSKeychainPassword, @"Passwords should not be equal when trying to fetch an unsynced password that was saved as synced.");
  
    query = [[SSKeychainQuery alloc] init];
    query.service = kSSKeychainServiceName;
    query.account = kSSKeychainAccountName;
    query.password = nil;
    query.synchronizationMode = SSKeychainQuerySynchronizationModeAny;
    XCTAssertTrue([query fetch:&error], @"Unable to fetch keychain item: %@", error);
    XCTAssertEqualObjects(query.password, kSSKeychainPassword, @"Passwords were not equal");
}


// Test Class Methods of SSKeychain
- (void)testSSKeychain {
    NSError *error = nil;
    
    // create a new keychain item
    XCTAssertTrue([SSKeychain setPassword:kSSKeychainPassword forService:kSSKeychainServiceName account:kSSKeychainAccountName error:&error], @"Unable to save item: %@", error);
    
    // check password
    XCTAssertEqualObjects([SSKeychain passwordForService:kSSKeychainServiceName account:kSSKeychainAccountName], kSSKeychainPassword, @"Passwords were not equal");
    
    // check all accounts
    XCTAssertTrue([self _accounts:[SSKeychain allAccounts] containsAccountWithName:kSSKeychainAccountName], @"Matching account was not returned");
    // check account
    XCTAssertTrue([self _accounts:[SSKeychain accountsForService:kSSKeychainServiceName] containsAccountWithName:kSSKeychainAccountName], @"Matching account was not returned");
    
    // delete password
    XCTAssertTrue([SSKeychain deletePasswordForService:kSSKeychainServiceName account:kSSKeychainAccountName error:&error], @"Unable to delete password: %@", error);
    
    // set password and delete it without error function
    XCTAssertTrue([SSKeychain setPassword:kSSKeychainPassword forService:kSSKeychainServiceName account:kSSKeychainAccountName], @"Unable to save item");
    XCTAssertTrue([SSKeychain deletePasswordForService:kSSKeychainServiceName account:kSSKeychainAccountName], @"Unable to delete password");
    
#if __IPHONE_4_0 && TARGET_OS_IPHONE
    [SSKeychain setAccessibilityType:kSecAttrAccessibleWhenUnlockedThisDeviceOnly];
    XCTAssertTrue([SSKeychain accessibilityType] == kSecAttrAccessibleWhenUnlockedThisDeviceOnly, @"Unable to verify accessibilityType");
#endif
}


#pragma mark - Private

- (BOOL)_accounts:(NSArray *)accounts containsAccountWithName:(NSString *)name {
    for (NSDictionary *dictionary in accounts) {
        if ([[dictionary objectForKey:@"acct"] isEqualToString:name]) {
            return YES;
        }
    }
    return NO;
}

@end
View Code

相关文章: