请求一次性同意读取短信验证码

本页介绍了如何使用 SMS User Consent API 来征求用户同意 来阅读单条验证短信。如果用户同意,API 会返回 短信正文,您可以从中获取验证码 完成验证流程。

安装依赖项

在应用的 build.gradle 文件中添加 Play 服务身份验证组件:

implementation 'com.google.android.gms:play-services-auth:17.0.0'
implementation
'com.google.android.gms:play-services-auth-api-phone:17.4.0'

1. 获取用户的电话号码

如果你没有用户的电话号码,请在发送短信前先请求一下 验证流程。

您可以采用适合自己的方式获取用户的电话号码 应用。您可以考虑使用 Smart Lock(密码专用)提示选择器 在用户不需要填写电话号码时帮助填写该信息 创建用户的账号。如需使用提示选择器,请执行以下操作:

KotlinJava
private val CREDENTIAL_PICKER_REQUEST = 1  // Set to an unused request code

// Construct a request for phone numbers and show the picker
private fun requestHint() {
   
val hintRequest = HintRequest.Builder()
       
.setPhoneNumberIdentifierSupported(true)
       
.build()
   
val credentialsClient = Credentials.getClient(this)
   
val intent = credentialsClient.getHintPickerIntent(hintRequest)
    startIntentSenderForResult
(
        intent
.intentSender,
        CREDENTIAL
_PICKER_REQUEST,
       
null, 0, 0, 0
   
)
}

public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
   
super.onActivityResult(requestCode, resultCode, data)
   
when (requestCode) {
        CREDENTIAL
_PICKER_REQUEST ->
           
// Obtain the phone number from the result
           
if (resultCode == Activity.RESULT_OK && data != null) {
               
val credential = data.getParcelableExtra<Credential>(Credential.EXTRA_KEY)
               
// credential.getId();  <-- will need to process phone number string
           
}
       
// ...
   
}
}
private static final int CREDENTIAL_PICKER_REQUEST = 1;  // Set to an unused request code

// Construct a request for phone numbers and show the picker
private void requestHint() throws IntentSender.SendIntentException {
   
HintRequest hintRequest = new HintRequest.Builder()
           
.setPhoneNumberIdentifierSupported(true)
           
.build();
   
PendingIntent intent = Credentials.getClient(this).getHintPickerIntent(hintRequest);
    startIntentSenderForResult
(intent.getIntentSender(),
            RESOLVE_HINT
, null, 0, 0, 0);
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
   
super.onActivityResult(requestCode, resultCode, data);
   
switch (requestCode) {
       
case CREDENTIAL_PICKER_REQUEST:
           
// Obtain the phone number from the result
           
if (resultCode == RESULT_OK) {
               
Credential credential = data.getParcelableExtra(Credential.EXTRA_KEY);
               
// credential.getId();  <-- will need to process phone number string
           
}
           
break;
       
// ...
   
}
}

2. 开始监听传入消息

接下来,调用 SMS User Consent API 的 startSmsUserConsent() 方法以启动 并监听传入消息如果您知道接收该短信的电话号码 则指定该消息(否则,传递 null)。这样,短信 User Consent API 只会针对来自此号码的消息触发。

要开始收听,请执行以下操作:

KotlinJava
// Start listening for SMS User Consent broadcasts from senderPhoneNumber
// The Task<Void> will be successful if SmsRetriever was able to start
// SMS User Consent, and will error if there was an error starting.
val task = SmsRetriever.getClient(context).startSmsUserConsent(senderPhoneNumber /* or null */)
// Start listening for SMS User Consent broadcasts from senderPhoneNumber
// The Task<Void> will be successful if SmsRetriever was able to start
// SMS User Consent, and will error if there was an error starting.
Task<Void> task = SmsRetriever.getClient(context).startSmsUserConsent(senderPhoneNumber /* or null */);

在监听传入的短信后, 系统会将验证码发送到 第一步。

在接下来的五分钟内,当设备收到包含以下内容的短信时 Play 服务会向您的应用广播一个 intent,提示 用户获取阅读消息的权限。有消息触发广播 必须满足以下条件:

  • 邮件包含一个 4-10 个字符的字母数字字符串,且该字符串中至少包含一个 数字。
  • 如果您指定了发送者的电话号码,则该消息是由该发送者的 数字。

使用具有 SEND_PERMISSION 的广播接收器处理这些广播 权限并响应 SMS_RETRIEVED_ACTION intent。要创建和 注册广播接收器:

KotlinJava
private val SMS_CONSENT_REQUEST = 2  // Set to an unused request code
private val smsVerificationReceiver = object : BroadcastReceiver() {
   
override fun onReceive(context: Context, intent: Intent) {
       
if (SmsRetriever.SMS_RETRIEVED_ACTION == intent.action) {
           
val extras = intent.extras
           
val smsRetrieverStatus = extras?.get(SmsRetriever.EXTRA_STATUS) as Status

           
when (smsRetrieverStatus.statusCode) {
               
CommonStatusCodes.SUCCESS -> {
                   
// Get consent intent
                   
val consentIntent = extras.getParcelable<Intent>(SmsRetriever.EXTRA_CONSENT_INTENT)
                   
try {
                       
// Start activity to show consent dialog to user, activity must be started in
                       
// 5 minutes, otherwise you'll receive another TIMEOUT intent
                        startActivityForResult
(consentIntent, SMS_CONSENT_REQUEST)
                   
} catch (e: ActivityNotFoundException) {
                       
// Handle the exception ...
                   
}
               
}
               
CommonStatusCodes.TIMEOUT -> {
                   
// Time out occurred, handle the error.
               
}
           
}
       
}
   
}
}

override fun onCreate(savedInstanceState: Bundle?) {
   
// ...

   
val intentFilter = IntentFilter(SmsRetriever.SMS_RETRIEVED_ACTION)
    registerReceiver
(smsVerificationReceiver, SmsRetriever.SEND_PERMISSION, intentFilter)
}
private static final int SMS_CONSENT_REQUEST = 2;  // Set to an unused request code
private final BroadcastReceiver smsVerificationReceiver = new BroadcastReceiver() {
   
@Override
   
public void onReceive(Context context, Intent intent) {
       
if (SmsRetriever.SMS_RETRIEVED_ACTION.equals(intent.getAction())) {
           
Bundle extras = intent.getExtras();
           
Status smsRetrieverStatus = (Status) extras.get(SmsRetriever.EXTRA_STATUS);

           
switch (smsRetrieverStatus.getStatusCode()) {
               
case CommonStatusCodes.SUCCESS:
                   
// Get consent intent
                   
Intent consentIntent = extras.getParcelable(SmsRetriever.EXTRA_CONSENT_INTENT);
                   
try {
                       
// Start activity to show consent dialog to user, activity must be started in
                       
// 5 minutes, otherwise you'll receive another TIMEOUT intent
                        startActivityForResult
(consentIntent, SMS_CONSENT_REQUEST);
                   
} catch (ActivityNotFoundException e) {
                       
// Handle the exception ...
                   
}
                   
break;
               
case CommonStatusCodes.TIMEOUT:
                   
// Time out occurred, handle the error.
                   
break;
           
}
       
}
   
}
};

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

   
// ...

   
IntentFilter intentFilter = new IntentFilter(SmsRetriever.SMS_RETRIEVED_ACTION);
    registerReceiver
(smsVerificationReceiver, SmsRetriever.SEND_PERMISSION, intentFilter);
}

通过启动 EXTRA_CONSENT_INTENT 的 activity,您可以提示用户 读取邮件内容的一次性权限。

3. 通过短信获取验证码

onActivityResult() 方法中,处理用户对请求的响应 获取权限。如果返回的结果代码为 RESULT_OK, 读取消息内容的权限,并且可以获取消息文本 。

KotlinJava
public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
   
super.onActivityResult(requestCode, resultCode, data)
   
when (requestCode) {
       
// ...
        SMS
_CONSENT_REQUEST ->
           
// Obtain the phone number from the result
           
if (resultCode == Activity.RESULT_OK && data != null) {
               
// Get SMS message content
               
val message = data.getStringExtra(SmsRetriever.EXTRA_SMS_MESSAGE)
               
// Extract one-time code from the message and complete verification
               
// `message` contains the entire text of the SMS message, so you will need
               
// to parse the string.
               
val oneTimeCode = parseOneTimeCode(message) // define this function

               
// send one time code to the server
           
} else {
               
// Consent denied. User can type OTC manually.
           
}
   
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
   
super.onActivityResult(requestCode, resultCode, data);
   
switch (requestCode) {
       
// ...
       
case SMS_CONSENT_REQUEST:
           
if (resultCode == RESULT_OK) {
               
// Get SMS message content
               
String message = data.getStringExtra(SmsRetriever.EXTRA_SMS_MESSAGE);
               
// Extract one-time code from the message and complete verification
               
// `sms` contains the entire text of the SMS message, so you will need
               
// to parse the string.
               
String oneTimeCode = parseOneTimeCode(message); // define this function

               
// send one time code to the server
           
} else {
               
// Consent canceled, handle the error ...
           
}
           
break;
   
}
}

收到短信后,您就可以解析验证码, 自动填充表单或以其他方式完成验证流程。