如何在复杂业务场景中优雅实现Android指纹验证?

那控制器 FingerPrintTypeController 和四个场景的关系又是如何?我们看看类图。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

可以看到,四个场景,对应四个状态类,控制器和状态类实现了同一个接口,在内部根据当前场景转发给对应的类, 那怎么根据场景转发给对应类?我们建立一个映射表,把场景和类对应起来。每次匹配的话只要 O(1) 复杂度。

private interface FingerPrintType {
void onAuthenticationSucceeded();

void onAuthenticationError(String content);
}

private class LoginAuthType implements FingerPrintType {
@Override
public void onAuthenticationSucceeded() { }

@Override
public void onAuthenticationError(String content) { }
}

private class ClearType implements FingerPrintType {
@Override
public void onAuthenticationSucceeded() { }

@Override
public void onAuthenticationError(String content) { }
}

private class LoginSettingType implements FingerPrintType {
@Override
public void onAuthenticationSucceeded() { }

@Override
public void onAuthenticationError(String content) { }
}

private class SettingType implements FingerPrintType {
@Override
public void onAuthenticationSucceeded() { }

@Override
public void onAuthenticationError(String content) { }
}

private class FingerPrintTypeController implements FingerPrintType {
private Map<String, FingerPrintType> typeMappingMap = new HashMap<>();

public FingerPrintTypeController() {
typeMappingMap.put(GESTURE_FINGER_SETTING, new SettingType());
typeMappingMap.put(GESTURE_FINGER_LOGIN_SETTING, new LoginSettingType());
typeMappingMap.put(GESTURE_FINGER_CLEAR, new ClearType());
typeMappingMap.put(GESTURE_FINGER_LOGIN, new LoginAuthType());
}

@Override
public void onAuthenticationSucceeded() {
typeMappingMap.get(mType).onAuthenticationSucceeded();
}

@Override
public void onAuthenticationError(String content) {
typeMappingMap.get(mType).onAuthenticationError(content);
}
}

这个时候产品又说了,同样是异常情况,但是被劫持和异常过程异常的提示文案要不一样,ok,那我们将提示语和操作分离开来,提示和业务场景的对应关系也预先缓存在 Map 里,直接 get 获取具体提示,作为参数传入就可以了。

//普通异常情况提示
exceptionTipsMappingMap = new HashMap<>();
exceptionTipsMappingMap.put(GESTURE_FINGER_SETTING, getString(R.string.fingerprint_no_support_fingerprint_gesture));
exceptionTipsMappingMap.put(GESTURE_FINGER_LOGIN_SETTING, getString(R.string.fingerprint_no_support_fingerprint_gesture));
exceptionTipsMappingMap.put(GESTURE_FINGER_CLEAR, null);
exceptionTipsMappingMap.put(GESTURE_FINGER_LOGIN, getString(R.string.fingerprint_no_support_fingerprint_account));

兼容问题

1. 明明符合条件,isHardwareDetected() 返回 false?

在同一机型上调用 FingerprintManagerCompat 的 isHardwareDetected() 和 hasEnrolledFingerprints() 时候,返回的都是 false,但是调用 FingerprintManager 的 isHardwareDetected() 和 hasEnrolledFingerprints() 时,却是返回 true。

解决:是否符合指纹条件可以多加一层判断。

2. Letv X500 Android 6.0,API23 不按正常的套路回调

onAuthenticationError 和 onAuthenticationFailed,理论上应该是识别失败的情况,但是该机型点击取消指纹识别也会先回调一次Error,如果遇到这种情况,只能根据具体项目环境中去进行规避适配了。

3. 魅族上遇到的坑

onAuthenticationHelp 回调不按套路出牌,正常官网文档解释,这个方法的回调时机是在指纹认证期间发生可恢复性的错误时回调。结果在魅族上,启动指纹识别认证的时候就会回调这个方法,里面传递回来的信息提示是“等待按下手指”,也就是说,它的 onAuthenticationHelp 回调跟官网时机不一样,而且方法的作用也变了,它在正常的情况回调了 onAuthenticationHelp。

解决:不影响验证流程,无需解决

4. 小米 锁屏和切后台生命周期不一致

产品需求:用户锁屏或切到后台时(onStop)自动停止指纹验证,回到界面时(onResume)自动调起验证。

所以我在指纹回调方法中加入了标志位 isInAuth。onStop时保存 isInAuth,onResume时 isInAuth == true 则自动调起验证。

@Override
public void onAuthenticationSucceeded(FingerprintManagerCompat.AuthenticationResult result) {
isInAuth = false;
}

@Override
public void onAuthenticationError(int errMsgId, CharSequence errString) {
isInAuth = false;
}

@Override
public void onAuthenticationFailed() {
isInAuth = true;
}

@Override
public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {
isInAuth = true;
}

然而小米6、米mix2 锁屏时的生命周期是 onAuthenticationError -> onStop;切到后台是 onStop -> onAuthenticationError。导致不同流程下拿到 isInAuth 标志位不一致,无法自动调起验证。

解决:界面指纹按钮可以手动调起验证,无需兼容处理。

小米5生命周期同上,但是无论是自动还是手动调起验证,马上就回调了 onAuthenticationError,也就是说 MI5 从后台切回来后,指纹验证流程中断。

解决:用一个栈来存储调用方法顺序,如果验证方法调起,马上就回调 onAuthenticationError 方法,则判定是属于兼容问题,按验证失败来解决。

5. 密钥解密失败

三星SM-A9100 、Nexus 6P密钥解密失败
解决:暂无法解决

其他兼容解决方案:

  • 三星passSdk(不过从2018下半年开始,Pass SDK 将不再提供 DEVICE_FINGERPRINT_UNIQUE_ID 。也就是不再为每个已注册的指纹提供索引了。因此将无法通过 SDK 区分使用哪个指纹来验证用户。)
  • 魅族 flyme开发平台提供了指纹验证官方api

非兼容问题

1. 新注册指纹密钥解密失败

系统中注册了一个新的指纹的情况下,即使指纹在系统指纹列表里,验证也不通过。
解决:删除了当前无效的key,然后根据参数再次生成密钥。

@Override
public void onAuthenticationSucceeded(FingerprintManagerCompat.AuthenticationResult result) {

/**

  • doFinal方法会检查结果是不是会拦截或者篡改过,
  • 如果是的话会抛出一个异常,异常的时候都将认证当做是失败来处理
    */
    try {
    result.getCryptoObject().getCipher().doFinal();
    mCustomCallback.onAuthenticationSucceeded(true);
    } catch (IllegalBlockSizeException e) {
    //如果是新录入的指纹,会抛出该异常,需要重新生成密钥对重新验证,这里加个次数限制,避免进入验证异常->重新验证->又验证异常的死循环
    if (happenCount == 0) {
    beginAuthenticate();
    happenCount++;
    return;
    }
    mCustomCallback.onAuthenticationSucceeded(false);
    } catch (Exception e) {
    mCustomCallback.onAuthenticationSucceeded(false);
    }

    }
2. 设备已有指纹,生成密钥却异常提示没有指纹

非复现,和设备无关,怀疑是谷歌 API 的坑。
java.lang.IllegalStateException: At least one fingerprint must be enrolled to create keys requiring user authentication for every use
解决:暂时只想到针对这个特定异常,直接使用无密钥验证,有一定的安全风险,有更好方案欢迎补充。

最后

如果你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足。谢谢。

欢迎大家一起交流讨论啊~

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

那么很难做到真正的技术提升。**

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值