Android: Wie stelle ich Passcode / PIN nur für meine App ein?

Nov 19 2020

Ich arbeite an einer App, in der ich die Sicherheitsfunktion festlegen muss, indem ich Passcode / Passwort / PIN für den Benutzer einstelle, sodass die App jedes Mal, wenn er die App aus dem Hintergrund öffnet, nach Passwort / Passcode / PIN fragt. Ich habe ein paar Artikel / Lösungen wie diese gelesen, aber sie haben mir nicht geholfen. Dieselbe Funktionalität, die wir in normalen Banking-Apps gefunden haben, bei denen beim Öffnen einer App nach Passcode / Fingerabdruck gefragt wird.

Ich habe bereits die Logik eingerichtet, um den Passcode / die PIN in einer gemeinsamen Einstellung zu speichern, bin mir jedoch nicht sicher, wann ich danach fragen soll. Ich weiß, dass wir den Begrüßungsbildschirm nicht durch Passcode- / PIN-Aktivitäten ersetzen können, da der Benutzer manchmal auf dem HomeScreen / der MainActivity der App die Home-Taste drückt und beim erneuten Öffnen der App aus den letzten Apps nach der App nach Passcode / PIN fragen sollte, um den Vorgang fortzusetzen App verwenden.

Jede Hilfe wäre dankbar.

Antworten

SonTruong Nov 20 2020 at 11:59

Dies ist eine interessante Frage, ich werde meine Gedanken zu dieser Frage teilen und auch eine Lösung geben.

Terminologie:

App- Sperrtyp : Ein generischer Name für PIN / PIN-Code / Passwort / Passcode usw. (im folgenden Abschnitt werde ich den PIN-Namen zur Demonstration verwenden).

PinActivity: Ein Bildschirm, auf dem Benutzer ihre PIN eingeben, um sich selbst zu überprüfen

Geschichte:

Bei Apps, bei denen Benutzer eine PIN eingeben müssen, möchten sie normalerweise sicherstellen, dass vertrauliche Informationen nicht von anderen Personen verloren gehen oder gestohlen werden. Daher werden wir App-Aktivitäten in zwei Gruppen einteilen.

  • Normale Aktivitäten: Enthält keine vertraulichen Informationen, normalerweise bevor sich Benutzer bei der App angemeldet haben, wie z. B. SplashActivity, LoginActivity, RegistrationActivity, PinActivity usw.

  • Gesicherte Aktivitäten: Enthalten vertrauliche Informationen, normalerweise nachdem sich Benutzer angemeldet haben, z. B. MainActivity, HomeActivity, UserInfoActivity usw.

Bedingungen:

Für gesicherte Aktivitäten müssen wir sicherstellen, dass Benutzer immer ihre PIN eingeben, bevor sie den Inhalt anzeigen, indem wir die PinActivity anzeigen. Diese Aktivität wird in den folgenden Szenarien gezeigt:

  • [1] Wenn Benutzer eine gesicherte Aktivität öffnen, bilden Sie eine normale Aktivität, z. B. von SplashActivity zu MainActivity

  • [2] Wenn Benutzer eine gesicherte Aktivität öffnen, indem sie auf Benachrichtigungen tippen, z. B. auf eine Benachrichtigung, um MainActivity zu öffnen

  • [3] Wenn Benutzer auf dem Bildschirm "Letzte" auf die App tippen

  • [4] Wenn die App eine gesicherte Aktivität von einem anderen Ort aus startet, z. B. von Diensten, Rundfunkempfängern usw.

Implementierung:

Für den Fall [1] [2] und [4] fügen wir vor Beginn einer gesicherten Aktivität der ursprünglichen Absicht ein Extra hinzu. Ich werde eine Datei mit dem Namen IntentUtils.kt erstellen

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)
    }
}

Verwenden Sie diese Klasse für normale Aktivitäten, Benachrichtigungen, Dienste usw.

startActivity(Intent(this, MainActivity::class.java).secured())

Für Fall [3] werde ich 2 APIs verwenden:

  • ProcessLifecycleOwner : Um festzustellen, ob die App in den Hintergrund wechselt. Ein typisches Szenario ist, wenn Benutzer auf ihren Geräten auf die Start- / Menütaste klicken.

  • ActivityLifecycleCallbacks : Um festzustellen, ob eine Aktivität wieder aufgenommen wird, verlassen Sie sich auf die onActivityResumed- Methode (Aktivitätsmethode) .

Zuerst erstelle ich eine Basisaktivität, jede normale Aktivitis muss von dieser Klasse ausgehen

BaseActivity.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
}

Zweitens erstelle ich eine gesicherte Aktivität, alle gesicherten Aktivitäten müssen von dieser Klasse ausgehen

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)
    }
}

Drittens erstelle ich eine Klasse, die sich von der Anwendung aus erstreckt. Die gesamte Logik befindet sich in dieser Klasse

MyApplication.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) {}
    }
}

Fazit:

Diese Lösung funktioniert in den zuvor genannten Fällen, aber ich habe die folgenden Szenarien nicht getestet:

  • Beim Start hat eine gesicherte Aktivität den Startmodus singleTop | singleTask | singleInstance
  • Wenn die Anwendung vom System bei geringem Arbeitsspeicher beendet wird
  • Andere Szenarien, denen jemand begegnen könnte (wenn ja, lassen Sie es mich bitte im Kommentarbereich wissen).