本文档介绍如何向 Android 应用添加短信多重身份验证。
多重身份验证可提高应用的安全性。虽然攻击者通常会窃取密码和社交帐号,但拦截短信的难度要大得多。
准备工作
启用支持多重身份验证的提供商。其中包括:
- 电子邮件和密码
- 通过电子邮件发送链接
- Google Play
- GitHub
- Microsoft
- Yahoo
- 领英
确保您的应用正在验证用户的电子邮件。MFA 要求电子邮件验证。这可防止恶意操作者使用别人的电子邮件注册服务,然后通过添加第二重身份验证阻止真正的所有者登录。
在 Firebase 控制台中注册应用的 SHA-1 哈希(您的更改将自动转移到 Google Cloud Identity Platform)。
按照对客户端进行身份验证中的步骤获取应用的 SHA-1 哈希。
打开 Firebase 控制台。
转到项目设置。
在您的应用 (Your apps) 下,点击 Android 图标。
按照引导步骤添加 SHA-1 哈希。
启用多重身份验证
转到 Cloud Console 中的 Identity Platform MFA 页面。
转到 MFA 页面在标题为基于短信的多重身份验证框中,点击启用。
输入您将用于测试应用的电话号码。虽然并非必需,但我们强烈建议您注册测试电话号码,以免在开发过程中被节流。
如果您尚未授权应用的网域,请点击右侧的添加网域将其添加到允许列表中。
点击保存。
选择注册模式
您可以选择应用是否要求多重身份验证,以及何时和如何注册用户。一些常见模式包括:
在注册过程中注册用户的第二重身份验证。如果应用要求所有用户进行多重身份验证,请使用此方法。
提供可在注册期间跳过第二重身份验证注册的选项。如果应用鼓励但不要求进行多重身份验证,可能更适合这种方法。
提供从用户的帐号或个人资料管理页面(而不是注册屏幕)添加第二重身份验证的功能。这样可以使注册过程更顺畅,同时仍可为安全敏感用户提供多重身份验证。
如果用户希望访问安全性要求更高的功能,再要求添加第二重身份验证。
注册第二重身份验证
要为用户注册新的第二重身份验证,请执行以下操作:
重新验证用户身份。
让用户输入自己的电话号码。
为用户获取多重身份验证会话:
user.getMultiFactor().getSession() .addOnCompleteListener( new OnCompleteListener<MultiFactorSession>() { @Override public void onComplete(@NonNull Task<MultiFactorSession> task) { if (task.isSuccessful()) { MultiFactorSession multiFactorSession = task.getResult(); } } });
构建一个
OnVerificationStateChangedCallbacks
对象以处理验证过程中的不同事件:OnVerificationStateChangedCallbacks callbacks = new 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. You can disable this feature by calling // PhoneAuthOptions.builder#requireSmsValidation(true) when building // the options to pass to PhoneAuthProvider#verifyPhoneNumber(). // 2) Auto-retrieval. On some devices, Google Play services can // automatically detect the incoming verification SMS and perform // verification without user action. this.credential = credential; } @Override public void onVerificationFailed(FirebaseException e) { // This callback is invoked in response to invalid requests for // verification, like an incorrect phone number. 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( String verificationId, 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. // Save the verification ID and resending token for later use. this.verificationId = verificationId; this.forceResendingToken = token; // ... } };
使用用户的电话号码、多重身份验证会话和回调初始化
PhoneInfoOptions
对象:PhoneAuthOptions phoneAuthOptions = PhoneAuthOptions.newBuilder() .setPhoneNumber(phoneNumber) .setTimeout(30L, TimeUnit.SECONDS) .setMultiFactorSession(MultiFactorSession) .setCallbacks(callbacks) .build();
默认情况下,系统会启用即时验证。要将其停用,请添加对
requireSmsValidation(true)
的调用。向用户的手机发送验证消息:
PhoneAuthProvider.verifyPhoneNumber(phoneAuthOptions);
(可选)最佳做法是事先告知用户他们会收到短信,并需按标准费率支付短信费用。
短信验证码发出后,要求用户验证该验证码:
// Ask user for the verification code. PhoneAuthCredential credential = PhoneAuthProvider.getCredential(verificationId, verificationCode);
使用
PhoneAuthCredential
初始化MultiFactorAssertion
对象:MultiFactorAssertion multiFactorAssertion = PhoneMultiFactorGenerator.getAssertion(credential);
完成注册。(可选)您可以为第二重身份验证指定显示名。这对于具有多个第二重身份验证的用户非常有用,因为电话号码在身份验证流程中会被遮盖(例如 +1******1234)。
// Complete enrollment. This will update the underlying tokens // and trigger ID token change listener. FirebaseAuth.getInstance() .getCurrentUser() .getMultiFactor() .enroll(multiFactorAssertion, "My personal phone number") .addOnCompleteListener( new OnCompleteListener<Void>() { @Override public void onComplete(@NonNull Task<Void> task) { // ... } });
以下代码展示了注册第二重身份验证的完整示例:
MultiFactorAssertion multiFactorAssertion = PhoneMultiFactorGenerator.getAssertion(credential);
user.getMultiFactor().getSession()
.addOnCompleteListener(
new OnCompleteListener<MultiFactorSession>() {
@Override
public void onComplete(@NonNull Task<MultiFactorSession> task) {
if (task.isSuccessful()) {
MultiFactorSession multiFactorSession = task.getResult();
PhoneAuthOptions phoneAuthOptions =
PhoneAuthOptions.newBuilder()
.setPhoneNumber(phoneNumber)
.setTimeout(30L, TimeUnit.SECONDS)
.setMultiFactorSession(multiFactorSession)
.setCallbacks(callbacks)
.build();
// Send SMS verification code.
PhoneAuthProvider.verifyPhoneNumber(phoneAuthOptions);
}
}
});
// Ask user for the verification code.
PhoneAuthCredential credential =
PhoneAuthProvider.getCredential(verificationId, verificationCode);
MultiFactorAssertion multiFactorAssertion = PhoneMultiFactorGenerator.getAssertion(credential);
// Complete enrollment.
FirebaseAuth.getInstance()
.getCurrentUser()
.getMultiFactor()
.enroll(multiFactorAssertion, "My personal phone number")
.addOnCompleteListener(
new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task) {
// ...
}
});
恭喜!您已成功为用户注册了第二重身份验证。
让用户通过第二重身份验证登录
要让用户通过双重身份验证(包含短信验证)登录,请执行以下操作:
让用户以第一重身份验证登录,然后捕获
FirebaseAuthMultiFactorException
异常。此错误包含一个解析器,您可以使用该解析器获取用户注册的第二重身份验证。它还包含一个底层会话,可证明用户已成功通过其第一重身份验证。例如,如果用户的第一重身份验证是电子邮件和密码:
FirebaseAuth.getInstance() .signInWithEmailAndPassword(email, password) .addOnCompleteListener( new OnCompleteListener<AuthResult>() { @Override public void onComplete(@NonNull Task<AuthResult> task) { if (task.isSuccessful()) { // User is not enrolled with a second factor and is successfully // signed in. // ... return; } if (task.getException() instanceof FirebaseAuthMultiFactorException) { // The user is a multi-factor user. Second factor challenge is // required. MultiFactorResolver multiFactorResolver = task.getException().getResolver(); // ... } else { // Handle other errors such as wrong password. } } });
如果用户的第一重身份验证是联合提供商(例如 OAuth),则在调用
startActivityForSignInWithProvider()
后捕获错误。如果用户注册了多个第二重身份验证,询问他们要使用哪一个:
// Ask user which second factor to use. // You can get the masked phone number using // resolver.getHints().get(selectedIndex).getPhoneNumber() // You can get the display name using // resolver.getHints().get(selectedIndex).getDisplayName() if (resolver.getHints().get(selectedIndex).getFactorId() == PhoneMultiFactorGenerator.FACTOR_ID) { // User selected a phone second factor. MultiFactorInfo selectedHint = multiFactorResolver.getHints().get(selectedIndex); } else { // Unsupported second factor. // Note that only phone second factors are currently supported. }
使用提示和多重身份验证会话初始化
PhoneAuthOptions
对象。这些值包含在附加到FirebaseAuthMultiFactorException
的解析器中。PhoneAuthOptions phoneAuthOptions = PhoneAuthOptions.newBuilder() .setMultiFactorHint(selectedHint) .setTimeout(30L, TimeUnit.SECONDS) .setMultiFactorSession(multiFactorResolver.getSession()) .setCallbacks(callbacks) // Optionally disable instant verification. // .requireSmsValidation(true) .build();
向用户的手机发送验证消息:
// Send SMS verification code PhoneAuthProvider.verifyPhoneNumber(phoneAuthOptions);
短信验证码发出后,要求用户验证该验证码:
// Ask user for the verification code. PhoneAuthCredential credential = PhoneAuthProvider.getCredential(verificationId, verificationCode);
使用
PhoneAuthCredential
初始化MultiFactorAssertion
对象:MultiFactorAssertion multiFactorAssertion = PhoneMultiFactorGenerator.getAssertion(credential);
调用
resolver.resolveSignIn()
以完成第二重身份验证。然后,您可以访问原始登录结果,其中包括特定于提供商的标准数据和身份验证凭据:multiFactorResolver .resolveSignIn(multiFactorAssertion) .addOnCompleteListener( new OnCompleteListener<AuthResult>() { @Override public void onComplete(@NonNull Task<AuthResult> task) { if (task.isSuccessful()) { AuthResult authResult = task.getResult(); // AuthResult will also contain the user, additionalUserInfo, // and an optional credential (null for email/password) // associated with the first factor sign-in. // For example, if the user signed in with Google as a first // factor, authResult.getAdditionalUserInfo() will contain data // related to Google provider that the user signed in with. // authResult.getCredential() will contain the Google OAuth // credential. // authResult.getCredential().getAccessToken() will contain the // Google OAuth access token. // authResult.getCredential().getIdToken() contains the Google // OAuth ID token. } } });
以下代码展示了有关让多重身份验证用户登录的完整示例:
FirebaseAuth.getInstance()
.signInWithEmailAndPassword(email, password)
.addOnCompleteListener(
new OnCompleteListener<AuthResult>() {
@Override
public void onComplete(@NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
// User is not enrolled with a second factor and is successfully
// signed in.
// ...
return;
}
if (task.getException() instanceof FirebaseAuthMultiFactorException) {
FirebaseAuthMultiFactorException e =
(FirebaseAuthMultiFactorException) task.getException();
MultiFactorResolver multiFactorResolver = e.getResolver();
// Ask user which second factor to use.
MultiFactorInfo selectedHint =
multiFactorResolver.getHints().get(selectedIndex);
// Send the SMS verification code.
PhoneAuthProvider.verifyPhoneNumber(
PhoneAuthOptions.newBuilder()
.setActivity(this)
.setMultiFactorSession(multiFactorResolver.getSession())
.setMultiFactorHint(selectedtHint)
.setCallbacks(generateCallbacks())
.setTimeout(30L, TimeUnit.SECONDS)
.build());
// Ask user for the SMS verification code.
PhoneAuthCredential credential =
PhoneAuthProvider.getCredential(verificationId, verificationCode);
// Initialize a MultiFactorAssertion object with the
// PhoneAuthCredential.
MultiFactorAssertion multiFactorAssertion =
PhoneMultiFactorGenerator.getAssertion(credential);
// Complete sign-in.
multiFactorResolver
.resolveSignIn(multiFactorAssertion)
.addOnCompleteListener(
new OnCompleteListener<AuthResult>() {
@Override
public void onComplete(@NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
// User successfully signed in with the
// second factor phone number.
}
// ...
}
});
} else {
// Handle other errors such as wrong password.
}
}
});
恭喜!您已成功让使用多重身份验证的用户登录。
后续步骤
- 使用 Admin SDK 以编程方式管理多重身份验证用户。