otp(令牌)应该自动从消息中读取

vu8f3i0k  于 2021-06-29  发布在  Java
关注(0)|答案(8)|浏览(348)

我正在开发一个android应用程序,其中服务器发送一个otp,用户需要在应用程序中输入这个otp,才能注册我的应用程序。我想要的是,我的应用程序应该能够自动读取服务器发送的otp。我怎样才能做到这一点?在此方面的任何帮助或指导都将不胜感激。

aij0ehis

aij0ehis1#

是的,这在浏览器中也是可能的。chrome在84及以上版本中发布了此功能。借助webotpapi,我们可以在web上检测移动设备的otp。
以下是一个与pwa应用程序集成的web otp代码:https://github.com/rohit3230/webotpautoreadbyangular
去现场工作的应用程序网址。https://rohit3230.github.io/webotpautoreadbyangular/

798qvoo8

798qvoo82#

我实现了这样的东西。但是,当消息传入时,我只检索六位代码,将其捆绑在一个intent中,并将其发送给需要它的活动或片段,并验证代码。这个例子向您展示了如何获取短信。请看下面的代码,以了解如何使用localbrodcastmanager发送邮件,如果您的邮件包含更多文本(例如问候语),请对其进行标准化以更好地帮助您。e、 g“您的验证码是:84hg73”您可以创建这样的regex模式 ([0-9]){2}([A-Z]){2}([0-9]){2} 意思是两个整数,两个大写字母和两个整数。祝你好运!
丢弃邮件中所有不需要的信息后

Intent intent = new Intent("AddedItem");
 intent.putExtra("items", code);
 LocalBroadcastManager.getInstance(getActivity()).sendBroadcast(intent);

以及接收它的碎片/活动

@Override
public void onResume() {
    LocalBroadcastManager.getInstance(getActivity()).registerReceiver(receiver, new IntentFilter("AddedItem"));
    super.onResume();
}

@Override
public void onPause() {
    super.onDestroy();
    LocalBroadcastManager.getInstance(getActivity()).unregisterReceiver(receiver);
}

代码是用来处理你收集的有效载荷的

private BroadcastReceiver receiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction()) {
            final String message = intent.getStringExtra("message");
            //Do whatever you want with the code here
        }
    }
};

这有帮助吗。我用回调做得更好

piztneat

piztneat3#

使用sms retriever api,可以读取otp而无需声明 android.permission.READ_SMS .
启动短信检索器

private fun startSMSRetriever() {
        // Get an instance of SmsRetrieverClient, used to start listening for a matching SMS message.
        val client = SmsRetriever.getClient(this /* context */);

        // Starts SmsRetriever, which waits for ONE matching SMS message until timeout
        // (5 minutes). The matching SMS message will be sent via a Broadcast Intent with
        // action SmsRetriever#SMS_RETRIEVED_ACTION.
        val task: Task<Void> = client.startSmsRetriever();

        // Listen for success/failure of the start Task. If in a background thread, this
        // can be made blocking using Tasks.await(task, [timeout]);
        task.addOnSuccessListener {
            Log.d("SmsRetriever", "SmsRetriever Start Success")
        }

        task.addOnFailureListener {
            Log.d("SmsRetriever", "SmsRetriever Start Failed")
        }
    }

通过广播接收消息

public class MySMSBroadcastReceiver : BroadcastReceiver() {

        override fun onReceive(context: Context?, intent: Intent?) {
            if (SmsRetriever.SMS_RETRIEVED_ACTION == intent?.action && intent.extras!=null) {
                val extras = intent.extras
                val status = extras.get(SmsRetriever.EXTRA_STATUS) as Status

                when (status.statusCode) {
                    CommonStatusCodes.SUCCESS -> {
                        // Get SMS message contents
                        val message = extras.get(SmsRetriever.EXTRA_SMS_MESSAGE) as String
                        Log.e("Message", message);
                        // Extract one-time code from the message and complete verification
                        // by sending the code back to your server.
                    }
                    CommonStatusCodes.TIMEOUT -> {
                        // Waiting for SMS timed out (5 minutes)
                        // Handle the error ...
                    }
                }
            }
        }

    }   

    /**Don't forgot to define BroadcastReceiver in AndroidManifest.xml.*/       
    <receiver android:name=".MySMSBroadcastReceiver" android:exported="true">
        <intent-filter>
            <action android:name="com.google.android.gms.auth.api.phone.SMS_RETRIEVED"/>
        </intent-filter>
    </receiver>

将验证消息中的一次性代码发送到服务器
确保您的短信格式完全符合以下要求:

<#> Your ExampleApp code is: 123ABC78
fBzOyyp9h6L

不超过140字节
以前缀开始
以11个字符的哈希字符串结束,该字符串标识您的应用程序
可以使用以下代码计算应用程序哈希:

import android.content.Context
import android.content.ContextWrapper
import android.content.pm.PackageManager
import android.util.Base64
import android.util.Log
import java.nio.charset.StandardCharsets
import java.security.MessageDigest
import java.security.NoSuchAlgorithmException
import java.util.*

/**
 * This is a helper class to generate your message hash to be included in your SMS message.
 *
 * Without the correct hash, your app won't recieve the message callback. This only needs to be
 * generated once per app and stored. Then you can remove this helper class from your code.
 *
 * For More Detail: https://developers.google.com/identity/sms-retriever/verify#computing_your_apps_hash_string
 *
 */
public class AppSignatureHelper(private val context: Context) : ContextWrapper(context) {

    companion object {
        val TAG = AppSignatureHelper::class.java.simpleName;

        private const val HASH_TYPE = "SHA-256";
        const val NUM_HASHED_BYTES = 9;
        const val NUM_BASE64_CHAR = 11;
    }

    /**
     * Get all the app signatures for the current package
     * @return
     */
    public fun getAppSignatures(): ArrayList<String> {
        val appCodes = ArrayList<String>();

        try {
            // Get all package signatures for the current package
            val signatures = packageManager.getPackageInfo(
                packageName,
                PackageManager.GET_SIGNATURES
            ).signatures;

            // For each signature create a compatible hash
            for (signature in signatures) {
                val hash = hash(packageName, signature.toCharsString());
                if (hash != null) {
                    appCodes.add(String.format("%s", hash));
                }
            }
        } catch (e: PackageManager.NameNotFoundException) {
            Log.e(TAG, "Unable to find package to obtain hash.", e);
        }
        return appCodes;
    }

    private fun hash(packageName: String, signature: String): String? {
        val appInfo = "$packageName $signature";
        try {
            val messageDigest = MessageDigest.getInstance(HASH_TYPE);
            messageDigest.update(appInfo.toByteArray(StandardCharsets.UTF_8));
            var hashSignature = messageDigest.digest();

            // truncated into NUM_HASHED_BYTES
            hashSignature = Arrays.copyOfRange(hashSignature, 0, NUM_HASHED_BYTES);
            // encode into Base64
            var base64Hash = Base64.encodeToString(hashSignature, Base64.NO_PADDING or Base64.NO_WRAP);
            base64Hash = base64Hash.substring(0, NUM_BASE64_CHAR);

            Log.e(TAG, String.format("pkg: %s -- hash: %s", packageName, base64Hash));
            return base64Hash;
        } catch (e: NoSuchAlgorithmException) {
            Log.e(TAG, "hash:NoSuchAlgorithm", e);
        }
        return null;
    }
}

所需等级:

implementation "com.google.android.gms:play-services-auth-api-phone:16.0.0"

参考文献:
https://developers.google.com/identity/sms-retriever/overview
https://developers.google.com/identity/sms-retriever/request
https://developers.google.com/identity/sms-retriever/verify

0vvn1miw

0vvn1miw4#


**activity_main.xml**

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.mukundwn.broadcastreceiver.MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

**MainActivity.java**

import android.content.BroadcastReceiver;
import android.content.IntentFilter;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

    private BroadcastReceiver broadcastReceiver;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        broadcastReceiver =new MyBroadcastReceiver();
    }

@Override
    protected void onStart()
{
    super.onStart();
    IntentFilter intentFilter=new IntentFilter("android.provider.Telephony.SMS_RECEIVED");
    registerReceiver(broadcastReceiver,intentFilter);
}
@Override
    protected void onStop()
{
    super.onStop();
    unregisterReceiver(broadcastReceiver);
}

}

**MyBroadcastReceiver.java**

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;

/**
 * Created by mukundwn on 12/02/18.
 */

public class MyBroadcastReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context,"hello received an sms",Toast.LENGTH_SHORT).show();
    }
}

**Manifest.xml**

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.mukundwn.broadcastreceiver">

    <uses-permission android:name="android.permission.RECEIVE_SMS"/>
    <uses-permission android:name="android.permission.READ_SMS"></uses-permission>
    <uses-permission android:name="android.permission.READ_PHONE_STATE"></uses-permission>

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <receiver android:name=".MyBroadcastReceiver">
        <intent-filter>
            <action android:name="android.provider.Telephony.SMS_RECEIVE"></action>
        </intent-filter>
        </receiver>
    </application>

</manifest>
zbq4xfa0

zbq4xfa05#

由于google限制了read\u sms权限的使用,这里是一个没有read\u sms权限的解决方案。

sms检索器api

基本功能是避免使用android临界权限读写短信,用这种方法完成任务。吹是你需要的步骤。
发送otp到用户号后,检查短信检索器api是否能得到消息

SmsRetrieverClient client = SmsRetriever.getClient(SignupSetResetPasswordActivity.this);
Task<Void> task = client.startSmsRetriever();
task.addOnSuccessListener(new OnSuccessListener<Void>() {
    @Override
    public void onSuccess(Void aVoid) {
        // Android will provide message once receive. Start your broadcast receiver.
        IntentFilter filter = new IntentFilter();
        filter.addAction(SmsRetriever.SMS_RETRIEVED_ACTION);
        registerReceiver(new SmsReceiver(), filter);
    }
});
task.addOnFailureListener(new OnFailureListener() {
    @Override
    public void onFailure(@NonNull Exception e) {
        // Failed to start retriever, inspect Exception for more details
    }
});

广播接收器代码

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;

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;

public class SmsReceiver extends BroadcastReceiver {

    @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:
                    // Get SMS message contents
                    String otp;
                    String msgs = (String) extras.get(SmsRetriever.EXTRA_SMS_MESSAGE);

                    // Extract one-time code from the message and complete verification
                    break;
                case CommonStatusCodes.TIMEOUT:
                    // Waiting for SMS timed out (5 minutes)
                    // Handle the error ...
                    break;
            }
        }
    }
}

最后一步。将此收件人注册到您的清单中

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

你的短信必须如下。

<#> Your OTP code is: 6789
QWsa8754qw2

这里qwsa8754qw2是您自己的应用程序11个字符的哈希代码。点击此链接
不超过140字节
以前缀开始
以11个字符的哈希字符串结束,该字符串标识您的应用程序
导入 com.google.android.gms.auth.api.phone.SmsRetriever ,别忘了将这一行添加到你的app build.gradle:

implementation "com.google.android.gms:play-services-auth-api-phone:16.0.0"
ego6inou

ego6inou6#

你可以尝试使用一个简单的库,比如
通过gradle安装并添加权限后,在oncreate activity这样的方法中启动smsverifycatcher:

smsVerifyCatcher = new SmsVerifyCatcher(this, new OnSmsCatchListener<String>() {
    @Override
    public void onSmsCatch(String message) {
        String code = parseCode(message);//Parse verification code
        etCode.setText(code);//set code in edit text
        //then you can send verification code to server
    }
});

另外,重写活动生命周期方法:

@Override
protected void onStart() {
    super.onStart();
    smsVerifyCatcher.onStart();
}

@Override
protected void onStop() {
    super.onStop();
    smsVerifyCatcher.onStop();
}

/**
 * need for Android 6 real time permissions
 */
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    smsVerifyCatcher.onRequestPermissionsResult(requestCode, permissions, grantResults);
}

public String parseCode(String message) {
    Pattern p = Pattern.compile("\\b\\d{4}\\b");
    Matcher m = p.matcher(message);
    String code = "";
    while (m.find()) {
        code = m.group(0);
    }
    return code;
}
knsnq2tg

knsnq2tg7#

抱歉,迟来的答复,但仍然想张贴我的答案,如果它有帮助。它为6位otp工作。

@Override
    public void onOTPReceived(String messageBody)
    {
        Pattern pattern = Pattern.compile(SMSReceiver.OTP_REGEX);
        Matcher matcher = pattern.matcher(messageBody);
        String otp = HkpConstants.EMPTY;
        while (matcher.find())
        {
            otp = matcher.group();
        }
        checkAndSetOTP(otp);
    }
Adding constants here

public static final String OTP_REGEX = "[0-9]{1,6}";

对于sms侦听器,可以按照下面的类

public class SMSReceiver extends BroadcastReceiver
{
    public static final String SMS_BUNDLE = "pdus";
    public static final String OTP_REGEX = "[0-9]{1,6}";
    private static final String FORMAT = "format";

    private OnOTPSMSReceivedListener otpSMSListener;

    public SMSReceiver(OnOTPSMSReceivedListener listener)
    {
        otpSMSListener = listener;
    }

    @Override
    public void onReceive(Context context, Intent intent)
    {
        Bundle intentExtras = intent.getExtras();
        if (intentExtras != null)
        {
            Object[] sms_bundle = (Object[]) intentExtras.get(SMS_BUNDLE);
            String format = intent.getStringExtra(FORMAT);
            if (sms_bundle != null)
            {
                otpSMSListener.onOTPSMSReceived(format, sms_bundle);
            }
            else {
                // do nothing
            }
        }
    }

    @FunctionalInterface
    public interface OnOTPSMSReceivedListener
    {
        void onOTPSMSReceived(@Nullable String format, Object... smsBundle);
    }
}

    @Override
    public void onOTPSMSReceived(@Nullable String format, Object... smsBundle)
    {
        for (Object aSmsBundle : smsBundle)
        {
            SmsMessage smsMessage = getIncomingMessage(format, aSmsBundle);
            String sender = smsMessage.getDisplayOriginatingAddress();
            if (sender.toLowerCase().contains(ONEMG))
            {
                getIncomingMessage(smsMessage.getMessageBody());
            } else
            {
                // do nothing
            }
        }
    }

    private SmsMessage getIncomingMessage(@Nullable String format, Object aObject)
    {
        SmsMessage currentSMS;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && format != null)
        {
            currentSMS = SmsMessage.createFromPdu((byte[]) aObject, format);
        } else
        {
            currentSMS = SmsMessage.createFromPdu((byte[]) aObject);
        }

        return currentSMS;
    }
jjhzyzn0

jjhzyzn08#

我建议您不要使用任何第三方库自动获取短信收件箱的otp。如果您对广播接收器及其工作原理有基本的了解,这是很容易做到的。请尝试以下方法:
步骤1)创建单个接口,即smslistner

package com.wnrcorp.reba;
public interface SmsListener{
public void messageReceived(String messageText);}

步骤2)创建单个广播接收器,即smsreceiver

package com.wnrcorp.reba;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.telephony.SmsMessage;
public class SmsReceiver extends BroadcastReceiver {
private static SmsListener mListener;
Boolean b;
String abcd,xyz;
@Override
public void onReceive(Context context, Intent intent) {
Bundle data  = intent.getExtras();
Object[] pdus = (Object[]) data.get("pdus");
    for(int i=0;i<pdus.length;i++){
        SmsMessage smsMessage = SmsMessage.createFromPdu((byte[]) pdus[i]);
        String sender = smsMessage.getDisplayOriginatingAddress();
       // b=sender.endsWith("WNRCRP");  //Just to fetch otp sent from WNRCRP
        String messageBody = smsMessage.getMessageBody();
       abcd=messageBody.replaceAll("[^0-9]","");   // here abcd contains otp 
        which is in number format
        //Pass on the text to our listener.
        if(b==true) {
            mListener.messageReceived(abcd);  // attach value to interface 
  object
        }
        else
        {
        }
    }
}
public static void bindListener(SmsListener listener) {
    mListener = listener;
}
}

步骤3)在android清单文件中添加监听器,即广播接收器

<receiver android:name=".SmsReceiver">    
        <intent-filter>
            <action android:name="android.provider.Telephony.SMS_RECEIVED"/>
        </intent-filter>
</receiver>

并添加权限 <uses-permission android:name="android.permission.RECEIVE_SMS"/> 最后一步4)当收到收件箱中的otp时,您将自动获取otp的活动。在我的例子中,我获取otp并设置edittext字段。

public class OtpVerificationActivity extends AppCompatActivity {
EditText ed;
TextView tv;
String otp_generated,contactNo,id1;
GlobalData gd = new GlobalData();
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_otp_verification);
    ed=(EditText)findViewById(R.id.otp);
    tv=(TextView) findViewById(R.id.verify_otp); 
    /*This is important because this will be called every time you receive 
     any sms */            
 SmsReceiver.bindListener(new SmsListener() {
        @Override
        public void messageReceived(String messageText) {
            ed.setText(messageText);     
        }
    });
    tv.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            try
            {
                InputMethodManager imm=
  (InputMethodManager)getSystemService(INPUT_METHOD_SERVICE);                    
  imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(),0);
            }
            catch(Exception e)
            {}           
            if (ed.getText().toString().equals(otp_generated))
            {
                Toast.makeText(OtpVerificationActivity.this, "OTP Verified 
       Successfully !", Toast.LENGTH_SHORT).show();           
             }
    });
   }
}

OTP验证活动的布局文件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_otp_verification"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.wnrcorp.reba.OtpVerificationActivity">
<android.support.v7.widget.CardView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/firstcard"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    card_view:cardCornerRadius="10dp"
    >
   <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:background="@android:color/white">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="OTP Confirmation"
            android:textSize="18sp"
            android:textStyle="bold"
            android:id="@+id/dialogTitle"
            android:layout_margin="5dp"
            android:layout_gravity="center"
            />
        <EditText
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/otp"
            android:layout_margin="5dp"
            android:hint="OTP Here"
            />
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Verify"
            android:textSize="18sp"
            android:id="@+id/verify_otp"
            android:gravity="center"
            android:padding="10dp"
            android:layout_gravity="center"
            android:visibility="visible"
            android:layout_margin="5dp"
            android:background="@color/colorPrimary"
            android:textColor="#ffffff"
            />
        </LinearLayout>
        </android.support.v7.widget.CardView>
        </RelativeLayout>

otp验证活动的屏幕截图,您可以在收到消息后立即获取otp

相关问题