您可以使用Firebase身份驗證通過將SMS消息發送到用戶的電話來登錄用戶。用戶使用SMS消息中包含的一次性代碼登錄。
將電話號碼登錄添加到您的應用程序最簡單的方法是使用FirebaseUI ,它包括一個插入式登錄小部件,該小部件實現了電話號碼登錄的登錄流程以及基於密碼的聯合登錄-在。本文檔介紹瞭如何使用Firebase SDK實施電話號碼登錄流程。
在你開始之前
- 如果尚未將Firebase添加到您的Android項目中。
- 使用Firebase Android BoM ,在模塊(應用程序級)Gradle文件(通常為
app/build.gradle
)中聲明Firebase Authentication Android庫的依賴app/build.gradle
。爪哇
dependencies { // Import the BoM for the Firebase platform implementation platform('com.google.firebase:firebase-bom:26.6.0') // Declare the dependency for the Firebase Authentication library // When using the BoM, you don't specify versions in Firebase library dependencies implementation 'com.google.firebase:firebase-auth' }
通過使用Firebase Android BoM ,您的應用將始終使用Firebase Android庫的兼容版本。
(可選)不使用BoM聲明Firebase庫依賴關係
如果您選擇不使用Firebase BoM,則必須在其依賴關係行中指定每個Firebase庫版本。
請注意,如果您在應用中使用多個Firebase庫,我們強烈建議您使用BoM來管理庫版本,以確保所有版本都兼容。
dependencies { // Declare the dependency for the Firebase Authentication library // When NOT using the BoM, you must specify versions in Firebase library dependencies implementation 'com.google.firebase:firebase-auth:20.0.3' }
Kotlin + KTX
dependencies { // Import the BoM for the Firebase platform implementation platform('com.google.firebase:firebase-bom:26.6.0') // Declare the dependency for the Firebase Authentication library // When using the BoM, you don't specify versions in Firebase library dependencies implementation 'com.google.firebase:firebase-auth-ktx' }
通過使用Firebase Android BoM ,您的應用將始終使用Firebase Android庫的兼容版本。
(可選)不使用BoM聲明Firebase庫依賴關係
如果選擇不使用Firebase BoM,則必須在其依賴關係行中指定每個Firebase庫版本。
請注意,如果您在應用中使用多個Firebase庫,我們強烈建議您使用BoM來管理庫版本,以確保所有版本都兼容。
dependencies { // Declare the dependency for the Firebase Authentication library // When NOT using the BoM, you must specify versions in Firebase library dependencies implementation 'com.google.firebase:firebase-auth-ktx:20.0.3' }
- 如果您尚未將應用程序連接到Firebase項目,請從Firebase控制台進行。
- 如果您尚未在Firebase控制台中設置應用程序的SHA-1哈希,請進行設置。有關查找應用程序的SHA-1哈希的信息,請參見對您的客戶端進行身份驗證。
安全問題
僅使用電話號碼進行身份驗證雖然方便,但比其他可用方法安全性低,因為擁有電話號碼的身份可以在用戶之間輕鬆轉移。此外,在具有多個用戶配置文件的設備上,任何可以接收SMS消息的用戶都可以使用設備的電話號碼登錄到帳戶。
如果您在應用程序中使用基於電話號碼的登錄,則應將其與更安全的登錄方法一起提供,並告知用戶使用電話號碼登錄的安全權衡。
為您的Firebase項目啟用電話號碼登錄
要通過SMS登錄用戶,您必須首先為Firebase項目啟用電話號碼登錄方法:
- 在Firebase控制台中,打開“身份驗證”部分。
- 在“登錄方法”頁面上,啟用“電話號碼”登錄方法。
Firebase的電話號碼登錄請求配額足夠高,因此大多數應用程序都不會受到影響。但是,如果需要使用電話身份驗證登錄大量用戶,則可能需要升級定價計劃。請參閱定價頁面。
啟用應用驗證
要使用電話號碼身份驗證,Firebase必須能夠驗證電話號碼登錄請求是否來自您的應用程序。 Firebase身份驗證可通過兩種方式完成此任務:
- SafetyNet :如果用戶安裝了裝有Google Play服務的設備,並且Firebase身份驗證可以通過Android SafetyNet驗證該設備為合法設備,則可以繼續進行電話號碼登錄。
- 在Google Cloud Console中,為您的項目啟用Android DeviceCheck API 。將使用默認的Firebase API密鑰,並且需要允許其訪問DeviceCheck API。
- 如果您尚未指定應用程序的SHA-256指紋,請從Firebase控制台的“設置”頁面中指定。有關如何獲取應用程序的SHA-256指紋的詳細信息,請參閱身份驗證客戶端。
- reCAPTCHA驗證:在無法使用SafetyNet的情況下(例如,當用戶不支持Google Play服務或在模擬器上測試您的應用時),Firebase身份驗證會使用reCAPTCHA驗證來完成電話登錄流程。 reCAPTCHA挑戰通常可以在無需用戶解決任何問題的情況下完成。請注意,此流程要求SHA-1與您的應用程序關聯。
要啟用SafetyNet與Firebase身份驗證一起使用,請執行以下操作:
將驗證碼發送到用戶的手機
要啟動電話號碼登錄,請向用戶顯示一個界面,提示他們鍵入他們的電話號碼。法律要求各不相同,但是作為最佳實踐並為用戶設定期望,您應該告知他們,如果他們使用電話登錄,則他們可能會收到SMS消息以進行驗證,並採用標準費率。
然後,將其電話號碼傳遞給
PhoneAuthProvider.verifyPhoneNumber
方法,以請求Firebase驗證用戶的電話號碼。例如:爪哇
PhoneAuthOptions options = PhoneAuthOptions.newBuilder(mAuth) .setPhoneNumber(phoneNumber) // Phone number to verify .setTimeout(60L, TimeUnit.SECONDS) // Timeout and unit .setActivity(this) // Activity (for callback binding) .setCallbacks(mCallbacks) // OnVerificationStateChangedCallbacks .build(); PhoneAuthProvider.verifyPhoneNumber(options);
Kotlin + KTX
val options = PhoneAuthOptions.newBuilder(auth) .setPhoneNumber(phoneNumber) // Phone number to verify .setTimeout(60L, TimeUnit.SECONDS) // Timeout and unit .setActivity(this) // Activity (for callback binding) .setCallbacks(callbacks) // OnVerificationStateChangedCallbacks .build() PhoneAuthProvider.verifyPhoneNumber(options)
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 // ... } }
驗證回調
在大多數應用中,您實現
onVerificationCompleted
,onVerificationFailed
和onCodeSent
回調。您還可以實現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發送到用戶電話的驗證碼後,使用傳遞給
onCodeSent
或onCodeAutoRetrievalTimeOut
回調的驗證碼和驗證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
,通過將PhoneAuthCredential
對PhoneAuthCredential
傳遞給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審閱過程中被拒絕的風險。
- 無需任何額外的努力即可在開發環境中輕鬆進行測試,例如可以在iOS模擬器或沒有Google Play服務的Android模擬器中進行開發。
- 編寫集成測試,而不會受到通常在生產環境中應用於真實電話號碼的安全檢查的阻止。
虛構電話號碼必須符合以下要求:
- 確保您使用的電話號碼確實是虛構的,尚不存在。 Firebase身份驗證不允許您將真實用戶使用的現有電話號碼設置為測試號碼。一種選擇是使用555個帶前綴的號碼作為美國測試電話號碼,例如: +1 650-555-3434
- 電話號碼必須正確設置格式,以保證長度和其他限制。他們仍將通過與真實用戶的電話號碼相同的驗證。
- 您最多可以添加10個電話號碼進行開發。
- 使用難以猜測的測試電話號碼/代碼,並經常進行更改。
創建虛構的電話號碼和驗證碼
- 在Firebase控制台中,打開“身份驗證”部分。
- 在“登錄方法”選項卡中,如果尚未啟用,請啟用“電話”提供程序。
- 打開“電話號碼”以測試手風琴菜單。
- 提供您要測試的電話號碼,例如: +1 650-555-3434 。
- 提供該特定號碼的6位數驗證碼,例如: 654321 。
- 添加號碼。如果需要,可以將鼠標懸停在相應的行上,然後單擊垃圾桶圖標,以刪除電話號碼及其代碼。
手動測試
您可以在應用程序中直接開始使用虛構的電話號碼。這使您可以在開發階段執行手動測試,而不會遇到配額問題或節流問題。您也可以直接從iOS模擬器或Android模擬器進行測試,而無需安裝Google Play服務。
當您提供虛擬電話號碼並發送驗證碼時,不會發送任何實際的SMS。相反,您需要提供先前配置的驗證碼才能完成登錄。
登錄完成後,將使用該電話號碼創建一個Firebase用戶。該用戶具有與真實電話號碼用戶相同的行為和屬性,並且可以以相同方式訪問Realtime Database / Cloud Firestore和其他服務。在此過程中鑄造的ID令牌具有與真實電話號碼用戶相同的簽名。
另一個選擇是,如果您想進一步限制訪問權限,則可以通過對這些用戶的自定義聲明來設置測試角色,以將其區分為假用戶。
要手動觸發reCAPTCHA流進行測試,請使用
forceRecaptchaFlowForTesting()
方法。// Force reCAPTCHA flow FirebaseAuth.getInstance().getFirebaseAuthSettings().forceRecaptchaFlowForTesting();
整合測試
除了手動測試之外,Firebase身份驗證還提供API,以幫助編寫用於電話身份驗證測試的集成測試。這些API通過禁用Web中的reCAPTCHA要求和iOS中的靜默推送通知來禁用應用程序驗證。這使得在這些流程中進行自動化測試成為可能,並且更易於實現。此外,它們還有助於提供在Android上測試即時驗證流程的功能。
在Android上,在
signInWithPhoneNumber
調用之前調用setAppVerificationDisabledForTesting()
。這會自動禁用應用程序驗證,使您無需手動解決即可傳遞電話號碼。請注意,即使禁用了reCAPTCHA和/或SafetyNet,使用真實電話號碼仍將無法完成登錄。此API僅可以使用虛擬電話號碼。// Turn off phone auth app verification. FirebaseAuth.getInstance().getFirebaseAuthSettings() .setAppVerificationDisabledForTesting();
用虛構的數字調用
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. FirebaseAuth auth = FirebaseAuth.getInstance(); PhoneAuthOptions options = PhoneAuthOptions.newBuilder(auth) .setPhoneNumber(phoneNum) .setTimeout(60L, TimeUnit.SECONDS) .setActivity(this) .setCallbacks(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) { // ... } }) .build(); PhoneAuthProvider.verifyPhoneNumber(options);
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. val options = PhoneAuthOptions.newBuilder(Firebase.auth) .setPhoneNumber(phoneNum) .setTimeout(30L, TimeUnit.SECONDS) .setActivity(this) .setCallbacks(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) { // ... } }) .build() PhoneAuthProvider.verifyPhoneNumber(options)
此外,您可以通過調用
setAutoRetrievedSmsCodeForPhoneNumber
設置虛擬號碼及其相應的自動提取驗證碼,來測試Android中的自動提取setAutoRetrievedSmsCodeForPhoneNumber
。當
verifyPhoneNumber
被調用時,它會觸發onVerificationCompleted
與PhoneAuthCredential
直接。這僅適用於虛構的電話號碼。將應用程序發佈到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); PhoneAuthOptions options = PhoneAuthOptions.newBuilder(firebaseAuth) .setPhoneNumber(phoneNumber) .setTimeout(60L, TimeUnit.SECONDS) .setActivity(this) .setCallbacks(new PhoneAuthProvider.OnVerificationStateChangedCallbacks() { @Override public void onVerificationCompleted(PhoneAuthCredential credential) { // Instant verification is applied and a credential is directly returned. // ... } // ... }) .build(); PhoneAuthProvider.verifyPhoneNumber(options);
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 options = PhoneAuthOptions.newBuilder(firebaseAuth) .setPhoneNumber(phoneNumber) .setTimeout(60L, TimeUnit.SECONDS) .setActivity(this) .setCallbacks(object : PhoneAuthProvider.OnVerificationStateChangedCallbacks() { override fun onVerificationCompleted(credential: PhoneAuthCredential) { // Instant verification is applied and a credential is directly returned. // ... } // ... }) .build() PhoneAuthProvider.verifyPhoneNumber(options)
下一步
用戶首次登錄後,將創建一個新的用戶帳戶並將其鏈接到用戶登錄的憑據(即用戶名和密碼,電話號碼或身份驗證提供者信息)。此新帳戶存儲為Firebase項目的一部分,無論用戶如何登錄,都可以用來在項目中的每個應用程序中識別用戶。
在您的應用中,您可以從
FirebaseUser
對象獲取用戶的基本配置文件信息。請參閱管理用戶。在Firebase實時數據庫和雲存儲安全規則中,您可以從
auth
變量獲取登錄用戶的唯一用戶ID,並使用它來控制用戶可以訪問哪些數據。
通過將身份驗證提供程序憑據鏈接到現有用戶帳戶,可以允許用戶使用多個身份驗證提供程序登錄您的應用程序。
要註銷用戶,請致電
signOut
:爪哇
FirebaseAuth.getInstance().signOut();
Kotlin + KTX
Firebase.auth.signOut()
Except as otherwise noted, the content of this page is licensed under the Creative Commons Attribution 4.0 License, and code samples are licensed under the Apache 2.0 License. For details, see the Google Developers Site Policies. Java is a registered trademark of Oracle and/or its affiliates.
Last updated 2020-12-16 UTC.