android SmsRetrieverAPI Google OTP

Android自動填入SMS OTP實作:使用Google SMS Retriever API,免SMS_READ & SMS_RECIEVER權限

Luca Lin 2019/04/08 09:50:28
6552

 

l   前言:

一般來說,在開發android專案時,如欲開發的功能需要涉及使用者資料或系統功能等等,常常需要先在manifest檔先寫上permission,或是在程式碼中多加一條判斷式,如下圖所示需讀取聯絡人資料前:

 

 

n   各種添加在manifest權限的例子

 

通常開啟權限之後,在使用這個專案時,程式會不間斷地持續check這些權限是否開啟,假設一個應用程式需要驗證OTP,程式中就必須開啟SMS_READSMS_RECEIVER 這兩個權限,但整個專案中,假設只有在驗證OTP簡訊密碼時才需用到一次,其餘不會再用到了,在這種情況下,直接添加權限後的持續檢查,所耗費的資源久了也是相當可觀的!為了解決這樣的問題,Google官方有提供了一個新的APIGoogle Retriever API,透過這個API就可以讓程式從伺服器接收到簡訊密碼,而不用設定權限,提供了一次性使用的原理,很神奇吧!下面就來介紹它是怎麼使用的。

 

l   流程說明

在開始製作之前,先說明一下它的原理,請看下圖:

 

前兩步是在您添加了Retriever api之後,系統會自動跳出手機本身可以用來接收簡訊的號碼選單,在選完號碼之後,即會透過SMS Retriever API,將您的號碼先行做驗證(step3),把驗證成功的結果送給需要接收的伺服器端(step4),再通過Retrierver將簡訊密碼送回給使用者(step5),使用者收到密碼之後會自動代入應填欄位做後續的動作(step6 & 7),如此一來,即可應用SMS Retriever API這個溝通橋樑,提供「一次性驗證」及「自動代入」驗證,省去許多時間與資源喔!接著就來實作一個來詳細說明吧!

 

l   實際操作

首先開個簡單的Activity來試試,就名為SMSRetrieverTestActivity吧!

預設動作為:開啟該畫面→跳出提示框選擇裝置號碼→點擊「取得簡訊驗證碼」按鈕→收到簡訊→結果顯示在第二行文字。

中間有兩行文字,第一行將會根據我們的步驟所獲得的資訊做更動,第二行在成功收到簡訊消息後會自動代入訊息以便得知結果。

我將其分為以下四步

1.          取得使用者手機號碼

2.          取得裝置的signature

3.          註冊 SMS Retriever API

4.          送出驗證並取得簡訊message

 

l   Step1 取得使用者手機號碼

開始之前,我們先把需要的工具加入gradle,在gradle.app檔中的dependencies內添加以下工具

這些是為了取得裝置signature與驗證API需要用的,至於裝置signature是幹嘛用的呢?會在下面的步驟詳細說明

 

取得手機號碼資料的媒介為HintRequest,故方法就取名為requestHint,就是將取到的結果用提示框呈現,在這裡要implement兩個class,在實作方法中就呼叫requestHint方法來取得。

 

 

n   Hintrequest方法

n   GoogleApiClient物件

 

上述程式碼大致的意思就是使用HintRequest物件與GoogleApiClient物件,取得授權過後的pendingIntent回傳給自己,藉以得知可以用來接收簡訊消息的號碼有哪些,詳細寫法可以參考官方文件,基本上根據官方的寫法,不用多做修改就能依序取得。只是要記得上述的方法需要在您需要開始驗證的那一頁上,在啟動該頁時(onCreate)就要先取到

寫好之後在開啟該頁時,系統就會自動偵測,並將結果以提示框呈現

l   Step2 取得裝置 signature

取得這個是為了讓伺服器辨識出,透過SMS Retriever API驗證過後的裝置,有點像是手機的身份證id一樣,在註冊與接收簡訊時都會用到,以確認要接收的裝置是哪個,有點像機場快速通關的感覺!

 

取得signature的格式有規定型態與長度,於是可以建立一個signatureHelper來固定方法:

 

 

 

然後就能在mainActivity內取得signature

n   取到的結果回傳給自己

 

這樣在提示框點選要操作的號碼後,就能呈現結果在螢幕上了

 

 

l   Step3 註冊SMS Retriever API

前置作業(取得號碼與裝置signature)完成後,就可以註冊Retriever了,這裡使用SmsRetrieverClient物件取得服務,並且用Task接收開通後的結果,並新增兩個listener來做後面的動作,分別是addOnSuccessListener & addOnFailureListener

n   建立brocastReciever.java

為了接收傳送過來的簡訊,只要使用原本用來接收推送通知的BroadcastReciever就可以了,所以可以建立一個自定義的BroadcastReciever並覆寫其方法,但請留意switch前的設定必須要這樣寫,這樣在Retriever收到結果後,裝置才能同步收到:

最後SUCCESS狀態後取得的message就是伺服器傳回來的簡訊內容了!它會是一個完整的簡訊message,如果只要截取其中的某些訊息,可以在這邊先加工一下再傳回給自己。

這裡多新增了一個myBroadcastRecieverListener來傳送成功或失敗的結果給先前建立好的task

PsOnRecieved多做了一個推送通知讓結果顯示更明顯,不寫亦可,此處最重要的是要處理收到的簡訊message,做後續對伺服器的verify動作

 

對了!最後別忘了在manifest檔註冊SMS Retriever給你自定義的java檔喔!

l   Step4 測試送出驗證並取得簡訊message

 

上述方法都寫好之後,就可以來測試看看了!要自我測試是否成功的話,其方法有很多種,這裡是使用intent的方法自己寄簡訊給自己,然後測試是否有收到相對應的內容。流程是開啟此專案自動獲取號碼 & 裝置signature→點擊驗證到寄送簡訊頁→寄出簡訊收到簡訊

 

這裡要特別注意一件事情那就是,如果要使用SMS Retriever API,簡訊的格式是有規定的,根據官方的描述,對於簡訊的規定有以下幾項:

1.          總長度不得超過140 bytes

2.          開頭必須是 <#>

3.          要有一個一次性使用的字串來完成驗證流程

4.          結尾要是一個11個字元長度的裝置signature

 

關於第3點的字串就是OTP密碼,這個數字可以由伺服器提供,或是由您決定可以通過的字串(測試用),目前是測試要用,所以可以先暫時寫一串數字代替已經收到的字,並且建議是好輸入的字,一般來說都是阿拉伯數字的組合。總而言之,必須同步要求伺服器端回傳時是符合這樣的格式才行!

測試成功的畫面如下:

         

 

                 

 

 

 

另外我還做了另一個簡訊未符合格式的測試,流程與上面完全相同,來看看發生了什麼事吧!

 

         

 

這次的測試,因為簡訊未符合格式的傳送,原先應該要收到結果的BroadcastReciever完全沒有反應,推播通知也沒收到,動作就在收到簡訊後就結束了!當然也就沒有後續的流程了。

 

在這之後我仍做了幾次簡訊格式的測試,其實看畫面都有收到自己寄送的簡訊沒錯,但差別在於,第一次測試有符合格式的簡訊,在程式碼中才能真正收到feedback,也就是在task真正能收到BroadcastReciever傳來的結果,而後才能做出真正的驗證流程,如此一來就能不加SMS_READ,與SMS_RECEIVER權限製作簡訊驗證的功能了!上述的功能只會在開啟該頁面時去取得認證,其它功能頁面未操作到簡訊的部份就不會一直呼叫,達到一次性使用的好效率。

 

l 總結沒收到結果可能的原因有哪些

根據上述的操作流程,有些細節需要特別小心,只要有一步沒有寫好,不會出錯但就會收不到真正的結果,我在這邊列出幾個須檢查的項目供大家參考:

1. 裝置signature和hintrequest有沒有在一開始就取得?基本上來說這兩個關鍵值必須在程式或指定頁面一開始時就要取得,以便做後續的動作,取得signature的型態與長度,google也有規定,所以使用之前再檢查一次吧!

2. 自定brocastreciever的格式有沒有正確?Retriever在取得結果之後會用一個bundle帶相關的資料回來,所以在自定義的brocastreciever中的onRecieve要用bundle取得對應結果,如下圖黃色標示

傳回來的資料來自取得SmsRetriever的status結果為SUCCESS之後,所以要留意一下寫法。

 

3. 簡訊訊息的格式是否正確?根據上述提到的簡訊格式規定,它的頭尾必須按照官方的格式去製作,不然即便收到簡訊了,你的程式是收不到回應的(第二點提到的bundle),也就無法做出最後驗證的目的,所以在訊息上要再檢查一次是否符合格式!

 

l   結論:

近年來程式不斷更新,官方常會不定時的提供原先做法的替代方案,其目的大多是為了提升效能或減少過度冗長的程式,使用SMS Retriever API可以使程式的效率變好,提供一次性驗證的功能讓程式碼能更對症下藥,而不是通通撒大網捕小魚。結尾附上我此次samplegithub連結:https://github.com/LucaLin/SMSRetrieverTest      ,有興趣的人可以載下來試試看喔!

Luca Lin