TouchID 登陆

点击这里:KeyChain and TouchID Demo

首先需要引入一个库#import ,官方的一个用于TouchID安全验证登陆的Framework,首先需要验证是否支持TouchID登陆:

// 判断是否支持TouchID登陆
- (BOOL)canEvaluatePolicy
{
    LAContext *context = [[LAContext alloc] init];
    NSError *error;
    return [context canEvaluatePolicy:LAPolicyDeviceOwnerAuthentication error:&error];
}

如果不支持的话,就需要走自己APP中得密码验证方法来进行验证登陆;
如果支持的话,就调起TouchID验证的页面:

- (void)evaluatePolicy
{
    LAContext *context = [[LAContext alloc] init];
    [context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics localizedReason:@"使用TouchID进入APP首页" reply:^(BOOL success, NSError * _Nullable error) {
        dispatch_async(dispatch_get_main_queue(), ^{
            if (success) {
                // ...
            }
            else {
                // ...
            }
        });
    }];
}

注意

reply这个block中得代码是在异步线程中执行的,如果有需要的话,必须得抛到主线程中去执行一些东西。

TouchID的失败次数是有限制的,从开始算,如果失败了三次,系统就会自动的退出TouchID的验证界面,并在replyblock中返回失败的信息,这时还可以调起TouchID的验证,此时如果失败两次即会退出验证界面。
在这种情况下,如果还想用TouchID验证,则必须输入手机的登陆密码才能继续验证。
需要注意的是,这个失败的次数在iOS设备中所有的APP是累加起来的。

KeyChain

keychain是作为苹果中对于数据安全存储的一个地方,类似于NSUserDefaults,但又不同于NSUserDefaults

对于keychain来说,需要注意的方法有以下四个:

  • SecItemAdd
  • SecItemUpdate
  • SecItemCopyMatching
  • SecItemDelete

注意

在使用的时候一定要注意KeyChain操作的返回值。

KeyChain操作中得一些参数的设定(一些相关参数的说明):

- (NSMutableDictionary *)getKeychainQueryWithService:(NSString *)service
{
    NSMutableDictionary *query = [NSMutableDictionary dictionaryWithObjectsAndKeys:
                (__bridge id)kSecClassGenericPassword, (__bridge id)kSecClass,
                service, (__bridge id)kSecAttrService,
                service, (__bridge id)kSecAttrAccount,
                (__bridge id)kSecAttrAccessibleAfterFirstUnlock, (__bridge id)kSecAttrAccessible,
            nil];
    if (self.accessGroupName != nil) {
        [query setObject:self.accessGroupName forKey:(__bridge id)kSecAttrAccessGroup];
    }
    return query;
}

SecItemAdd

用于将个人数据条目保存到keychain中。

  • 方法原型:OSStatus SecItemAdd(CFDictionaryRef attributes, CFTypeRef * result);
    • attributes : 添加属性的设置。
    • result : 可以用与获取返回值,&res
- (OSStatus)saveObject:(id)object forService:(NSString *)service {
    NSMutableDictionary *keychainQuery = [self getKeychainQueryWithService:service];
    // 需要检查数据的存在性,如果存在的话,就用SecItemUpdate更新,否则就添加
    [keychainQuery setObject:[NSKeyedArchiver archivedDataWithRootObject:object] forKey:(__bridge id)kSecValueData];
    return SecItemAdd((__bridge CFDictionaryRef)keychainQuery, NULL);
}

SecItemUpdate

用于更新KeyChain中保存的数据。

  • 方法原型:OSStatus SecItemUpdate(CFDictionaryRef query, CFDictionaryRef attributesToUpdate);
    • query : 查找属性的设置
    • attributesToUpdate : 要更新的数据
- (OSStatus)updateService:(NSString *)service withObject:(id)object
{
    NSDictionary *keychainQuery = [self getKeychainQueryWithService:service];
    NSDictionary *changes = @{
                              (__bridge id)kSecValueData : [NSKeyedArchiver archivedDataWithRootObject:object]
                              };
    return SecItemUpdate((__bridge CFDictionaryRef)keychainQuery, (__bridge CFDictionaryRef)changes);
}

SecItemCopyMatching

用于获取保存在KeyChain中得数据。

  • 方法原型:OSStatus SecItemCopyMatching(CFDictionaryRef query, CFTypeRef * result);
    • query : 保存在KeyChain中得数据信息。
    • result : 从KeyChain中取得的数据。
- (id)loadObjectForService:(NSString *)service {
    NSMutableDictionary *keychainQuery = [self getKeychainQueryWithService:service];
    [keychainQuery setObject:(__bridge id)kCFBooleanTrue forKey:(__bridge id)kSecReturnData];
    [keychainQuery setObject:(__bridge id)kSecMatchLimitAll forKey:(__bridge id)kSecMatchLimit];
    CFDataRef dataRef = NULL;
    OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)keychainQuery, (CFTypeRef *)&dataRef);
    id results = nil;
    if (status == errSecSuccess) {
        @try {
            results = [NSKeyedUnarchiver unarchiveObjectWithData:(__bridge NSData *)dataRef];
        }
        @catch (NSException *exception) {
            NSLog(@"%@", exception);
        }
        @finally {
            NSLog(@"finally");
        }
    } 
    if (dataRef) {
        CFRelease(dataRef);
    }
    return results;
}

SecItemDelete

用于删除保存在KeyChain中得数据。

  • 方法原型:OSStatus SecItemDelete(CFDictionaryRef query)
    • query:要删除的数据的信息。
- (void)delete:(NSString *)service {
    NSMutableDictionary *keychainQuery = [self getKeychainQueryWithService:service];
    SecItemDelete((__bridge CFDictionaryRef)keychainQuery);
}
TIDLogin

附:

OSStatus状态码

CF_ENUM(OSStatus)
{
    errSecSuccess                               = 0,       /* No error. */
    errSecUnimplemented                         = -4,      /* Function or operation not implemented. */
    errSecIO                                    = -36,     /*I/O error (bummers)*/
    errSecOpWr                                  = -49,     /*file already open with with write permission*/
    errSecParam                                 = -50,     /* One or more parameters passed to a function where not valid. */
    errSecAllocate                              = -108,    /* Failed to allocate memory. */
    errSecUserCanceled                          = -128,    /* User canceled the operation. */
    errSecBadReq                                = -909,    /* Bad parameter or invalid state for operation. */
    errSecInternalComponent                     = -2070,
    errSecNotAvailable                          = -25291,  /* No keychain is available. You may need to restart your computer. */
    errSecDuplicateItem                         = -25299,  /* The specified item already exists in the keychain. */
    errSecItemNotFound                          = -25300,  /* The specified item could not be found in the keychain. */
    errSecInteractionNotAllowed                 = -25308,  /* User interaction is not allowed. */
    errSecDecode                                = -26275,  /* Unable to decode the provided data. */
    errSecAuthFailed                            = -25293,  /* The user name or passphrase you entered is not correct. */
};

你可能感兴趣的:(TouchID 登陆)