SSKeychain簡單實戰(zhàn)-獲取唯一標(biāo)識符和密碼自動填充功能

前段時間由于項目進度緊, 一直沒有倒開空來照顧豺狼的簡書, 寫博客是學(xué)習(xí)的一個非常好的途徑, 豺狼的技術(shù)并不好, 所以博客內(nèi)容也沒有特別高端, 只是把一些學(xué)到的知識在分享的過程中整合吸收而已, 能幫到各位看官當(dāng)然就更好了...

日常項目中我們總會遇到獲取DeviceId的需求, 比如定點推送消息, 未登錄收藏等功能, 這里吐槽下iOS對比安卓繁瑣的獲取方法,Keychain具體的解釋就不贅述了, 先摘錄一下網(wǎng)上關(guān)于獲取設(shè)備唯一標(biāo)識符的方法:

iOS中獲取設(shè)備唯一標(biāo)示符的方法一直隨版本的更新而變化。iOS 2.0版本以后UIDevice提供一個獲取設(shè)備唯一標(biāo)識符的方法uniqueIdentifier,通過該方法我們可以獲取設(shè)備的序列號,這個也是目前為止唯一可以確認唯一的標(biāo)示符。好景不長,因為該唯一標(biāo)識符與手機一一對應(yīng),蘋果覺得可能會泄露用戶隱私,所以在 iOS 5.0之后該方法就被廢棄掉了;iOS 6.0系統(tǒng)新增了兩個用于替換uniqueIdentifier的接口,分別是:identifierForVendor,advertisingIdentifier,但這兩個接口會在應(yīng)用重新安裝時改變數(shù)值,并不是唯一的標(biāo)示符,所以開發(fā)者改為使用WiFi的mac地址來取代;iOS 7中蘋果又封殺mac地址,所以開發(fā)者再次改變思路使用KeyChain來保存獲取到的UDID,這樣以后即使APP刪了再裝回來,也可以從KeyChain中讀取回來。

由此我們知道在iOS7之后的策略是將每次重裝都會改變的UUID保存到Keychain中, 之后每次使用就從Keychain中取出來, 由于Keychain不會隨著APP的刪除而清空, 所以達到了唯一標(biāo)識符的作用. 腦洞大開下, 會不會出現(xiàn)超小幾率的UUID重復(fù)呢?
同樣的原理, 某些APP在卸載重裝之后依然可以記住你的密碼就是這樣實現(xiàn)的, 比如招商銀行的手機客戶端非廣告, 我猜想就是根據(jù)唯一標(biāo)識符來記錄具體的圖像密碼實現(xiàn)的, 而一些APP的密碼自動填充功能也是同樣的實現(xiàn)方法. 理論說到這里, 具體的還是要到實踐中去體驗.

-->Demo<--

首先豺狼看了下蘋果關(guān)于Keychain的Demo, 發(fā)現(xiàn)竟然是MRC的代碼, 有警告聽說還有內(nèi)存泄露...現(xiàn)在向大家推薦良心三方庫Sam Soffes的SSKeychain, SSKeychain的功能很簡單, 想要詳細了解的可以看SSKeychain Documentation.

將SSKeychain導(dǎo)入工程后, 需要添加Security.framework支持庫

Security.framework

然后在TARGETS-->Capabilities-->Keychain Sharing打開, 會提示登錄開發(fā)者賬號, 選擇證書, 打開之后會生成一個.entitlements文件

.entitlements

其中的AppIdentifierPrefix是的你的App IDs的Prefix

AppIdentifierPrefix

這樣準(zhǔn)備工作就完成了, 之后我們把UI設(shè)計好, 具體功能如下圖. label用來顯示設(shè)備唯一標(biāo)識符, 刪除APP之后并不會改變. 第一次輸入用戶名和密碼之后, 再次輸入用戶名會自動填充密碼. 點擊清空按鈕, 會把賬號對應(yīng)的本地密碼刪除. 點擊查詢會在控制臺輸出APP內(nèi)保存的所有賬號信息.

UI

關(guān)于SSKeychain, 這里用到了如下幾個方法:

// 保存對應(yīng)賬戶密碼
+ (BOOL)setPassword:(NSString *)password forService:(NSString *)serviceName account:(NSString *)account;
// 提取對應(yīng)賬戶密碼
+ (NSString *)passwordForService:(NSString *)serviceName account:(NSString *)account;
// 刪除對應(yīng)賬戶密碼
+ (BOOL)deletePasswordForService:(NSString *)serviceName account:(NSString *)account;
// 獲取APP下所有賬戶
+ (NSArray *)accountsForService:(NSString *)serviceName;

詳細的代碼貼出來, 推薦下載Demo跑起來看看, 并不難, 都是對Keychain的最基本的用法

#import "ViewController.h"
#import "SSKeychain.h"

static NSString *kKeychainService = @"com.xuhaoran.keychaindemo";
static NSString *kKeychainDeviceId    = @"KeychainDeviceId";

@interface ViewController () <UITextFieldDelegate>
@property (weak, nonatomic) IBOutlet UITextField *accountTextField;
@property (weak, nonatomic) IBOutlet UITextField *passwordTextField;
@property (weak, nonatomic) IBOutlet UILabel *uuidLabel;
@end

@implementation ViewController

#pragma mark - life cycle
- (void)viewDidLoad {
    [super viewDidLoad];
    self.uuidLabel.text = [NSString stringWithFormat:@"設(shè)備號:\n%@", [self getDeviceId]];
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    [self.accountTextField resignFirstResponder];
    [self.passwordTextField resignFirstResponder];
}

#pragma mark - UITextFieldDelegate
- (void)textFieldDidBeginEditing:(UITextField *)textField {
    if (textField == self.accountTextField) {
        self.accountTextField.text = nil;
        self.passwordTextField.text = nil;
    }
}

- (BOOL)textFieldShouldReturn:(UITextField *)textField {
    if (textField == self.accountTextField) {
        [self.passwordTextField becomeFirstResponder];
    }
    else if (textField == self.passwordTextField) {
        [self loginAction:nil];
    }
    return YES;
}

- (void)textFieldDidEndEditing:(UITextField *)textField {
    if (textField == self.accountTextField) {
        // 提取本地密碼
        NSString *localPassword = [SSKeychain passwordForService:kKeychainService account:textField.text];
        if (localPassword) {
            self.passwordTextField.text = localPassword;
        }
    }
}

#pragma mark - responce action
- (IBAction)loginAction:(id)sender {
    if (!self.accountTextField.text || !self.passwordTextField.text) {
        [self showMsg:@"輸入賬號和密碼!"];
        return;
    }
    [self showMsg:[NSString stringWithFormat:@"賬戶名:%@\n密碼:%@", self.accountTextField.text, self.passwordTextField.text]];
    // 保存賬號密碼
    [SSKeychain setPassword:self.passwordTextField.text forService:kKeychainService account:self.accountTextField.text];
}

- (IBAction)clearAction:(id)sender {
    if (!self.accountTextField.text) {
        return;
    }
    if ([SSKeychain deletePasswordForService:kKeychainService account:self.accountTextField.text]) {
        [self showMsg:[NSString stringWithFormat:@"賬戶%@的密碼已清空!", self.accountTextField.text]];
        self.passwordTextField.text = nil;
    }
    else {
        [self showMsg:@"刪除失敗了"];
    }
}

- (IBAction)searchAllAction:(id)sender {
    NSArray *accounts = [SSKeychain accountsForService:kKeychainService];
    NSLog(@"accounts:\n%@", accounts);
    [self showMsg:@"看下控制臺輸出"];
}

#pragma mark - private method
- (NSString *)getDeviceId {
    // 讀取設(shè)備號
    NSString *localDeviceId = [SSKeychain passwordForService:kKeychainService account:kKeychainDeviceId];
    if (!localDeviceId) {
        // 保存設(shè)備號
        CFUUIDRef deviceId = CFUUIDCreate(NULL);
        assert(deviceId != NULL);
        CFStringRef deviceIdStr = CFUUIDCreateString(NULL, deviceId);
        [SSKeychain setPassword:[NSString stringWithFormat:@"%@", deviceIdStr] forService:kKeychainService account:kKeychainDeviceId];
        localDeviceId = [NSString stringWithFormat:@"%@", deviceIdStr];
    }
    return localDeviceId;
}

- (void)showMsg:(NSString *)msg {
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Tip" message:msg preferredStyle:UIAlertControllerStyleAlert];
    UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:nil];
    [alert addAction:cancel];
    [self showViewController:alert sender:nil];
}

@end

因為看網(wǎng)上大多都是簡單的方法介紹, 所以豺狼就寫了一個實戰(zhàn)型的Demo, 雖說是實戰(zhàn), 但也都是SSKeychain的基本用法, 希望對各位多有幫助, 如果喜歡請點個贊鼓勵一下豺狼, 謝謝.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容