Android 中获取和读取短信验证码

方法一:通过 SMS Retriever API

SMS Retriever API 是 Google 提供的一种安全的方式,可以从系统中获取不需要权限的短信验证码。这种方式不需要请求 READ_SMS 权限,非常适合处理短信验证码的情况。

1. 在 build.gradle 中添加依赖

cs 复制代码
dependencies {
    implementation 'com.google.android.gms:play-services-auth-api-phone:18.0.1'
}

2. 获取应用的哈希值

Google 的 SMS Retriever API 会通过一个特定的哈希值来验证短信的来源。获取这个哈希值后,将其附加在发送的短信中。

java 复制代码
private String getAppHashKey() {
    try {
        PackageInfo info = getPackageManager().getPackageInfo(
                getPackageName(),
                PackageManager.GET_SIGNATURES);
        for (Signature signature : info.signatures) {
            MessageDigest md = MessageDigest.getInstance("SHA");
            md.update(signature.toByteArray());
            return Base64.encodeToString(md.digest(), Base64.NO_WRAP).substring(0, 9);
        }
    } catch (PackageManager.NameNotFoundException | NoSuchAlgorithmException e) {
        e.printStackTrace();
    }
    return null;
}

3. 开启 SMS Retriever

java 复制代码
SmsRetrieverClient client = SmsRetriever.getClient(this);
Task<Void> task = client.startSmsRetriever();
task.addOnSuccessListener(new OnSuccessListener<Void>() {
    @Override
    public void onSuccess(Void aVoid) {
        // 成功启动 SMS Retriever
    }
});
task.addOnFailureListener(new OnFailureListener() {
    @Override
    public void onFailure(@NonNull Exception e) {
        // 启动失败
    }
});

4. 接收短信

注册一个 BroadcastReceiver 来监听 SMS Retriever API 的广播

java 复制代码
public class MySmsBroadcastReceiver extends BroadcastReceiver {
    private SmsReceiverListener listener;

    public void setSmsReceiverListener(SmsReceiverListener listener) {
        this.listener = listener;
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        if (SmsRetriever.SMS_RETRIEVED_ACTION.equals(intent.getAction())) {
            Bundle extras = intent.getExtras();
            Status status = (Status) extras.get(SmsRetriever.EXTRA_STATUS);
            switch (status.getStatusCode()) {
                case CommonStatusCodes.SUCCESS:
                    // 获取短信
                    String message = (String) extras.get(SmsRetriever.EXTRA_SMS_MESSAGE);
                    if (listener != null) {
                        listener.onSmsReceived(message);
                    }
                    break;
                case CommonStatusCodes.TIMEOUT:
                    // 超时
                    break;
            }
        }
    }
}

5. 处理短信中的验证码

使用正则表达式提取验证码

java 复制代码
Pattern pattern = Pattern.compile("\\d{6}");
Matcher matcher = pattern.matcher(message);
if (matcher.find()) {
    String code = matcher.group(0);
    // 使用验证码
}

短信格式:

通过 SMS Retriever API,需要确保短信的格式包含应用的哈希值,例如:

python 复制代码
Your verification code is 123456.
<#> Your App Name: Use this code to verify your phone number. 
abc123xyz (Your App's hash key)

在使用 SMS Retriever API 获取短信验证码时,短信必须包含应用的哈希值,这样 Android 系统才能识别出该短信是由你应用发送的,并自动从系统短信中提取该短信

abc123xyz 是通过应用的签名生成的哈希值

哈希值用于验证短信的来源,确保是从与你应用关联的服务器发送的短信,而不是其他来源伪造的。系统通过这个哈希值自动识别和提取短信内容,不需要用户手动输入验证码。

6.哈希值生成步骤

  • 获取应用的签名证书: 应用的哈希值是根据应用的签名证书生成的。在 Android 中,每个应用都有一个签名证书,用来唯一标识应用。SMS Retriever API 通过应用的签名证书来生成哈希值。

  • 使用 Java 代码生成哈希值: 可以在应用的 ActivityUtils 类中调用它

java 复制代码
import android.content.Context;
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;

public class AppSignatureHelper {

    private static final String HASH_TYPE = "SHA-256";
    private static final int NUM_HASHED_BYTES = 9;
    private static final int NUM_BASE64_CHAR = 11;

    public static String getAppHashKey(Context context) {
        try {
            // 获取应用包的信息
            PackageInfo packageInfo = context.getPackageManager().getPackageInfo(
                    context.getPackageName(),
                    PackageManager.GET_SIGNATURES);
            // 遍历签名信息
            for (Signature signature : packageInfo.signatures) {
                byte[] signatureBytes = signature.toByteArray();
                // 通过SHA-256进行哈希计算
                MessageDigest md = MessageDigest.getInstance(HASH_TYPE);
                md.update(signatureBytes);
                byte[] digest = md.digest();

                // 将哈希值编码为Base64格式,取前11位作为哈希值
                return Base64.encodeToString(digest, Base64.NO_WRAP).substring(0, NUM_BASE64_CHAR);
            }
        } catch (PackageManager.NameNotFoundException | NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return null;
    }
}
  • 调用哈希值生成方法

在应用启动时或者短信发送前调用此方法,获取哈希值。这个哈希值需要包含在发送的短信中

java 复制代码
String hashKey = AppSignatureHelper.getAppHashKey(context);
Log.d("AppHashKey", "Hash Key: " + hashKey);
  • 将哈希值添加到短信内容中

将生成的哈希值附加到短信的末尾

java 复制代码
Your verification code is 123456.
<#> MyApp: Use this code to verify your phone number.
abc123xyz (Your App's hash key)

方法二:通过读取短信权限 (READ_SMS)

这种方法需要获取读取短信的权限,但由于隐私和安全问题,Google 对读取短信的权限要求非常严格,并且 Android 6.0 以上的版本还需要动态申请权限。

1. 在 AndroidManifest.xml 中添加权限

XML 复制代码
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.READ_SMS" />

2. 动态申请权限(针对 Android 6.0 及以上)

java 复制代码
public class SmsReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (Telephony.Sms.Intents.SMS_RECEIVED_ACTION.equals(intent.getAction())) {
            Bundle bundle = intent.getExtras();
            if (bundle != null) {
                Object[] pdus = (Object[]) bundle.get("pdus");
                for (Object pdu : pdus) {
                    SmsMessage message = SmsMessage.createFromPdu((byte[]) pdu);
                    String sender = message.getDisplayOriginatingAddress();
                    String content = message.getMessageBody();
                    // 处理短信内容,提取验证码
                }
            }
        }
    }
}

3. 注册一个 BroadcastReceiver 来监听收到的短信

java 复制代码
public class SmsReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (Telephony.Sms.Intents.SMS_RECEIVED_ACTION.equals(intent.getAction())) {
            Bundle bundle = intent.getExtras();
            if (bundle != null) {
                Object[] pdus = (Object[]) bundle.get("pdus");
                for (Object pdu : pdus) {
                    SmsMessage message = SmsMessage.createFromPdu((byte[]) pdu);
                    String sender = message.getDisplayOriginatingAddress();
                    String content = message.getMessageBody();
                    // 处理短信内容,提取验证码
                }
            }
        }
    }
}

4. 提取短信验证码

SMS Retriever API 类似,可以使用正则表达式提取验证码。

总结

  1. SMS Retriever API 是更加推荐的方法,因为它不需要读取短信的权限,更加安全。
  2. 读取短信权限 方法需要申请敏感权限,使用较少。
相关推荐
zhangphil1 小时前
Android简洁缩放Matrix实现图像马赛克,Kotlin
android·kotlin
m0_512744641 小时前
极客大挑战2024-web-wp(详细)
android·前端
lw向北.1 小时前
Qt For Android之环境搭建(Qt 5.12.11 Qt下载SDK的处理方案)
android·开发语言·qt
不爱学习的啊Biao1 小时前
【13】MySQL如何选择合适的索引?
android·数据库·mysql
Clockwiseee2 小时前
PHP伪协议总结
android·开发语言·php
mmsx8 小时前
android sqlite 数据库简单封装示例(java)
android·java·数据库
众拾达人11 小时前
Android自动化测试实战 Java篇 主流工具 框架 脚本
android·java·开发语言
吃着火锅x唱着歌12 小时前
PHP7内核剖析 学习笔记 第四章 内存管理(1)
android·笔记·学习
_Shirley13 小时前
鸿蒙设置app更新跳转华为市场
android·华为·kotlin·harmonyos·鸿蒙
hedalei15 小时前
RK3576 Android14编译OTA包提示java.lang.UnsupportedClassVersionError问题
android·android14·rk3576