Android:如何仅为我的应用设置密码/PIN?

标签 android security kotlin passcode

我正在开发一个应用程序,我需要通过为用户设置密码/密码/密码来设置安全功能,以便每当他们从后台打开应用程序时,应用程序都会要求输入密码/密码/密码。我阅读了一些文章/解决方案,例如 this但他们没有帮助我。我们在普通银行应用程序中发现的相同功能,每当您打开应用程序时,他们都会要求输入密码/指纹。
我已经设置了将密码/pin 保存在共享首选项中的逻辑,但我不确定何时询问它。我知道我们不能用密码/pin Activity 替换启动屏幕,因为有时从应用程序的 homeScreen/MainActivity 中,用户按下主页按钮,当他们再次从最近的应用程序中打开应用程序时,应用程序应该要求输入密码/pin 以恢复应用程序使用。
任何帮助,将不胜感激。

最佳答案

这是一个有趣的问题,我将分享我对这个问题的想法并给出解决方案。
术语:
App Lock Type:pin/pincode/password/passcode等的通用名称(下节我将使用pin名来演示)
PinActivity:用户输入他们的密码以验证自己的屏幕
故事:
对于需要用户输入密码的应用程序,他们通常希望确保敏感信息不会被其他人泄露/窃取。因此,我们将应用 Activity 分为 2 组。

  • 正常 Activity :不包含任何敏感信息,通常在用户登录应用之前,如 SplashActivity、LoginActivity、RegistrationActivity、PinActivity 等。
  • 安全 Activity :包含敏感信息,通常在用户登录后,如 MainActivity、HomeActivity、UserInfoActivity 等。

  • 条件:
    对于安全 Activity ,我们必须确保用户在通过显示 PinActivity 查看内容之前始终输入他们的 pin。此 Activity 将在以下场景中显示:
  • [1] 当用户从正常的Activity打开一个安全的Activity时,比如从SplashActivity到MainActivity
  • [2] 当用户通过点击通知打开安全 Activity 时,例如他们点击通知以打开 MainActivity
  • [3] 当用户从“最近”屏幕点击应用程序时
  • [4] 当应用程序从其他地方(如服务、广播接收器等)启动安全 Activity 时。

  • 实现:
    对于案例 [1] [2] 和 [4],在开始安全 Activity 之前,我们将在原始 Intent 中添加额外内容。我将创建一个名为 IntentUtils.kt 的文件
    IntentUtils.kt
    const val EXTRA_IS_PIN_REQUIRED = "EXTRA_IS_PIN_REQUIRED"
    
    fun Intent.secured(): Intent {
        return this.apply {
            putExtra(EXTRA_IS_PIN_REQUIRED, true)
        }
    }
    
    从正常 Activity 、通知、服务等中使用此类。
    startActivity(Intent(this, MainActivity::class.java).secured())
    
    对于案例 [3],我将使用 2 个 API:
  • ProcessLifecycleOwner : 检测应用是否进入后台。一个典型的场景是当用户点击他们设备上的 Home/Menu 键时。
  • ActivityLifecycleCallbacks : 检测是否依赖 onActivityResumed(activity) 恢复 Activity 方法。

  • 首先我创建一个基础 Activity ,所有正常的 Activity 都必须从这个类扩展
    基本 Activity .kt
    open class BaseActivity : AppCompatActivity() {
        
        // This method indicates that a pin is required if 
        // users want to see the content inside.
        open fun isPinRequired() = false
    }
    
    其次我创建了一个安全 Activity ,所有安全 Activity 都必须从这个类扩展
    SecuredActivity.kt
    open class SecuredActivity : BaseActivity() {
        override fun isPinRequired() = true
    
        // This is useful when launch a secured activity with 
        // singleTop, singleTask, singleInstance launch mode
        override fun onNewIntent(intent: Intent?) {
            super.onNewIntent(intent)
            setIntent(intent)
        }
    }
    
    第三,我创建了一个从应用程序扩展的类,所有逻辑都在这个类中
    我的应用程序.kt
    class MyApplication : Application() {
    
        private var wasEnterBackground = false
    
        override fun onCreate() {
            super.onCreate()
            registerActivityLifecycleCallbacks(ActivityLifecycleCallbacksImpl())
            ProcessLifecycleOwner.get().lifecycle.addObserver(LifecycleObserverImpl())
        }
    
        private fun showPinActivity() {
            startActivity(Intent(this, PinActivity::class.java))
        }
    
        inner class LifecycleObserverImpl : LifecycleObserver {
    
            @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
            fun onEnterBackground() {
                wasEnterBackground = true
            }
        }
    
        inner class ActivityLifecycleCallbacksImpl : ActivityLifecycleCallbacks {
    
            override fun onActivityResumed(activity: Activity) {
                val baseActivity = activity as BaseActivity
                if (!wasEnterBackground) {
                    // Handle case [1] [2] and [4]
                    val removed = removeIsPinRequiredKeyFromActivity(activity)
                    if (removed) {
                        showPinActivity()
                    }
                } else {
                    // Handle case [3]
                    wasEnterBackground = false
                    if (baseActivity.isPinRequired()) {
                        removeIsPinRequiredKeyFromActivity(activity)
                        showPinActivity()
                    }
                }
            }
    
            private fun removeIsPinRequiredKeyFromActivity(activity: Activity): Boolean {
                val key = EXTRA_IS_PIN_REQUIRED
                if (activity.intent.hasExtra(key)) {
                    activity.intent.removeExtra(key)
                    return true
                }
                return false
            }
    
            override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {}
            override fun onActivityStarted(activity: Activity) {}
            override fun onActivityPaused(activity: Activity) {}
            override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}
            override fun onActivityStopped(activity: Activity) {}
            override fun onActivityDestroyed(activity: Activity) {}
        }
    }
    
    结论:
    此解决方案适用于我之前提到的那些情况,但我尚未测试以下场景:
  • 启动安全 Activity 时,启动模式为 singleTop|singleTask|singleInstance
  • 当应用程序在内存不足时被系统杀死
  • 有人可能遇到的其他情况(如果是,请在评论部分告诉我)。
  • 关于Android:如何仅为我的应用设置密码/PIN?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64907251/

    相关文章:

    security - 如何从 RESTful 服务发送安全 token ?

    android - Kotlin 类转换异常

    kotlin - 如何测试Completable Emitter.tryOnError()

    json - Moshi 忽略 Kotlin 中的字段

    android - actionscript 3 - 播放声音一次然后延迟

    java - 将图像存储为数组或枚举 : which is better?

    AndroidX 找不到 support-v4.aar

    php - Yii Captcha 小部件中的日语字符?

    android - Eclipse 调试 Android 不工作

    security - 我应该在 Asp.net WebAPI 中的哪里插入授权?