如需自动验证电话号码,您必须同时实现验证流程的客户端和服务器部分。本文档介绍了如何在 Android 应用中实现客户端部分。
如需在 Android 应用中启动电话号码验证流程,请将电话号码发送到验证服务器,并调用 SMS Retriever API 以开始监听包含应用一次性验证码的短信。收到短信后,请将该一次性验证码发送回服务器,以完成验证流程。
准备工作
为了让您的应用做好准备,请完成以下部分中的步骤。
应用要满足的前提条件
确保您应用的 build 文件使用以下值:
- minSdkVersion 为 19 或更高
- compileSdkVersion 28 或更高版本
配置您的应用
在项目级 build.gradle 文件中,同时在 buildscript
和 allprojects
部分中添加 Google 的 Maven 制品库和 Maven 中央制品库:
buildscript {
repositories {
google()
mavenCentral()
}
}
allprojects {
repositories {
google()
mavenCentral()
}
}
将 SMS Retriever API 的 Google Play 服务依赖项添加到模块的 Gradle build 文件(通常为 app/build.gradle
):
dependencies {
implementation 'com.google.android.gms:play-services-auth:20.7.0'
implementation 'com.google.android.gms:play-services-auth-api-phone:18.0.1'
}
1. 获取用户的电话号码
您可以通过适合您的应用的任何方式获取用户的电话号码。通常,最佳的用户体验是使用提示选择器提示用户从设备上存储的电话号码中进行选择,从而避免手动输入电话号码。如需使用提示选择器,请执行以下操作:
// Construct a request for phone numbers and show the picker
private void requestHint() {
HintRequest hintRequest = new HintRequest.Builder()
.setPhoneNumberIdentifierSupported(true)
.build();
PendingIntent intent = Auth.CredentialsApi.getHintPickerIntent(
apiClient, hintRequest);
startIntentSenderForResult(intent.getIntentSender(),
RESOLVE_HINT, null, 0, 0, 0);
}
// Obtain the phone number from the result
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == RESOLVE_HINT) {
if (resultCode == RESULT_OK) {
Credential credential = data.getParcelableExtra(Credential.EXTRA_KEY);
// credential.getId(); <-- will need to process phone number string
}
}
}
2. 启动短信检索器
当您准备好验证用户的电话号码时,请获取 SmsRetrieverClient
对象的实例,调用 startSmsRetriever
,并将成功和失败监听器附加到短信检索任务:
// Get an instance of SmsRetrieverClient, used to start listening for a matching
// SMS message.
SmsRetrieverClient 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.
Task<Void> task = 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(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void aVoid) {
// Successfully started retriever, expect broadcast intent
// ...
}
});
task.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
// Failed to start retriever, inspect Exception for more details
// ...
}
});
短信检索任务最多会监听 5 分钟的短信,其中应包含用于标识应用的唯一字符串。
3. 将电话号码发送到您的服务器
在获得用户的电话号码并开始监听短信后,请使用任何方法(通常使用 HTTPS POST 请求)将用户的电话号码发送到您的验证服务器。
您的服务器生成一条验证消息,并通过短信将其发送到您指定的电话号码。请参阅在服务器上执行短信验证。
4. 接收验证消息
当用户的设备上收到验证消息时,Play 服务会向您的应用明确广播一个 SmsRetriever.SMS_RETRIEVED_ACTION
intent,其中包含消息文本。请使用 BroadcastReceiver
接收此验证消息。
在 BroadcastReceiver
的 onReceive
处理程序中,从 intent 的 extra 中获取验证消息的文本:
/**
* 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 {
@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 message = (String) extras.get(SmsRetriever.EXTRA_SMS_MESSAGE);
// Extract one-time code from the message and complete verification
// by sending the code back to your server.
break;
case CommonStatusCodes.TIMEOUT:
// Waiting for SMS timed out (5 minutes)
// Handle the error ...
break;
}
}
}
}
如下例所示,在应用的 AndroidManifest.xml
文件中向 intent 过滤器 com.google.android.gms.auth.api.phone.SMS_RETRIEVED
(SmsRetriever.SMS_RETRIEVED_ACTION
常量的值)和权限 com.google.android.gms.auth.api.phone.permission.SEND
(SmsRetriever.SEND_PERMISSION
常量的值)注册此 BroadcastReceiver
,或者使用 Context.registerReceiver
动态注册。
<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>
5. 将验证邮件中的一次性验证码发送到您的服务器
现在您已经有了验证消息的文本内容,接下来可以使用正则表达式或一些其他逻辑从该消息中获取一次性代码。一次性代码的格式取决于您在服务器中实现代码的方式。
最后,通过安全连接将一次性代码发送到您的服务器。服务器收到一次性验证码时,会记录电话号码已通过验证。
可选:使用 Smart Lock(密码专用)保存电话号码
(可选)在用户验证其电话号码后,您可以提示用户使用 Smart Lock(密码专用)保存此电话号码帐号,以便在其他应用和其他设备上自动使用此电话号码帐号,而无需再次输入或选择电话号码:
Credential credential = new Credential.Builder(phoneNumberString)
.setAccountType("https://signin.example.com") // a URL specific to the app
.setName(displayName) // optional: a display name if available
.build();
Auth.CredentialsApi.save(apiClient, credential).setResultCallback(
new ResultCallback() {
public void onResult(Result result) {
Status status = result.getStatus();
if (status.isSuccess()) {
Log.d(TAG, "SAVE: OK"); // already saved
} else if (status.hasResolution()) {
// Prompt the user to save
status.startResolutionForResult(this, RC_SAVE);
}
}
});
然后,当用户在新设备上重新安装应用或安装应用后,您可以检索已保存的电话号码,而无需再次向用户索要电话号码:
// On the next install, retrieve the phone number
mCredentialRequest = new CredentialRequest.Builder()
.setAccountTypes("https://signin.example.com") // the URL specific to the developer
.build();
Auth.CredentialsApi.request(apiClient, mCredentialRequest).setResultCallback(
new ResultCallback<CredentialRequestResult>() {
public void onResult(CredentialRequestResult credentialRequestResult) {
if (credentialRequestResult.getStatus().isSuccess()) {
credentialRequestResult.getCredential().getId(); // this is the phone number
}
}
});
// Then, initiate verification and sign the user in (same as original verification logic)