📱 안드로이드 Android ~ Kotlin

[Android/Kotiln] 생체 인식 인증 방식 Biometric 사용하기

핑크빛연어 2022. 4. 28. 17:19

 

안드로이드에서 제공하는 생체 인증 방식Fingerprint, Biometric 가 있습니다.

Marshmallow(6.0) ~ Oreo(8.0) : Fingerprint
Pie(9.0) 이상 ~ : Biometric

 

Android API Level 29 부터는 Biometric 사용을 권장합니다.

Biometric 를 사용한 지문인식를 Kotlin 으로 구현한 소스입니다.

 

작성한 파일 목록 입니다.

1. build.gradle(:app)
2. AndroidManifest.xml
3. BiometricActivity.kt
4. activity_biometric.xml

 

 

build.gradle(:app)

dependencies 에 implementation 'androidx.biometric:biometric:1.1.0' 를 추가해줍니다.

android {
	...
    buildFeatures {
        viewBinding = true
    }
}


dependencies {
	...

    //Bio
    implementation 'androidx.biometric:biometric:1.1.0'

	...

}

 

 

AndroidManifest.xml

manifest 에 <uses-permission android:name="android.permission.USE_BIOMETRIC" /> 권한을 추가해줍니다.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.eun.mykotlin">

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.USE_BIOMETRIC" />

    <application
      ...
    </application>

</manifest>

 

 

BiometricActivity.kt

btn_bio_set 버튼을 클릭 시 authenticateToEncrypt() 를 호출하여 생체 인증 가능 여부를 확인하고,
확인 후 goAuthenticate() 메소드를 호출하여 biometricPrompt?.authenticate() 를 통해 생체 인식을 실행합니다.
인증 후 setBiometricPrompt() 의 BiometricPrompt.AuthenticationCallback() 에서 결과를 콜백받습니다.

생체 인식 정보가 등록되어 있지 않은 경우, goBiometricSettings() 을 호출하여 지문등록 설정화면으로 이동되고, 
등록 후 registerForActivityResult() 내부에서 다시 authenticateToEncrypt() 를 호출하여 상태를 확인합니다.

package com.eun.mykotlin

import android.app.Activity
import android.app.AlertDialog
import android.content.Intent
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import androidx.biometric.BiometricManager
import android.os.Build
import android.provider.Settings
import androidx.biometric.BiometricPrompt
import com.eun.mytmapkotlin.databinding.ActivityBiometricBinding
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_STRONG
import androidx.biometric.BiometricManager.Authenticators.DEVICE_CREDENTIAL
import androidx.biometric.BiometricPrompt.PromptInfo
import androidx.core.content.ContextCompat
import java.util.concurrent.Executor


class BiometricActivity: AppCompatActivity() {

    companion object {
        const val TAG: String = "BiometricActivity"
    }

    lateinit var binding: ActivityBiometricBinding

    private var executor: Executor? = null
    private var biometricPrompt: BiometricPrompt? = null
    private var promptInfo: BiometricPrompt.PromptInfo? = null

    private val loginLauncher =
        registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
            Log.d(TAG, "registerForActivityResult - result : $result")

            if (result.resultCode == Activity.RESULT_OK) {
                Log.d(TAG, "registerForActivityResult - RESULT_OK")
                authenticateToEncrypt()  //생체 인증 가능 여부확인 다시 호출
            } else {
                Log.d(TAG, "registerForActivityResult - NOT RESULT_OK")
            }
        }

    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

//        setContentView(R.layout.activity_biometric)
        binding = ActivityBiometricBinding.inflate(layoutInflater)
        setContentView(binding.root)

        biometricPrompt = setBiometricPrompt()
        promptInfo = setPromptInfo()

        //지문 인증 호출 버튼 클릭 시
        binding.btnBioSet.setOnClickListener {
            authenticateToEncrypt()  //생체 인증 가능 여부확인
        }

    }
    
    
    private fun setPromptInfo(): BiometricPrompt.PromptInfo {

        val promptBuilder: BiometricPrompt.PromptInfo.Builder = BiometricPrompt.PromptInfo.Builder()

        promptBuilder.setTitle("Biometric login for my app")
        promptBuilder.setSubtitle("Log in using your biometric credential")
        promptBuilder.setNegativeButtonText("Use account password")

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { //  안면인식 ap사용 android 11부터 지원
            promptBuilder.setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG or BiometricManager.Authenticators.DEVICE_CREDENTIAL)
        }

        promptInfo = promptBuilder.build()
        return promptInfo as PromptInfo
    }


    private fun setBiometricPrompt(): BiometricPrompt {
        executor = ContextCompat.getMainExecutor(this)

        biometricPrompt = BiometricPrompt(this@BiometricActivity, executor!!, object : BiometricPrompt.AuthenticationCallback() {

            override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
                super.onAuthenticationError(errorCode, errString)
                Toast.makeText(this@BiometricActivity, """"지문 인식 ERROR [ errorCode: $errorCode, errString: $errString ]""".trimIndent(), Toast.LENGTH_SHORT).show()
            }

            override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
                super.onAuthenticationSucceeded(result)
                Toast.makeText(this@BiometricActivity, "지문 인식 성공", Toast.LENGTH_SHORT).show()
            }

            override fun onAuthenticationFailed() {
                super.onAuthenticationFailed()
                Toast.makeText(this@BiometricActivity, "지문 인식 실패", Toast.LENGTH_SHORT).show()
            }

        } )
        return biometricPrompt as BiometricPrompt
    }

    
    /*
    * 생체 인식 인증을 사용할 수 있는지 확인
    * */
    fun authenticateToEncrypt() = with(binding) {

        Log.d(TAG, "authenticateToEncrypt() ")

        var textStatus = ""
        val biometricManager = BiometricManager.from(this@BiometricActivity)
//        when (biometricManager.canAuthenticate(BIOMETRIC_STRONG or DEVICE_CREDENTIAL)) {
        when (biometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK)) {

            //생체 인증 가능
            BiometricManager.BIOMETRIC_SUCCESS -> textStatus = "App can authenticate using biometrics."

            //기기에서 생체 인증을 지원하지 않는 경우
            BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE -> textStatus = "No biometric features available on this device."

            //현재 생체 인증을 사용할 수 없는 경우
            BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE -> textStatus = "Biometric features are currently unavailable."

            //생체 인식 정보가 등록되어 있지 않은 경우
            BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED -> {
                textStatus = "Prompts the user to create credentials that your app accepts."

                val dialogBuilder = AlertDialog.Builder(this@BiometricActivity)
                dialogBuilder
                    .setTitle("나의앱")
                    .setMessage("지문 등록이 필요합니다. 지문등록 설정화면으로 이동하시겠습니까?")
                    .setPositiveButton("확인") { dialog, which -> goBiometricSettings() }
                    .setNegativeButton("취소") {dialog, which -> dialog.cancel() }
                dialogBuilder.show()
            }

            //기타 실패
            else ->  textStatus = "Fail Biometric facility"

        }
        binding.tvStatus.text = textStatus

        //인증 실행하기
        goAuthenticate()
    }
    
    
    /*
    * 생체 인식 인증 실행
    * */
    private fun goAuthenticate() {
        Log.d(TAG, "goAuthenticate - promptInfo : $promptInfo")
        promptInfo?.let {
            biometricPrompt?.authenticate(it);  //인증 실행
        }
    }


    /*
    * 지문 등록 화면으로 이동
    * */
    fun goBiometricSettings() {
        val enrollIntent = Intent(Settings.ACTION_BIOMETRIC_ENROLL).apply {
            putExtra(
                Settings.EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED,
                BIOMETRIC_STRONG or DEVICE_CREDENTIAL)
        }
        loginLauncher.launch(enrollIntent)
    }

}

 

 

activity_biometric.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <TextView
        android:id="@+id/tv_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="50dp"
        android:text="Biometric"
        android:textStyle="bold"
        android:textSize="20sp"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

    <TextView
        android:id="@+id/tv_status"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Biometric status ..."
        android:textSize="15sp"
        android:background="@color/amber_200"
        app:layout_constraintTop_toBottomOf="@id/tv_title"
        app:layout_constraintBottom_toTopOf="@id/btn_bio_set"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"  />

    <Button
        android:id="@+id/btn_bio_set"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="지문 인증 호출"
        app:layout_constraintTop_toBottomOf="@id/tv_status"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"  />

</androidx.constraintlayout.widget.ConstraintLayout>

 

 

안드로이드 결과화면

 

 

 

https://developer.android.com/training/sign-in/biometric-auth?hl=ko 

 

생체 인식 인증 대화상자 표시  |  Android 개발자  |  Android Developers

생체 인식 인증 대화상자 표시 앱의 민감한 정보나 고급 콘텐츠를 보호하는 데는 얼굴 인식, 지문 인식 같은 생체 인식 인증을 요청하는 방법이 있습니다. 이 가이드에서는 앱에서 생체 인식 로

developer.android.com

 

 

감사합니당 :)

 

728x90
반응형