Google is committed to advancing racial equity for Black communities. See how.
本頁面由 Cloud Translation API 翻譯而成。
Switch to English

使用電話號碼在Android上通過Firebase進行身份驗證

您可以通過向用戶的手機發送SMS消息來使用Firebase身份驗證來登錄用戶。用戶使用SMS消息中包含的一次性代碼登錄。

將電話號碼登錄添加到您的應用程序最簡單的方法是使用FirebaseUI ,它包括一個插入式登錄小部件,該小部件實現了電話號碼登錄的登錄流程以及基於密碼的聯合登錄-在。本文檔介紹瞭如何使用Firebase SDK實施電話號碼登錄流程。

在你開始之前

  1. 如果尚未將Firebase添加到您的Android項目中
  2. 在項目級build.gradle文件,確保包括兩個你谷歌的Maven倉庫buildscriptallprojects部分。
  3. 將Firebase身份驗證Android庫的依賴項添加到模塊(應用程序級)Gradle文件(通常為app/build.gradle )中:
    implementation 'com.google.firebase:firebase-auth:19.3.2'
  4. 如果您尚未將應用程序連接到Firebase項目,請從Firebase控制台進行
  5. 如果您尚未在Firebase控制台中設置應用程序的SHA-1哈希,請進行設置。有關查找應用程序的SHA-1哈希的信息,請參閱驗證客戶端

另外,請注意,電話號碼登錄需要使用物理設備,並且不能在模擬器上運行。

安全問題

僅使用電話號碼進行身份驗證雖然方便,但比其他可用方法安全性低,因為擁有電話號碼的身份可以在用戶之間輕鬆轉移。同樣,在具有多個用戶配置文件的設備上,任何可以接收SMS消息的用戶都可以使用設備的電話號碼登錄到帳戶。

如果您在應用中使用基於電話號碼的登錄,則應將其與更安全的登錄方法一起提供,並告知用戶使用電話號碼登錄的安全權衡。

為您的Firebase項目啟用電話號碼登錄

要通過SMS登錄用戶,必須首先為Firebase項目啟用“電話號碼”登錄方法:

  1. Firebase控制台中 ,打開“ 身份驗證”部分。
  2. 在“ 登錄方法”頁面上,啟用“ 電話號碼”登錄方法。

Firebase的電話號碼登錄請求配額足夠高,因此大多數應用程序都不會受到影響。但是,如果您需要使用電話認證登錄大量用戶,則可能需要升級定價計劃。請參閱定價頁面。

將驗證碼發送到用戶的手機

要啟動電話號碼登錄,請向用戶顯示一個界​​面,提示他們鍵入他們的電話號碼。法律要求各不相同,但是作為最佳實踐並為用戶設定期望,您應該告知他們,如果他們使用電話登錄,則他們可能會收到SMS消息以進行驗證,並採用標準費率。

然後,將其電話號碼傳遞給PhoneAuthProvider.verifyPhoneNumber方法,以請求Firebase驗證用戶的電話號碼。例如:

爪哇

PhoneAuthProvider.getInstance().verifyPhoneNumber(
        phoneNumber,        // Phone number to verify
        60,                 // Timeout duration
        TimeUnit.SECONDS,   // Unit of timeout
        this,               // Activity (for callback binding)
        mCallbacks);        // OnVerificationStateChangedCallbacks

Kotlin + KTX

PhoneAuthProvider.getInstance().verifyPhoneNumber(
        phoneNumber, // Phone number to verify
        60, // Timeout duration
        TimeUnit.SECONDS, // Unit of timeout
        this, // Activity (for callback binding)
        callbacks) // OnVerificationStateChangedCallbacks

verifyPhoneNumber方法是可重入的:如果您多次調用它,例如在活動的onStart方法中,除非原始請求已超時,否則verifyPhoneNumber方法將不會發送第二條SMS。

如果您的應用程序在用戶登錄之前關閉(例如,當用戶使用其SMS應用程序時),則可以使用此行為來恢復電話號碼登錄過程。調用verifyPhoneNumber ,設置一個標誌,指示正在進行驗證。然後,將標誌保存在Activity的onSaveInstanceState方法中,並將標誌還原到onRestoreInstanceState 。最後,在Activity的onStart方法中,檢查是否已在進行驗證,如果已在驗證中,請再次調用verifyPhoneNumber 。驗證完成或失敗時,請確保清除該標誌(請參閱驗證回調 )。

要輕鬆處理屏幕旋轉並重新啟動Activity的其他實例,請將您的Activity傳遞給verifyPhoneNumber方法。當Activity停止時,回調將自動分離,因此您可以在回調方法中自由編寫UI轉換代碼。

也可以通過在您的Auth實例上通過setLanguageCode方法指定身份驗證語言來本地化Firebase發送的SMS消息。

爪哇

auth.setLanguageCode("fr");
// To apply the default app language instead of explicitly setting it.
// auth.useAppLanguage();

Kotlin + KTX

auth.setLanguageCode("fr")
// To apply the default app language instead of explicitly setting it.
// auth.useAppLanguage()

調用PhoneAuthProvider.verifyPhoneNumber ,還必須提供OnVerificationStateChangedCallbacks實例,該實例包含處理請求結果的回調函數的實現。例如:

爪哇

mCallbacks = new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {

    @Override
    public void onVerificationCompleted(PhoneAuthCredential credential) {
        // This callback will be invoked in two situations:
        // 1 - Instant verification. In some cases the phone number can be instantly
        //     verified without needing to send or enter a verification code.
        // 2 - Auto-retrieval. On some devices Google Play services can automatically
        //     detect the incoming verification SMS and perform verification without
        //     user action.
        Log.d(TAG, "onVerificationCompleted:" + credential);

        signInWithPhoneAuthCredential(credential);
    }

    @Override
    public void onVerificationFailed(FirebaseException e) {
        // This callback is invoked in an invalid request for verification is made,
        // for instance if the the phone number format is not valid.
        Log.w(TAG, "onVerificationFailed", e);

        if (e instanceof FirebaseAuthInvalidCredentialsException) {
            // Invalid request
            // ...
        } else if (e instanceof FirebaseTooManyRequestsException) {
            // The SMS quota for the project has been exceeded
            // ...
        }

        // Show a message and update the UI
        // ...
    }

    @Override
    public void onCodeSent(@NonNull String verificationId,
                           @NonNull PhoneAuthProvider.ForceResendingToken token) {
        // The SMS verification code has been sent to the provided phone number, we
        // now need to ask the user to enter the code and then construct a credential
        // by combining the code with a verification ID.
        Log.d(TAG, "onCodeSent:" + verificationId);

        // Save verification ID and resending token so we can use them later
        mVerificationId = verificationId;
        mResendToken = token;

        // ...
    }
};

Kotlin + KTX

callbacks = object : PhoneAuthProvider.OnVerificationStateChangedCallbacks() {

    override fun onVerificationCompleted(credential: PhoneAuthCredential) {
        // This callback will be invoked in two situations:
        // 1 - Instant verification. In some cases the phone number can be instantly
        //     verified without needing to send or enter a verification code.
        // 2 - Auto-retrieval. On some devices Google Play services can automatically
        //     detect the incoming verification SMS and perform verification without
        //     user action.
        Log.d(TAG, "onVerificationCompleted:$credential")

        signInWithPhoneAuthCredential(credential)
    }

    override fun onVerificationFailed(e: FirebaseException) {
        // This callback is invoked in an invalid request for verification is made,
        // for instance if the the phone number format is not valid.
        Log.w(TAG, "onVerificationFailed", e)

        if (e is FirebaseAuthInvalidCredentialsException) {
            // Invalid request
            // ...
        } else if (e is FirebaseTooManyRequestsException) {
            // The SMS quota for the project has been exceeded
            // ...
        }

        // Show a message and update the UI
        // ...
    }

    override fun onCodeSent(
        verificationId: String,
        token: PhoneAuthProvider.ForceResendingToken
    ) {
        // The SMS verification code has been sent to the provided phone number, we
        // now need to ask the user to enter the code and then construct a credential
        // by combining the code with a verification ID.
        Log.d(TAG, "onCodeSent:$verificationId")

        // Save verification ID and resending token so we can use them later
        storedVerificationId = verificationId
        resendToken = token

        // ...
    }
}

驗證回調

在大多數應用中,您實現onVerificationCompletedonVerificationFailedonCodeSent回調。您還可以實現onCodeAutoRetrievalTimeOut ,具體取決於應用程序的要求。

onVerificationCompleted(PhoneAuthCredential)

在兩種情況下調用此方法:

  • 即時驗證:在某些情況下,無需發送或輸入驗證碼即可立即驗證電話號碼。
  • 自動檢索:在某些設備上,Google Play服務可以自動檢測傳入的驗證短信並執行驗證,而無需用戶進行任何操作。 (某些運營商可能不提供此功能。)
無論哪種情況,都已經成功驗證了用戶的電話號碼,並且您可以使用傳遞給回調的PhoneAuthCredential對象登錄user

onVerificationFailed(FirebaseException)

響應無效的驗證請求(例如指定無效電話號碼或驗證碼的請求)而調用此方法。

onCodeSent(字符串VerificationId,PhoneAuthProvider.ForceResendingToken)

可選的。 SMS將驗證碼發送到提供的電話號碼後,將調用此方法。

調用此方法時,大多數應用程序會顯示一個UI,提示用戶鍵入SMS消息中的驗證碼。 (同時,自動驗證可能會在後台進行。)然後,在用戶鍵入驗證碼之後,您可以使用傳遞給該方法的驗證碼和驗證ID來創建PhoneAuthCredential對象,該對象您可以依次使用來登錄用戶。但是,某些應用程序可能要等到調用onCodeAutoRetrievalTimeOut才顯示顯示驗證碼用戶界面(不建議)。

onCodeAutoRetrievalTimeOut(字符串VerificationId)

可選的。在為verifyPhoneNumber指定的超時時間過去之後,沒有onVerificationCompleted觸發verifyPhoneNumber情況下調用此方法。在沒有SIM卡的設備上,此方法會立即調用,因為無法進行SMS自動檢索。

某些應用程序會阻止用戶輸入,直到自動驗證時間超時為止,然後才顯示UI,提示用戶輸入SMS消息中的驗證碼(不建議)。

創建一個PhoneAuthCredential對象

用戶輸入Firebase發送到用戶電話的驗證碼後,使用傳遞給onCodeSentonCodeAutoRetrievalTimeOut回調的驗證碼和驗證ID創建一個PhoneAuthCredential對象。 (調用onVerificationCompleted ,您將直接獲取PhoneAuthCredential對象,因此可以跳過此步驟。)

要創建PhoneAuthCredential對象,請調用PhoneAuthProvider.getCredential

爪哇

PhoneAuthCredential credential = PhoneAuthProvider.getCredential(verificationId, code);

Kotlin + KTX

val credential = PhoneAuthProvider.getCredential(verificationId!!, code)

登錄用戶

在獲取PhoneAuthCredential對象之後,無論是在onVerificationCompleted回調中還是通過調用PhoneAuthProvider.getCredential ,通過將PhoneAuthCredentialPhoneAuthCredential傳遞給FirebaseAuth.signInWithCredential來完成登錄流程:

爪哇

private void signInWithPhoneAuthCredential(PhoneAuthCredential credential) {
    mAuth.signInWithCredential(credential)
            .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
                @Override
                public void onComplete(@NonNull Task<AuthResult> task) {
                    if (task.isSuccessful()) {
                        // Sign in success, update UI with the signed-in user's information
                        Log.d(TAG, "signInWithCredential:success");

                        FirebaseUser user = task.getResult().getUser();
                        // ...
                    } else {
                        // Sign in failed, display a message and update the UI
                        Log.w(TAG, "signInWithCredential:failure", task.getException());
                        if (task.getException() instanceof FirebaseAuthInvalidCredentialsException) {
                            // The verification code entered was invalid
                        }
                    }
                }
            });
}

Kotlin + KTX

private fun signInWithPhoneAuthCredential(credential: PhoneAuthCredential) {
    auth.signInWithCredential(credential)
            .addOnCompleteListener(this) { task ->
                if (task.isSuccessful) {
                    // Sign in success, update UI with the signed-in user's information
                    Log.d(TAG, "signInWithCredential:success")

                    val user = task.result?.user
                    // ...
                } else {
                    // Sign in failed, display a message and update the UI
                    Log.w(TAG, "signInWithCredential:failure", task.exception)
                    if (task.exception is FirebaseAuthInvalidCredentialsException) {
                        // The verification code entered was invalid
                    }
                }
            }
}

測試列入白名單的電話號碼

您可以通過Firebase控制台將電話號碼列入白名單進行開發。將電話號碼列入白名單可提供以下好處:

  • 在不消耗您的使用配額的情況下測試電話號碼身份驗證。
  • 測試電話號碼身份驗證,而不發送實際的SMS消息。
  • 使用相同的電話號碼運行連續測試,而不會受到限制。如果審閱者碰巧使用相同的電話號碼進行測試,則可以最大程度地減少在App Store審閱過程中拒絕的風險。
  • 無需任何額外努力即可在開發環境中輕鬆測試,例如可以在沒有Google Play服務的iOS模擬器或Android模擬器中進行開發。
  • 編寫集成測試,而不會受到通常在生產環境中應用於真實電話號碼的安全檢查的阻止。

列入白名單的電話號碼必須滿足以下要求:

  1. 確保使用不存在的虛構數字。 Firebase身份驗證不允許您將真實用戶使用的現有電話號碼列入白名單。一種選擇是使用555前綴號碼作為美國測試電話號碼,例如: +1 650-555-3434
  2. 電話號碼必須正確格式化,以保證長度和其他限制。他們仍將通過與真實用戶的電話號碼相同的驗證。
  3. 您最多可以添加10個電話號碼進行開發。
  4. 使用難以猜測的測試電話號碼/代碼,並經常進行更改。

將電話號碼和驗證碼列入白名單

  1. Firebase控制台中 ,打開“ 身份驗證”部分。
  2. 在“ 登錄方法”選項卡中,如果尚未啟用,請啟用電話提供程序。
  3. 打開用於測試手風琴的電話號碼菜單。
  4. 提供您要測試的電話號碼,例如: +1 650-555-3434
  5. 提供該特定號碼的6位驗證碼,例如: 654321
  6. 添加號碼。如果需要,可以將鼠標懸停在相應的行上,然後單擊垃圾桶圖標,以刪除電話號碼及其代碼。

手動測試

您可以在應用程序中直接開始使用列入白名單的電話號碼。這樣,您就可以在開發階段執行手動測試,而不會遇到配額問題或限制。您也可以直接從iOS模擬器或Android模擬器進行測試,而無需安裝Google Play服務。

提供白名單中的電話號碼並發送驗證碼時,不會發送任何實際的SMS。相反,您需要提供先前配置的驗證碼才能完成登錄。

登錄完成後,將使用該電話號碼創建一個Firebase用戶。該用戶具有與真實電話號碼用戶相同的行為和屬性,並且可以以相同方式訪問Realtime Database / Cloud Firestore和其他服務。在此過程中鑄造的ID令牌具有與真實電話號碼用戶相同的簽名。

另一個選擇是,如果您想進一步限制訪問權限,則可以通過對這些用戶的自定義聲明設置測試角色,以將其區分為假用戶。

整合測試

除了手動測試之外,Firebase身份驗證還提供API,以幫助編寫用於電話驗證測試的集成測試。這些API通過禁用Web中的reCAPTCHA要求和iOS中的靜默推送通知來禁用應用程序驗證。這使得在這些流程中進行自動化測試成為可能,並且更易於實現。此外,它們還有助於提供在Android上測試即時驗證流程的功能。

在Android上,您可以輕鬆使用列入白名單的電話號碼,而無需任何其他API調用。使用白名單號碼調用verifyPhoneNumber會觸發onCodeSent回調,您需要在其中提供相應的驗證碼。這允許在Android模擬器中進行測試。

爪哇

String phoneNum = "+16505554567";
String testVerificationCode = "123456";

// Whenever verification is triggered with the whitelisted number,
// provided it is not set for auto-retrieval, onCodeSent will be triggered.
PhoneAuthProvider.getInstance().verifyPhoneNumber(
        phoneNum, 30L /*timeout*/, TimeUnit.SECONDS,
        this, new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {

            @Override
            public void onCodeSent(String verificationId,
                                   PhoneAuthProvider.ForceResendingToken forceResendingToken) {
                // Save the verification id somewhere
                // ...

                // The corresponding whitelisted code above should be used to complete sign-in.
                MainActivity.this.enableUserManuallyInputCode();
            }

            @Override
            public void onVerificationCompleted(PhoneAuthCredential phoneAuthCredential) {
                // Sign in with the credential
                // ...
            }

            @Override
            public void onVerificationFailed(FirebaseException e) {
                 // ...
            }

        });

Kotlin + KTX

val phoneNum = "+16505554567"
val testVerificationCode = "123456"

// Whenever verification is triggered with the whitelisted number,
// provided it is not set for auto-retrieval, onCodeSent will be triggered.
PhoneAuthProvider.getInstance().verifyPhoneNumber(
        phoneNum, 30L /*timeout*/, TimeUnit.SECONDS,
        this, object : PhoneAuthProvider.OnVerificationStateChangedCallbacks() {

    override fun onCodeSent(
        verificationId: String,
        forceResendingToken: PhoneAuthProvider.ForceResendingToken
    ) {
        // Save the verification id somewhere
        // ...

        // The corresponding whitelisted code above should be used to complete sign-in.
        this@MainActivity.enableUserManuallyInputCode()
    }

    override fun onVerificationCompleted(phoneAuthCredential: PhoneAuthCredential) {
        // Sign in with the credential
        // ...
    }

    override fun onVerificationFailed(e: FirebaseException) {
        // ...
    }
})

此外,您可以通過調用調用setAutoRetrievedSmsCodeForPhoneNumber設置白名單編號及其對應的自動檢索驗證碼,來測試Android中的自動檢索流。

verifyPhoneNumber被調用時,它會觸發onVerificationCompletedPhoneAuthCredential直接。僅適用於列入白名單的電話號碼。

將應用程序發佈到Google Play商店時,請確保已禁用此功能,並且應用程序中沒有將列入白名單的電話號碼硬編碼。

爪哇

// The test phone number and code should be whitelisted in the console.
String phoneNumber = "+16505554567";
String smsCode = "123456";

FirebaseAuth firebaseAuth = FirebaseAuth.getInstance();
FirebaseAuthSettings firebaseAuthSettings = firebaseAuth.getFirebaseAuthSettings();

// Configure faking the auto-retrieval with the whitelisted numbers.
firebaseAuthSettings.setAutoRetrievedSmsCodeForPhoneNumber(phoneNumber, smsCode);

PhoneAuthProvider phoneAuthProvider = PhoneAuthProvider.getInstance();
phoneAuthProvider.verifyPhoneNumber(
        phoneNumber,
        60L,
        TimeUnit.SECONDS,
        this, /* activity */
        new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
            @Override
            public void onVerificationCompleted(PhoneAuthCredential credential) {
                // Instant verification is applied and a credential is directly returned.
                // ...
            }

            // ...
        });

Kotlin + KTX

// The test phone number and code should be whitelisted in the console.
val phoneNumber = "+16505554567"
val smsCode = "123456"

val firebaseAuth = Firebase.auth
val firebaseAuthSettings = firebaseAuth.firebaseAuthSettings

// Configure faking the auto-retrieval with the whitelisted numbers.
firebaseAuthSettings.setAutoRetrievedSmsCodeForPhoneNumber(phoneNumber, smsCode)

val phoneAuthProvider = PhoneAuthProvider.getInstance()
phoneAuthProvider.verifyPhoneNumber(
        phoneNumber,
        60L,
        TimeUnit.SECONDS,
        this, /* activity */
        object : PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
            override fun onVerificationCompleted(credential: PhoneAuthCredential) {
                // Instant verification is applied and a credential is directly returned.
                // ...
            }

            // ...
        })

下一步

用戶首次登錄後,將創建一個新的用戶帳戶並將其鏈接到該用戶登錄的憑據(即用戶名和密碼,電話號碼或身份驗證提供者信息)。此新帳戶存儲為Firebase項目的一部分,可用於在項目中的每個應用程序中識別用戶,而無論用戶如何登錄。

  • 在您的應用程序中,您可以從FirebaseUser對象獲取用戶的基本配置文件信息。請參閱管理用戶

  • 在Firebase實時數據庫和雲存儲安全規則中 ,您可以從auth變量中獲取登錄用戶的唯一用戶ID,然後使用它來控制用戶可以訪問哪些數據。

通過將身份驗證提供程序憑據鏈接到現有用戶帳戶,可以允許用戶使用多個身份驗證提供程序登錄您的應用程序

要註銷用戶,請致電signOut

爪哇

FirebaseAuth.getInstance().signOut();

Kotlin + KTX

Firebase.auth.signOut()