You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Android Java项目中基于Firebase实现登录时的SMS 2FA后续流程

Firebase SMS多因素认证(2FA)OTP验证页面实现方案

以下是完整的实现步骤,涵盖OTP页面创建、权限配置、跳转逻辑和OTP验证流程:

1. 创建OTP输入Activity

1.1 布局文件(res/layout/activity_otp_verify.xml)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp"
    android:gravity="center">

    <EditText
        android:id="@+id/et_otp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="请输入6位验证码"
        android:inputType="number"
        android:maxLength="6"
        android:textSize="18sp" />

    <Button
        android:id="@+id/btn_verify_otp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="验证验证码"
        android:layout_marginTop="16dp" />

    <Button
        android:id="@+id/btn_resend_otp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="重新发送验证码"
        android:layout_marginTop="8dp" />
</LinearLayout>

1.2 Java代码(OtpVerifyActivity.java)

import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseException;
import com.google.firebase.auth.FirebaseUser;
import com.google.firebase.auth.MultiFactorAssertion;
import com.google.firebase.auth.MultiFactorResolver;
import com.google.firebase.auth.PhoneAuthCredential;
import com.google.firebase.auth.PhoneAuthOptions;
import com.google.firebase.auth.PhoneAuthProvider;
import com.google.firebase.auth.PhoneMultiFactorGenerator;
import com.google.firebase.auth.PhoneMultiFactorInfo;
import java.util.concurrent.TimeUnit;

public class OtpVerifyActivity extends AppCompatActivity {
    private static final int SMS_PERMISSION_REQUEST_CODE = 100;
    private EditText etOtp;
    private Button btnVerifyOtp, btnResendOtp;
    private MultiFactorResolver multiFactorResolver;
    private PhoneMultiFactorInfo selectedPhoneHint;
    private String verificationId;
    private PhoneAuthProvider.ForceResendingToken resendingToken;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_otp_verify);

        // 检查并申请SMS权限
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.SEND_SMS) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.SEND_SMS}, SMS_PERMISSION_REQUEST_CODE);
        } else {
            initViewsAndLogic();
        }
    }

    private void initViewsAndLogic() {
        // 初始化视图
        etOtp = findViewById(R.id.et_otp);
        btnVerifyOtp = findViewById(R.id.btn_verify_otp);
        btnResendOtp = findViewById(R.id.btn_resend_otp);

        // 获取从登录页传递的参数
        multiFactorResolver = getIntent().getParcelableExtra("MULTI_FACTOR_RESOLVER");
        selectedPhoneHint = (PhoneMultiFactorInfo) getIntent().getParcelableExtra("SELECTED_PHONE_HINT");

        // 初始发送OTP
        sendSmsOtp();

        // 验证按钮点击事件
        btnVerifyOtp.setOnClickListener(v -> {
            String otp = etOtp.getText().toString().trim();
            if (TextUtils.isEmpty(otp) || otp.length() != 6) {
                Toast.makeText(OtpVerifyActivity.this, "请输入有效的6位验证码", Toast.LENGTH_SHORT).show();
                return;
            }
            verifyOtp(otp);
        });

        // 重新发送按钮点击事件
        btnResendOtp.setOnClickListener(v -> {
            if (resendingToken != null) {
                resendSmsOtp();
            } else {
                sendSmsOtp();
            }
        });
    }

    // 发送SMS OTP
    private void sendSmsOtp() {
        PhoneAuthProvider.getInstance().verifyPhoneNumber(
                PhoneAuthOptions.newBuilder(FirebaseAuth.getInstance())
                        .setPhoneNumber(selectedPhoneHint.getPhoneNumber())
                        .setTimeout(60L, TimeUnit.SECONDS)
                        .setActivity(this)
                        .setCallbacks(new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
                            @Override
                            public void onVerificationCompleted(PhoneAuthCredential credential) {
                                // 自动验证完成,直接使用凭证登录
                                verifyOtp(credential.getSmsCode());
                            }

                            @Override
                            public void onVerificationFailed(FirebaseException e) {
                                Toast.makeText(OtpVerifyActivity.this, "验证码发送失败:" + e.getMessage(), Toast.LENGTH_SHORT).show();
                            }

                            @Override
                            public void onCodeSent(String s, PhoneAuthProvider.ForceResendingToken token) {
                                super.onCodeSent(s, token);
                                verificationId = s;
                                resendingToken = token;
                                Toast.makeText(OtpVerifyActivity.this, "验证码已发送至您的手机", Toast.LENGTH_SHORT).show();
                            }
                        })
                        .build()
        );
    }

    // 重新发送SMS OTP
    private void resendSmsOtp() {
        PhoneAuthProvider.getInstance().verifyPhoneNumber(
                PhoneAuthOptions.newBuilder(FirebaseAuth.getInstance())
                        .setPhoneNumber(selectedPhoneHint.getPhoneNumber())
                        .setTimeout(60L, TimeUnit.SECONDS)
                        .setActivity(this)
                        .setCallbacks(new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
                            @Override
                            public void onVerificationCompleted(PhoneAuthCredential credential) {
                                verifyOtp(credential.getSmsCode());
                            }

                            @Override
                            public void onVerificationFailed(FirebaseException e) {
                                Toast.makeText(OtpVerifyActivity.this, "验证码重发失败:" + e.getMessage(), Toast.LENGTH_SHORT).show();
                            }

                            @Override
                            public void onCodeSent(String s, PhoneAuthProvider.ForceResendingToken token) {
                                super.onCodeSent(s, token);
                                verificationId = s;
                                resendingToken = token;
                                Toast.makeText(OtpVerifyActivity.this, "验证码已重新发送", Toast.LENGTH_SHORT).show();
                            }
                        })
                        .setForceResendingToken(resendingToken)
                        .build()
        );
    }

    // 验证OTP并完成登录
    private void verifyOtp(String otp) {
        if (verificationId == null) {
            Toast.makeText(this, "请先获取验证码", Toast.LENGTH_SHORT).show();
            return;
        }

        PhoneAuthCredential credential = PhoneAuthProvider.getCredential(verificationId, otp);
        MultiFactorAssertion assertion = PhoneMultiFactorGenerator.getAssertion(credential);

        multiFactorResolver.resolveSignIn(assertion)
                .addOnCompleteListener(task -> {
                    if (task.isSuccessful()) {
                        // MFA验证成功,登录完成
                        FirebaseUser user = task.getResult().getUser();
                        Toast.makeText(OtpVerifyActivity.this, "登录成功", Toast.LENGTH_SHORT).show();
                        // 跳转到主页面
                        startActivity(new Intent(OtpVerifyActivity.this, MainActivity.class));
                        finish();
                    } else {
                        Toast.makeText(OtpVerifyActivity.this, "验证码验证失败:" + task.getException().getMessage(), Toast.LENGTH_SHORT).show();
                    }
                });
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == SMS_PERMISSION_REQUEST_CODE) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                initViewsAndLogic();
            } else {
                Toast.makeText(this, "需要授予发送SMS权限才能完成验证", Toast.LENGTH_SHORT).show();
                finish();
            }
        }
    }
}

2. 配置权限与Activity注册

在AndroidManifest.xml中添加以下内容:

<!-- 发送SMS权限 -->
<uses-permission android:name="android.permission.SEND_SMS" />
<!-- 可选:接收/读取SMS权限,用于自动填充验证码 -->
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.READ_SMS" />

<!-- 注册OTP验证Activity -->
<activity android:name=".OtpVerifyActivity" />

3. 修改登录页跳转逻辑

在登录页的signInWithEmailAndPassword回调中,处理FirebaseAuthMultiFactorException并跳转到OTP页面:

FirebaseAuth.getInstance()
    .signInWithEmailAndPassword(email, password)
    .addOnCompleteListener(new OnCompleteListener<AuthResult>() {
        @Override
        public void onComplete(@NonNull Task<AuthResult> task) {
            if (task.isSuccessful()) {
                // 用户未启用第二因素认证,登录成功
                startActivity(new Intent(LoginActivity.this, MainActivity.class));
                finish();
                return;
            }

            if (task.getException() instanceof FirebaseAuthMultiFactorException) {
                FirebaseAuthMultiFactorException e = (FirebaseAuthMultiFactorException) task.getException();
                MultiFactorResolver multiFactorResolver = e.getResolver();

                // 若存在多个认证方式,可弹窗让用户选择,这里默认取第一个SMS方式
                for (MultiFactorInfo hint : multiFactorResolver.getHints()) {
                    if (hint instanceof PhoneMultiFactorInfo) {
                        PhoneMultiFactorInfo phoneHint = (PhoneMultiFactorInfo) hint;
                        Intent intent = new Intent(LoginActivity.this, OtpVerifyActivity.class);
                        intent.putExtra("MULTI_FACTOR_RESOLVER", multiFactorResolver);
                        intent.putExtra("SELECTED_PHONE_HINT", phoneHint);
                        startActivity(intent);
                        finish();
                        return;
                    }
                }
                Toast.makeText(LoginActivity.this, "未找到可用的SMS认证方式", Toast.LENGTH_SHORT).show();
            } else {
                // 其他登录失败情况
                Toast.makeText(LoginActivity.this, "登录失败:" + task.getException().getMessage(), Toast.LENGTH_SHORT).show();
            }
        }
    });

关键说明

  • 权限处理:Android 6.0及以上需动态申请SEND_SMS权限,否则无法发送验证码。
  • 自动验证:onVerificationCompleted回调会在系统自动获取到验证码时触发,无需用户手动输入。
  • 多方式选择:若用户绑定了多个第二因素认证方式,可添加弹窗让用户选择具体使用的方式,而非默认取第一个。

内容的提问来源于stack exchange,提问作者Bhumesh Thakur

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 04:57:33