本機號碼一鍵登入基本成為各個 APP 的標配了。
傳統的手機驗證碼登入方式:
- 輸入手機號;
- 前端判斷手機號是否有效;
- 後臺判斷手機號是否有效;
- 藉助第三方下發手機驗證碼;
- 前端有一個倒數計時,防止驗證碼過期輸入;
- 手機收到驗證碼;
- 複製貼上輸入驗證碼校驗;
- 將資訊發給後臺介面,驗證手機號和驗證碼是否正確,然後才是註冊和登入流程,下發認證 Auth 資訊。
這一套流程下來足夠費勁了。而上述的「本機號碼一鍵登入」完全無感,只要一點按鈕,所有操作一步到位。
今天我們來說一說如何利用「極光」做到「本機號碼一鍵登入」的。
開通「極光認證」
開通「極光認證」,需要先完成實名認證,實名認證後可以領取 1,000 次極光認證禮包。
認證成功後,填入 android 和 iOS 包名、簽名等資訊。
開通後,即可領取免費使用次數。
注:簽名生成工具 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 外掛原始碼,
具體包括:
- setup
- setDebugMode
- isInitSuccess
- checkVerifyEnable
- getToken
- verifyNumber
- loginAuth
- loginAuthSyncApi
- preLogin
- dismissLoginAuthView
- setCustomUI (註釋掉了該功能)
- setCustomAuthViewAllWidgets
- clearPreLoginCache
- setCustomAuthorizationView
極光認證提供了兩個方向性的功能:驗證手機號和一鍵登入功能,今天我們主要是用到一鍵登入功能,整個外掛使用流程大致是這樣的:
主要用到的核心功能有:
- setup
- setDebugMode
- isInitSuccess
- checkVerifyEnable
- preLogin
- setCustomAuthViewAllWidgets
- 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()
時帶上 appKey
和 channel
屬性,Android 則在配置檔案中配置。
在 /android/app/build.gradle 中新增極光上建立應用的配置資訊,我之前用到極光推送了,用的同一個應用。
// 註冊極光認證
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");
});
複製程式碼
到此,基本完成客戶端程式碼工作了,我們執行下看看效果:
「coding01一鍵登入」:
如果返回碼為: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 加密公鑰」,這時候就派上用場了。
具體看方法 $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 外掛原始碼,未完待續