当前位置: 首页 > article >正文

Android 短信验证码自动填充

本文主要介绍国外google上线的app 短信自动填充方案。

本方案主要是使用google提出的,防止开发者使用SMS相关权限造成的用户信息泄露

目录

注意点:

1、本方式不适合华为手机,华为有自己的获取方式

2、本方式不需要添加任何短信权限

3、项目版本

开发成本:

Android代码

1、导库

1)在项目根目录的build.gradle中添加下面代码

2)项目级build.gradle引用库

2、注册广播

1)静态注册

2)动态注册

3、启动监听

4、广播代码

其他开发成本

短信模板 (短信后i按加上11位哈市值)

1)命令(mac 命令)

2)代码,放入项目中运行,打包、启动app(注意debug签名与正是签名包的不同,正式的hashcode  需要正式打包,然后打印出来))


注意点:

1、本方式不适合华为手机,华为有自己的获取方式
2、本方式不需要添加任何短信权限
3、项目版本

compileSdkVersion>=34

minSdkVersion>=19

开发成本:

app需要集成两个google库,后端只需要改短信模板,在短信后面添加11位hash值,hash值生成方式在下面链接可以看到

google开发地址(国内需要翻墙)

Android代码

1、导库
1)在项目根目录的build.gradle中添加下面代码
buildscript {
 
    repositories {
        ...
        google()
        mavenCentral()

    }
}

allprojects {
    repositories { 
        google()
        mavenCentral()
    }
}

2)项目级build.gradle引用库
dependencies {
  
  implementation 'com.google.android.gms:play-services-auth:21.2.0'
    implementation 'com.google.android.gms:play-services-auth-api-phone:18.1.0'
}

2、注册广播

1)静态注册
       <receiver
            android:name="<路径>.MySMSBroadcastReceiver"
            android:exported="true"
            android:permission="com.google.android.gms.auth.api.phone.permission.SEND">
            <intent-filter>
                <action android:name="com.google.android.gms.auth.api.phone.SMS_RETRIEVED" />
            </intent-filter>
        </receiver>
2)动态注册
 if(smsBrod==null){
             smsBrod= MySMSBroadcastReceiver(this,null)
            }
            val intentFilter = IntentFilter();

            intentFilter.addAction(SmsRetriever.SMS_RETRIEVED_ACTION);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                this@TestAlendActivity.registerReceiver(
                    smsBrod,
                    intentFilter,
                    SmsRetriever.SEND_PERMISSION,
                   null, Context.RECEIVER_EXPORTED
                )
            }

3、启动监听

 fun googleMSM() {
        val client = SmsRetriever.getClient(this)
        val task: Task<Void> = client.startSmsRetriever()
        task.addOnSuccessListener(OnSuccessListener<Void?> {
            Log.i("SMS_CON", "googleMSM addOnSuccessListener")
            if(smsBrod==null){
             smsBrod= MySMSBroadcastReceiver(this,null)
            }
            val intentFilter = IntentFilter();

            intentFilter.addAction(SmsRetriever.SMS_RETRIEVED_ACTION);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                this@TestAlendActivity.registerReceiver(
                    smsBrod,
                    intentFilter,
                    SmsRetriever.SEND_PERMISSION,
                   null, Context.RECEIVER_EXPORTED
                )
            }
        })

        task.addOnFailureListener(OnFailureListener {
            Log.i("SMS_CON", "googleMSM addOnFailureListener")
        })
    }

4、广播代码

 

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;

 
import com.google.android.gms.auth.api.phone.SmsRetriever;
import com.google.android.gms.common.api.CommonStatusCodes;
import com.google.android.gms.common.api.Status;

import org.json.JSONException;
import org.json.JSONObject;

import java.lang.ref.SoftReference;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * BroadcastReceiver to wait for SMS messages. This can be registered either
 * in the AndroidManifest or at runtime.  Should filter Intents on
 * SmsRetriever.SMS_RETRIEVED_ACTION.
 */
public class MySMSBroadcastReceiver extends BroadcastReceiver {
 
    
 

    public MySMSBroadcastReceiver() {
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.i("SMS_CON", "googleMSM MySMSBroadcastReceiver action=" + intent.getAction());

        if (SmsRetriever.SMS_RETRIEVED_ACTION.equals(intent.getAction())) {
            Bundle extras = intent.getExtras();
            Status status = (Status) extras.get(SmsRetriever.EXTRA_STATUS);
            Log.i("SMS_CON", "status =" + status);
            if(status!=null){
                switch (status.getStatusCode()) {
                    case CommonStatusCodes.SUCCESS:
                        // (Optional) Get SMS Sender address - only available in
                        // GMS version 24.20 onwards, else it will return null
//                    String senderAddress = extras.getString(SmsRetriever.EXTRA_SMS_ORIGINATING_ADDRESS);
                        // Get SMS message contents
                        String message = extras.getString(SmsRetriever.EXTRA_SMS_MESSAGE);
                        // Extract one-time code from the message and complete verification
                        // by sending the code back to your server.
                        Log.i("SMS_CON", "message =" + message);

                        if (message != null) {
                            Pattern code = Pattern.compile("(\\d{6})");
                            Matcher matcher = code.matcher(message);
                            String otp = "";
                            if (matcher.find()) {
                                otp = matcher.group(0);
                            } else {
                                otp = message;
                            }
                            Log.i("SMS_CON", "otp =" + otp);

                            Toast.makeText(context,"otp=\n"+otp,Toast.LENGTH_LONG).show();
                         
                        }

                        break;
                    case CommonStatusCodes.TIMEOUT:
                        // Waiting for SMS timed out (5 minutes)
                        // Handle the error ...
                        Log.i("SMS_CON", "TIMEOUT =");
                        break;
                }
            }

        }
    }
}

其他开发成本

短信模板 (短信后i按加上11位哈市值)

Hash值生成方式

1)命令(mac 命令)

keytool -exportcert -keystore keystory名称.jks -storepass 你的密码 -alias 别名 | xxd -p  | tr -d "[:space:]" | echo -n 你的包名 `cat` | shasum -a 256 | tr -d "[:space:]-" | xxd -r -p | base64 | cut -c1-11

2)代码,放入项目中运行,打包、启动app(注意debug签名与正是签名包的不同,正式的hashcode  需要正式打包,然后打印出来))

aab文件也可以通过命令安装到手机,网上搜一下 bundletool.jar 这个工具

 

import android.annotation.TargetApi;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.util.Base64;
import android.util.Log;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;

public class AppSignatureHashHelper extends ContextWrapper {
    public static final String TAG = AppSignatureHashHelper.class.getSimpleName();

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

    public AppSignatureHashHelper(Context context) {
        super(context);
    }

    public ArrayList<String> getAppSignatures() {
        ArrayList<String> appSignaturesHashs = new ArrayList<>();

        try {
            String packageName = getPackageName();
            PackageManager packageManager = getPackageManager();
            Signature[] signatures = packageManager.getPackageInfo(packageName,
                    PackageManager.GET_SIGNATURES).signatures;

            for (Signature signature : signatures) {
                String hash = hash(packageName, signature.toCharsString());
                if (hash != null) {
                    appSignaturesHashs.add(String.format("%s", hash));
                }
            }
        } catch (Exception e) {
            Log.e(TAG, "Package not found", e);
        }
        return appSignaturesHashs;
    }

    @TargetApi(19)
    private static String hash(String packageName, String signature) {
        String appInfo = packageName + " " + signature;
        try {
            MessageDigest messageDigest = MessageDigest.getInstance(HASH_TYPE);
            messageDigest.update(appInfo.getBytes(StandardCharsets.UTF_8));
            byte[] hashSignature = messageDigest.digest();

            hashSignature = Arrays.copyOfRange(hashSignature, 0, NUM_HASHED_BYTES);
            String base64Hash = Base64.encodeToString(hashSignature, Base64.NO_PADDING | Base64.NO_WRAP);
            base64Hash = base64Hash.substring(0, NUM_BASE64_CHAR);

            return base64Hash;
        } catch (NoSuchAlgorithmException e) {
            Log.e(TAG, "No Such Algorithm Exception", e);
        }
        return null;
    }
}

调用方式

 String hashcodeStr= AppSignatureHashHelper(this).getAppSignatures()

上面会生成11位hash值,放在短信后面就行了

***以上是获取短信,短信自动填充代码就不写了,拿到字符串 设置到输入框就行,推荐按使用动态注册,可以给广播传入回调接口,处理返回的验证码(otp)

到这里就完成了,上面获取命令的方式是通过自己签名的方式,如果是让google生成签名,则需要去看链接里的步骤,比较详细

测试的话 可以让别人发短信给你 ,在app里会受到广播


http://www.kler.cn/news/315243.html

相关文章:

  • Unity 设计模式 之 创建型模式 -【单例模式】【原型模式】 【建造者模式】
  • 【力扣】2376. 统计特殊整数
  • Linux:虚拟文件系统/proc和self进程
  • 某招标公告公示搜索引擎爬虫逆向
  • git配置SSH
  • 第二届Apache Flink极客挑战赛冠军比赛攻略_SkyPeaceLL队
  • 安卓开发,插件化换肤思路
  • 【Java】接口interface【主线学习笔记】
  • Mac使用gradle编译springboot-2.7.x源码
  • Hadoop分布式集群配置
  • SIP Servlets学习
  • ModbusTCP报文详解
  • 冒泡排序,选择排序,插入排序,归并排序,快速排序五种排序方法
  • thop计算模型复杂度(params,flops)
  • 云计算实训50——Kubernetes基础命令、常用指令
  • 怎样把PPT上顽固的图标删了
  • Html css水平居中+垂直居中+水平垂直居中的方法总结
  • WPF 自定义路由事件
  • HTTP的基本格式
  • 整合多方大佬博客以及视频 一文读懂 servlet
  • go/函数
  • Java笔试面试题AI答之单元测试JUnit(5)
  • 3. 什么是连接池?为什么使用数据库连接池?
  • AgentScope中带有@功能的多Agent组对话
  • Github Wiki 超链接 转 码云Gitee Wiki 超链接
  • hcia-openEuler V1.0师资题库-试卷2
  • MySQL数据库专栏(九)内联查询
  • 如何做系统架构?从动态系统思考的角度
  • 线性规划------ + 案例 + Python源码求解(见文中)
  • idea2021git从dev分支合并到主分支master