本機號碼一鍵登入!推薦 Flutter 極光認證外掛

coding01發表於2020-01-13

一鍵登入舉例

本機號碼一鍵登入基本成為各個 APP 的標配了。

傳統的手機驗證碼登入方式:

  1. 輸入手機號;
  2. 前端判斷手機號是否有效;
  3. 後臺判斷手機號是否有效;
  4. 藉助第三方下發手機驗證碼;
  5. 前端有一個倒數計時,防止驗證碼過期輸入;
  6. 手機收到驗證碼;
  7. 複製貼上輸入驗證碼校驗;
  8. 將資訊發給後臺介面,驗證手機號和驗證碼是否正確,然後才是註冊和登入流程,下發認證 Auth 資訊。

這一套流程下來足夠費勁了。而上述的「本機號碼一鍵登入」完全無感,只要一點按鈕,所有操作一步到位。

今天我們來說一說如何利用「極光」做到「本機號碼一鍵登入」的。

開通「極光認證」

開通「極光認證」,需要先完成實名認證,實名認證後可以領取 1,000 次極光認證禮包。

本機號碼一鍵登入!推薦 Flutter 極光認證外掛

認證成功後,填入 android 和 iOS 包名、簽名等資訊。

本機號碼一鍵登入!推薦 Flutter 極光認證外掛

開通後,即可領取免費使用次數。

本機號碼一鍵登入!推薦 Flutter 極光認證外掛

本機號碼一鍵登入!推薦 Flutter 極光認證外掛

注:簽名生成工具 apk 包下載連結:res.wx.qq.com/open/zh_CN/…

安裝極光認證外掛

看過之前文章的朋友應該知道我們公司的 APP 是基於 Flutter 開發的,而且用到極光的推送外掛:《推薦一款 Flutter Push 推送功能外掛》mp.weixin.qq.com/s/l0_lghnp7…

這裡我們還是使用極光認證提供的 Flutter 外掛,根據官方提供的安裝方法有 github 原始碼整合和 pub 整合,這裡我推薦 pub 整合方法:

dependencies:
  jverify: 0.6.1
複製程式碼

外掛功能

有了外掛,接下來就可以寫功能了。先來看看外掛都提供哪些功能,這裡我們主要看 Flutter 外掛原始碼,

本機號碼一鍵登入!推薦 Flutter 極光認證外掛

具體包括:

  1. setup
  2. setDebugMode
  3. isInitSuccess
  4. checkVerifyEnable
  5. getToken
  6. verifyNumber
  7. loginAuth
  8. loginAuthSyncApi
  9. preLogin
  10. dismissLoginAuthView
  11. setCustomUI (註釋掉了該功能)
  12. setCustomAuthViewAllWidgets
  13. clearPreLoginCache
  14. setCustomAuthorizationView

極光認證提供了兩個方向性的功能:驗證手機號和一鍵登入功能,今天我們主要是用到一鍵登入功能,整個外掛使用流程大致是這樣的:

開發決策流程圖

主要用到的核心功能有:

  1. setup
  2. setDebugMode
  3. isInitSuccess
  4. checkVerifyEnable
  5. preLogin
  6. setCustomAuthViewAllWidgets
  7. loginAuth

每個功能都挺好理解的,可以過一遍程式碼。

  /// 統一 key
  final String f_result_key = "result";
  /// 錯誤碼
  final  String  f_code_key = "code";
  /// 回撥的提示資訊,統一返回 flutter 為 message
  final  String  f_msg_key  = "message";
  /// 運營商資訊
  final  String  f_opr_key  = "operator";
  
  final Jverify jverify = new Jverify();
複製程式碼

setup

由於我的 APP 是 Flutter 一套開發的,所以在用 setup() 時帶上 appKeychannel 屬性,Android 則在配置檔案中配置。

在 /android/app/build.gradle 中新增極光上建立應用的配置資訊,我之前用到極光推送了,用的同一個應用。

本機號碼一鍵登入!推薦 Flutter 極光認證外掛

// 註冊極光認證
jverify.setup(
    appKey: "6592925ae1*****658473",//"你自己應用的 AppKey",
    channel: "devloper-default");
複製程式碼

setDebugMode

這個簡單,就是是否需要 debug 模式,在開發中建議開啟,你也可以建立一個全域性變數,在開發中預設開啟,在打包 release 模式下是關閉的,避免在 APP 開發過程和打包過程中反覆去修改這個狀態

jverify.setDebugMode(isDebug); // 是否開啟除錯模式
複製程式碼

isInitSuccess

判斷 sdk 初始換是否成功:

jverify.isInitSuccess().then((map) {
  bool result = map[f_result_key];
  setState(() {
    if (result) {
      _result = "sdk 初始換成功";
    }else {
      _result = "sdk 初始換失敗";
    }
  });
複製程式碼

checkVerifyEnable

判斷當前的手機網路環境是否可以使用認證。

jverify.checkVerifyEnable().then((map) {
  bool result = map[f_result_key];
  setState(() {
    if (result) {
      _result = "當前網路環境【支援認證】!";
    }else {
      _result = "當前網路環境【不支援認證】!";
    }
  });
});
複製程式碼

以上的幾個方法基本都是在 sdk 初始化和驗證是否可以滿足一鍵登入條件。

preLogin

當環境滿足一鍵登入後,我們就開始進行一鍵登入操作了,但操作之前,官網提示我們最好先進行 preLogin SDK 一鍵登入預取號操作,理由是:

sdk會快取預取號結果,提升之後授權頁拉起速度。所以建議拉起授權頁前,比如在開屏頁或者業務入口頁預先呼叫此介面進行預取號。

請求成功後,不要頻繁重複呼叫。

不要在預取號回撥中重複呼叫預取號或者拉起授權頁介面。

jverify.preLogin().then((map) {
  int code = map[f_code_key];
  String message = map[f_msg_key];
  setState(() {
    _loading = false;
    _result = "[$code] message = $message";
  });
});
複製程式碼

好了,這時候是萬事具備,只欠東風了,但,就如我們開篇的截圖,我們必須要有一個介面,用於提示我們的使用者,我們需要開始一鍵登入操作,並得到他們的認可,以及獲取個人資訊的說明,也就需要下一個功能的定製化實現。

setCustomAuthViewAllWidgets

jverify.setCustomAuthorizationView(true, uiConfig, landscapeConfig: uiConfig);
複製程式碼

這裡需要注意的是:

Android 橫屏的 UI 配置,只有當 isAutorotate=true 時必須傳(也就是方法中的第一個引數),並且該配置只生效在 Android,iOS 使用 portraitConfig 的約束適配橫屏。

橫豎屏的 UI 則需要根據自己的 APP 設計風格來定製,demo 中提供簡要的配置:

final screenSize = MediaQuery.of(context).size;
final screenWidth = screenSize.width;
final screenHeight = screenSize.height;
bool isiOS = Platform.isIOS;

/// 自定義授權的 UI 介面,以下設定的圖片必須新增到資原始檔裡,
/// android專案將圖片存放至drawable資料夾下,可使用圖片選擇器的檔名,例如:btn_login.xml,入參為"btn_login"。
/// ios專案存放在 Assets.xcassets。
/// 
JVUIConfig uiConfig = JVUIConfig();
//uiConfig.authBackgroundImage = ;

//uiConfig.navHidden = true;
uiConfig.navColor = Colors.red.value;
uiConfig.navText = "coding01登入";
uiConfig.navTextColor = Colors.blue.value;
uiConfig.navReturnImgPath = "return_bg";//圖片必須存在

uiConfig.logoWidth = 100;
uiConfig.logoHeight = 100;
//uiConfig.logoOffsetX = isiOS ? 0 : null;//(screenWidth/2 - uiConfig.logoWidth/2).toInt();
uiConfig.logoOffsetY = 10;
uiConfig.logoVerticalLayoutItem = JVIOSLayoutItem.ItemSuper;
uiConfig.logoHidden = false;
uiConfig.logoImgPath = "logo";

uiConfig.numberFieldWidth = 200;
uiConfig.numberFieldHeight = 40 ;
//uiConfig.numFieldOffsetX = isiOS ? 0 : null;//(screenWidth/2 - uiConfig.numberFieldWidth/2).toInt();
uiConfig.numFieldOffsetY = isiOS ? 20 : 120;
uiConfig.numberVerticalLayoutItem = JVIOSLayoutItem.ItemLogo;
uiConfig.numberColor = Colors.blue.value;
uiConfig.numberSize = 18;

uiConfig.sloganOffsetY = isiOS ? 20 : 160;
uiConfig.sloganVerticalLayoutItem = JVIOSLayoutItem.ItemNumber;
uiConfig.sloganTextColor = Colors.black.value;
uiConfig.sloganTextSize = 15;
//uiConfig.sloganHidden = 0;

uiConfig.logBtnWidth = 220;
uiConfig.logBtnHeight = 50;
//uiConfig.logBtnOffsetX = isiOS ? 0 : null;//(screenWidth/2 - uiConfig.logBtnWidth/2).toInt();
uiConfig.logBtnOffsetY = isiOS ? 20 : 230;
uiConfig.logBtnVerticalLayoutItem = JVIOSLayoutItem.ItemSlogan;
uiConfig.logBtnText = "登入按鈕";
uiConfig.logBtnTextColor = Colors.brown.value;
uiConfig.logBtnTextSize = 16;
uiConfig.loginBtnNormalImage = "login_btn_normal";//圖片必須存在
uiConfig.loginBtnPressedImage = "login_btn_press";//圖片必須存在
uiConfig.loginBtnUnableImage = "login_btn_unable";//圖片必須存在


uiConfig.privacyState = true;//設定預設勾選
uiConfig.privacyCheckboxSize = 20;
uiConfig.checkedImgPath = "check_image";//圖片必須存在
uiConfig.uncheckedImgPath = "uncheck_image";//圖片必須存在
uiConfig.privacyCheckboxInCenter = true;
//uiConfig.privacyCheckboxHidden = false;

//uiConfig.privacyOffsetX = isiOS ? (20 + uiConfig.privacyCheckboxSize) : null;
uiConfig.privacyOffsetY = 15;// 距離底部距離
uiConfig.privacyVerticalLayoutItem = JVIOSLayoutItem.ItemSuper;
uiConfig.clauseName = "協議1";
uiConfig.clauseUrl = "http://www.baidu.com";
uiConfig.clauseBaseColor = Colors.black.value;
uiConfig.clauseNameTwo = "協議二";
uiConfig.clauseUrlTwo = "http://www.hao123.com";
uiConfig.clauseColor = Colors.red.value;
uiConfig.privacyText = ["1極","2光","3認","4證"];
uiConfig.privacyTextSize = 13;
//uiConfig.privacyWithBookTitleMark = true;
//uiConfig.privacyTextCenterGravity = false;


uiConfig.privacyNavColor =  Colors.red.value;;
uiConfig.privacyNavTitleTextColor = Colors.blue.value;
uiConfig.privacyNavTitleTextSize = 16;
uiConfig.privacyNavTitleTitle1 = "協議1 web頁標題";
uiConfig.privacyNavTitleTitle2 = "協議2 web頁標題";
uiConfig.privacyNavReturnBtnImage = "return_bg";//圖片必須存在;
複製程式碼

這裡就不再對每個引數進行說明了,很好理解。

好了接下來就是我們客戶端的最後一步了,調起一鍵登入介面和邏輯。

loginAuth

這裡提供了兩種方法:同步和非同步,就看自身業務邏輯需要了。

/// 方式一:使用同步介面 (如果想使用非同步介面,則忽略此步驟,看方式二)
/// 先,新增 loginAuthSyncApi 介面回撥的監聽
jverify.addLoginAuthCallBackListener((event){
  setState(() {
    _loading = false;
    _result = "監聽獲取返回資料:[${event.code}] message = ${event.message}";
  });
  print("通過新增監聽,獲取到 loginAuthSyncApi 介面返回資料,code=${event.code},message = ${event.message},operator = ${event.operator}");
});
/// 再,執行同步的一鍵登入介面
jverify.loginAuthSyncApi(autoDismiss: true);


/// 方式二:使用非同步介面 (如果想使用非同步介面,則忽略此步驟,看方式二)

/// 先,執行非同步的一鍵登入介面
jverify.loginAuth(true).then((map) {

  /// 再,在回撥裡獲取 loginAuth 介面非同步返回資料(如果是通過新增 JVLoginAuthCallBackListener 監聽來獲取返回資料,則忽略此步驟)
  int code = map[f_code_key];
  String content = map[f_msg_key];
  String operator = map[f_opr_key];
  setState(() {
    _loading = false;
    _result = "介面非同步返回資料:[$code] message = $content";
  });
  print("通過介面非同步返回,獲取到 loginAuth 介面返回資料,code=$code,message = $content,operator = $operator");
});
複製程式碼

到此,基本完成客戶端程式碼工作了,我們執行下看看效果:

本機號碼一鍵登入!推薦 Flutter 極光認證外掛

「coding01一鍵登入」:

本機號碼一鍵登入!推薦 Flutter 極光認證外掛

如果返回碼為:6000,則 message 的值就是我們需要的 loginToken。

下面我們開始後臺的介面對接工作了,我們可以將 loginToken 傳給我們自己的伺服器介面,然後再利用極光提供的REST API 提供的 loginTokenVerify API 獲取加密的手機號資料,註冊或者登入操作,下發給客戶端建立使用者或者登入成功後的 Auth 認證資訊。

loginTokenVerify API

功能說明:提交loginToken,驗證後返回手機號碼

呼叫介面:POST api.verification.jpush.cn/v1/web/logi…

我主要還是基於 Laravel PHP 框架來使用 loginTokenVerify API,這裡使用的是 GuzzleHttp 網路請求外掛:

/*
* 通過客戶端提供的 loginToken,請求極光介面獲得加密手機號
*/
public function jiguanVerify($loginToken)
{
    $client = new Client(['base_uri' => $this->loginTokenVerifyUrl]);
    try {
        $response = $client->request('POST', '', [
            'json' => [
                'loginToken' => $loginToken
            ],
            'auth' => [env('JPUSH_APPKEY'), env('JPUSH_MASTERSECRET')],
        ]);

        $contents = json_decode($response->getBody()->getContents(), true);

        // 正確結果
        // {"id":117270465679982592,"code":8000,"content":"get phone success","exID":"1234566","phone":"HpBLIQ/6SkFl0pAq0LMdw1aZ8RHoofgWmaY//LE+0ahkSdHC5oTCnjrR8Tj8y5naKVI03torFU+EzAQnwtVqAoQyYckT0S3Q02TKuAal3VRGiR5Lmp4g2A5Mh4/W5A4o6QFviHuBVJZE/WV0AzU5w4NGhpyQntOeF0UyovYATy4="}
        // 失敗結果
        // {"id":268773997490073600,"code":8001,"content":"get phone fail","exID":null,"phone":null}
        if ( $contents['code'] == 8000) {
            return [
                'verify' => true,
                'message' => $this->getPhone($contents['phone']) // 解碼獲取手機號
            ];
        }

        return [
            'verify' => false,
            'message' => $contents['content']
        ];
    } catch (RequestException $e) {
        return [
            'verify' => false,
            'message' => $e->getMessage()
        ];
    } catch (GuzzleException $e) {
        return [
            'verify' => false,
            'message' => $e->getMessage()
        ];
    }
}
複製程式碼

當然根據官網說明,通過 loginTokenVerify API 介面返回的手機號是加密的,需要進行解密,一開始申請認證時,我們在極光後臺配置了我們的「RSA 加密公鑰」,這時候就派上用場了。

本機號碼一鍵登入!推薦 Flutter 極光認證外掛

具體看方法 $this->getPhone($contents['phone'])

/**
 * @param $phone
 * @return string 解密的手機號
 */
private function getPhone($phone)
{
    $prefix = '-----BEGIN RSA PRIVATE KEY-----';
    $suffix = '-----END RSA PRIVATE KEY-----';
    $result = '';

    $encrypted = null;
    $prikey = null;

    $key = $prefix . "\n" . $prikey . "\n" . $suffix;
    openssl_private_decrypt(base64_decode($encrypted), $result, openssl_pkey_get_private($key));

    return $result . "\n";
}
複製程式碼

拿到手機號後,那剩下的就是和我們業務流程有關的程式碼了,利用手機號登入使用者資訊,或者建立使用者,然後下發登入成功的 Auth 資訊給我們的客戶端。

總結

有了極光認證提供的一鍵登入功能,我們客戶端開發就變得很簡單,不再需要使用者自己手動輸入手機號,客戶端和介面端去驗證手機號的有效性、下發驗證碼到第三方簡訊平臺、再由簡訊平臺下發給使用者,使用者再去客戶端去輸入驗證碼,然後驗證成功,再把資訊提交給介面,介面拿著手機號去做認證操作。

所有的操作都不需要了,使用者只需點一點「本地手機號一鍵登入」即可,剩下的都交給我們開發來完成,而且我們開發工作量也變得很少,只需要一個請求介面就可完成登入功能

這就是極光認證功能 —— 一鍵登入的作用。

參考閱讀

  • 推薦一款 Flutter Push 推送功能外掛,這是使用極光推送的 Flutter 外掛,可以簡化推送功能開發,推送一步到位,推薦閱讀:mp.weixin.qq.com/s/l0_lghnp7…

  • 跟我一步一步實現 Flutter 視訊播放外掛,這是一篇早期 Flutter 剛在國內使用時,針對視訊播放器 SDK 製作的 Flutter 外掛,後來有些大廠工程師們也參考這篇文章去使用和開發外掛,強烈推薦一看:mp.weixin.qq.com/s/goMXcCpcq…

  • JPush's officially supported Flutter plugin (Android & iOS). 極光推送官方支援的 Flutter 外掛(Android & iOS)。而本文的 demo 程式碼主要來自此,推薦檢視原始碼,是學習的最好途徑。github.com/jpush/jveri…

下一步我們來解讀 Flutter 外掛原始碼,未完待續

相關文章