Wyobraź sobie aplikację, w której potrzebujesz potwierdzić rejestrację lub numer telefonu za pomocą SMS-a. Wydaje się dość skomplikowane, ale to tylko pozory. Dziś na warsztat weżniemy właśnie ten temat. Weryfikacja za pomocą SMS-a jest popularna wśród różnych serwisów czy aplikacji mobilnych. Jest to również mechanizm bezpieczeństwa, na przykład w aplikacjach bankowych.
Jak to działa?
- Musisz pobrać nr telefonu od użytkownika.
- Zarejestrować usługę, która będzie nadsłuchiwała na przyjście SMS-a.
- Przesłać numer telefonu na serwer OTP, który wyśle SMS-a.
- Wysłać SMS-a z poufnym kodem.
- Odebrać SMS-a na telefonie.
- Wysłać kod z SMS-a + nr tel na serwer. w celu weryfikacji.
- Wykonać odpowiednią akcję w aplikacji po weryfikacji.
- Wyrejestrować usługę, która nadsłuchuje.
Wydaje się dość skomplikowane, w kodzie jest jeszcze prościej….
Weryfikacja sms w Androdzie
Jeżeli zdobyłeś numer telefonu to teraz musisz zarejestrować SMS retriever w następujący sposób:
SmsRetrieverClient client = SmsRetriever.getClient(this );
Task<Void> task = client.startSmsRetriever();
task.addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void aVoid) {
Log.d(TAG, "SMS Retriever starts");
}
});
task.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
Log.d(TAG, "SMS Retriever error" + e);
}
});Następnie musimy zarejestrować broadcast, który będzie nadsłuchiwał na przyjście wiadomości:
mySMSBroadcastReceiver = new MySMSBroadcastReceiver(); mySMSBroadcastReceiver.setOTPListener(this); IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(SmsRetriever.SMS_RETRIEVED_ACTION); this.registerReceiver(mySMSBroadcastReceiver, intentFilter);
Przykładowa klasa MySMSBroadcastReceiver może wyglądać następująco:
public class MySMSBroadcastReceiver extends BroadcastReceiver {
private OTPReceiveListener OTPReceiveListener;
@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 (OTPReceiveListener != null) {
String str = message.replaceAll("\\D+", "");
OTPReceiveListener.onOTPReceived(str);
}
break;
case CommonStatusCodes.TIMEOUT:
if (OTPReceiveListener != null)
OTPReceiveListener.onOTPTimeOut("SMS retriever API Timeout");
break;
}
}
}
public void setOTPListener(OTPReceiveListener OTPReceiveListener) {
this.OTPReceiveListener = OTPReceiveListener;
}
interface OTPReceiveListener {
void onOTPReceived(String code);
void onOTPTimeOut(String text);
}
}Jeszcze implementacja interfejsu:
@Override
public void onOTPReceived(String code) {
if (mySMSBroadcastReceiver != null)
this.unregisterReceiver(mySMSBroadcastReceiver);
Log.e(TAG,"OTP Received code: "+ code);
}
@Override
public void onOTPTimeOut(String text) {
Log.e(TAG, text);
}Kod po stronie aplikacji można powiedzieć, że mamy gotowy. Teraz trzeba wysłać nr telefonu na serwer i przygotować odpowiedniego SMS-a. Treść wiadomości powinna spełniać następujące warunki:
- Początek wiadomości musi zawierać <#> lub \u200b\u200b. Pierwszy to widoczny przedrostek, który pojawi się w treści wiadomości, a drugi jest niewidocznym łańcuchem Unicode, ale wymaga obsługi standardu Unicode.
- Treść wiadomości nie może przekraczać 140 bajtów.
- Zawiera kod jednorazowy.
- I specjalny 11-znakowy skrót dla twojej aplikacji. Ten hash może być wygenerowany przez tę klasę lub w ten sposób.
- Reszta treści jest opcjonalna.
Przykładowa treść może wyglądać następująco:
<#> Your code is: 58628 NcZLqx+vXTu
Android Oreo na ratunek
Powyższa metoda działa. Niestety nie jest do końca bezpieczna, ponieważ aplikację, które mają dostęp do czytania SMS-a mogą odczytać kod weryfikacyjny. W Androidzie O wprowadzono metodę createAppSpecificSmsToken, która generuje specjalny token, który działa do przyjścia SMS-a. Po odebraniu wiadomości tylko aplikacja zareaguje na tą wiadomość. Inne aplikacje nie dowiedzą się, że przyszedł SMS.
@TargetApi(Build.VERSION_CODES.O)
private void appSmsToken(){
SmsManager smsManager = SmsManager.getDefault();
Intent intent = new Intent(this, SmsTokenResultActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 1234, intent,0);
String appSmsToken = smsManager.createAppSpecificSmsToken(pendingIntent);
Log.i(TAG, "sms token " + appSmsToken);
}Kod zadziała nawet po zamknięciu aplikacji. Gdy wygenerujesz specjalny token wysyłać go do serwera OTP, który następnie wysyła SMS-a z tyn tokenem. Gdy nadejdzie wiadomość, system Android wywoła klasę SmsTokenResultActivity. W niej możesz odebrać wiadomość w taki sposób:
for (SmsMessage pdu : Telephony.Sms.Intents.getMessagesFromIntent(getIntent())) {
Log.d(TAG, pdu.getDisplayMessageBody());
}Podsumowanie
Weryfikacja SMS w systemie Android jest prosta i skuteczna. Nie wymaga uprawnień SMS, a to jest duży plus. Jedynie co potrzebujesz to telefon z Androidem z usługą Google Play Services.
