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

无法提取短信OTP,如何在不申请READ_SMS权限下实现验证?

解决无READ_SMS权限下的OTP提取问题

在Android 10(API 29)及以上版本,RECEIVE_SMS权限已经不足以让广播接收器获取短信内容——系统限制了后台组件对短信的访问,这就是你现有代码无法提取OTP的原因。要在不申请全短信访问权限的情况下提取OTP,推荐使用Google官方提供的两种方案:


方案一:SMS Retriever API(全自动,无权限要求)

这个API无需任何短信权限,只要你的验证短信包含应用的哈希值,就能自动监听并提取OTP。

步骤1:添加依赖

在app模块的build.gradle中添加:

dependencies {
    implementation "com.google.android.gms:play-services-auth-api-phone:18.0.1"
}

步骤2:启动OTP监听

在你的Activity/Fragment中初始化并启动监听:

import com.google.android.gms.auth.api.phone.SmsRetriever;
import com.google.android.gms.auth.api.phone.SmsRetrieverClient;
import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;

// 初始化Retriever客户端
SmsRetrieverClient client = SmsRetriever.getClient(this);

// 启动监听
client.startSmsRetriever()
    .addOnSuccessListener(aVoid -> {
        // 监听已启动,等待符合格式的短信
    })
    .addOnFailureListener(e -> {
        // 监听启动失败,处理错误
    });

步骤3:确保短信格式符合要求

验证短信必须包含应用的哈希值,格式示例:

Your OTP code is 123456. FA+9qCX9VSu

哈希值可通过代码生成(仅测试用,正式环境建议后端生成):

import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.util.Base64;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

private String getAppHash() {
    try {
        PackageInfo packageInfo = getPackageManager().getPackageInfo(
            getPackageName(), PackageManager.GET_SIGNATURES
        );
        for (Signature signature : packageInfo.signatures) {
            MessageDigest md = MessageDigest.getInstance("SHA-256");
            md.update(signature.toByteArray());
            return Base64.encodeToString(
                md.digest(), Base64.URL_SAFE | Base64.NO_PADDING | Base64.NO_WRAP
            );
        }
    } catch (PackageManager.NameNotFoundException | NoSuchAlgorithmException e) {
        e.printStackTrace();
    }
    return null;
}

步骤4:实现广播接收器处理结果

创建接收器接收Retriever返回的短信:

public class SmsRetrieverReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (SmsRetriever.SMS_RETRIEVED_ACTION.equals(intent.getAction())) {
            Bundle extras = intent.getExtras();
            com.google.android.gms.common.api.Status status = 
                (com.google.android.gms.common.api.Status) extras.get(SmsRetriever.EXTRA_STATUS);

            switch (status.getStatusCode()) {
                case com.google.android.gms.common.api.CommonStatusCodes.SUCCESS:
                    // 获取短信内容并提取OTP
                    String message = (String) extras.get(SmsRetriever.EXTRA_SMS_MESSAGE);
                    String otp = message.replaceAll("[^0-9]", "");
                    // 将OTP传递给业务逻辑
                    break;
                case com.google.android.gms.common.api.CommonStatusCodes.TIMEOUT:
                    // 超时未收到短信,处理逻辑
                    break;
            }
        }
    }
}

在Manifest中注册接收器:

<receiver android:name=".SmsRetrieverReceiver">
    <intent-filter>
        <action android:name="com.google.android.gms.auth.api.phone.SMS_RETRIEVED"/>
    </intent-filter>
</receiver>

如果无法修改短信格式(比如后端无法添加哈希值),可以用这个API:系统会弹出对话框让用户选择是否授权读取最近的OTP短信,无需READ_SMS权限。

步骤1:添加依赖(同Retriever API)

dependencies {
    implementation "com.google.android.gms:play-services-auth-api-phone:18.0.1"
}

步骤2:启动用户授权请求

import com.google.android.gms.auth.api.phone.SmsRetriever;
import com.google.android.gms.auth.api.phone.SmsRetrieverClient;

SmsRetrieverClient client = SmsRetriever.getClient(this);
// 可指定发送者号码,null表示监听所有包含OTP的短信
client.startSmsUserConsent(null)
    .addOnSuccessListener(aVoid -> {
        // 请求已发送,等待用户授权
    })
    .addOnFailureListener(e -> {
        // 请求失败,处理错误
    });

步骤3:处理授权结果

创建接收器接收用户授权后的短信:

public class SmsUserConsentReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (SmsRetriever.SMS_USER_CONSENT_ACTION.equals(intent.getAction())) {
            String message = intent.getStringExtra(SmsRetriever.EXTRA_SMS_MESSAGE);
            if (message != null) {
                String otp = message.replaceAll("[^0-9]", "");
                // 处理OTP
                abortBroadcast(); // 防止其他应用接收该短信
            }
        }
    }
}

在Manifest中注册接收器:

<receiver android:name=".SmsUserConsentReceiver">
    <intent-filter>
        <action android:name="com.google.android.gms.auth.api.phone.SMS_USER_CONSENT"/>
    </intent-filter>
</receiver>

原代码失效原因

从Android 10开始,系统收紧了短信权限:RECEIVE_SMS权限仅能触发广播告知有短信到达,但无法获取短信的具体内容(pdus数组会为空)。只有READ_SMS权限能获取全内容,但会触发隐私警告,因此官方推荐使用上述两种API替代。

内容的提问来源于stack exchange,提问作者Ayush Sharma

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 10:22:47