知識
不管是網(wǎng)站,軟件還是小程序,都要直接或間接能為您產(chǎn)生價值,我們在追求其視覺表現(xiàn)的同時,更側(cè)重于功能的便捷,營銷的便利,運營的高效,讓網(wǎng)站成為營銷工具,讓軟件能切實提升企業(yè)內(nèi)部管理水平和效率。優(yōu)秀的程序為后期升級提供便捷的支持!
您當前位置>首頁 » 新聞資訊 » 小程序相關(guān) >
小程序靜默登錄方案設(shè)計
發(fā)表時間:2021-3-1
發(fā)布人:葵宇科技
瀏覽次數(shù):128
首先談?wù)勗谛〕绦虻拈_發(fā)中,如何借助微信的能力標識一個用戶?
微信官方提供了兩種標識:
OpenId
是一個用戶對于一個小程序/公眾號的標識,開發(fā)者可以通過這個標識識別出用戶。UnionId
是一個用戶對于同主體微信小程序/公眾號/APP 的標識,開發(fā)者需要在微信開放平臺下綁定相同賬號的主體。開發(fā)者可通過UnionId
,實現(xiàn)多個小程序、公眾號、甚至 APP 之間的數(shù)據(jù)互通。
同一個用戶的這兩個 ID 對于同一個小程序來說是永久不變的,就算用戶刪了小程序,下次用戶進入小程序,開發(fā)者依舊可以通過后臺的記錄標識出來。那么如何獲取OpenId
和UnionId
呢?
早期(2018 年 4 月之前)的小程序設(shè)計使用 wx.getUserInfo
接口,來獲取用戶信息。設(shè)計這個接口的初衷是希望開發(fā)者在真正需要用戶信息(如頭像、昵稱、手機號等)的情況下才去調(diào)取這個接口。但很多開發(fā)者為了拿到UnionId
,會在小程序啟動時直接調(diào)用這個接口,導(dǎo)致用戶在使用小程序的時候產(chǎn)生困擾,歸結(jié)起來有幾點:
- 開發(fā)者在小程序首頁直接調(diào)用
wx.getUserInfo
進行授權(quán),彈框獲取用戶信息,會使得一部分用戶點擊“拒絕”按鈕。 - 在開發(fā)者沒有處理用戶拒絕彈框的情況下,用戶必須授權(quán)頭像昵稱等信息才能繼續(xù)使用小程序,會導(dǎo)致某些用戶放棄使用該小程序。
- 用戶沒有很好的方式重新授權(quán),盡管微信官方增加了設(shè)置頁面,可以讓用戶選擇重新授權(quán),但很多用戶并不知道可以這么操作。
微信官方也意識到了這個問題,針對獲取用戶信息更新了三個能力:
- 使用組件來獲取用戶信息。
- 若用戶滿足一定條件,則可以用
wx.login
獲取到的 code 直接換到unionId
。 wx.getUserInfo
不需要依賴wx.login
就能調(diào)用得到數(shù)據(jù)。
本文主要講述的是第二點能力,微信官方鼓勵開發(fā)者在不騷擾用戶的情況下合理獲得unionid
,而僅在必要時才向用戶彈窗申請使用昵稱頭像,從而衍生出「靜默登錄」和「用戶登錄」兩種概念。
2. 什么是靜默登錄?
小程序可以通過微信官方提供的登錄能力方便地獲取微信提供的用戶身份標識,快速建立小程序內(nèi)的用戶體系。
很多開發(fā)者會把 wx.login
和 wx.getUserInfo
捆綁調(diào)用當成登錄使用,其實 wx.login
已經(jīng)可以完成登錄,wx.getUserInfo
只是獲取額外的用戶信息。
在 wx.login
獲取到 code
后,會發(fā)送到開發(fā)者后端,開發(fā)者后端通過接口去微信后端換取到 openid
和 sessionKey
(現(xiàn)在會將 unionid
也一并返回)后,把自定義登錄態(tài) 3rd_session
(本業(yè)務(wù)命名為auth-token
) 返回給前端,就已經(jīng)完成登錄行為了。wx.login
行為是靜默,不必授權(quán)的,用戶不會察覺。
wx.getUserInfo
只是為了提供更優(yōu)質(zhì)的服務(wù)而存在,比如獲取用戶的手機號注冊會員,或者展示頭像昵稱,判斷性別,開發(fā)者可通過 unionId
和其他公眾號上已有的用戶畫像結(jié)合來提供歷史數(shù)據(jù)。因此開發(fā)者不必在用戶剛剛進入小程序的時候就強制要求授權(quán)。
2.1 靜默登錄流程時序
官方給出了 wx.login
的最佳實踐如下:
靜默登錄英文簡稱為silentLogin
,代碼如下所示:
private async silentLogin(): Promise<void> {
try {
this.status.silentLogin.ing();
// 獲取臨時登錄憑證code
const code = await getWxLoginCode();
// 將code發(fā)送給服務(wù)端
const res = await API.login(code);
// 保存登錄信息,如auth-token
storage.setSync(constant.STORAGE_SESSION_KEY, res.data);
this.status.silentLogin.success();
} catch (error) {
logger.error('靜默登錄失敗', error);
this.status.silentLogin.fail(error);
throw error;
}
}
復(fù)制代碼
總結(jié)為以下三步:
- 小程序端調(diào)用
wx.login()
獲取 臨時登錄憑證code
,并回傳到開發(fā)者服務(wù)器。 - 服務(wù)器端調(diào)用
auth.code2Session
接口,換取 用戶唯一標識OpenID
和 會話密鑰session_key
。 - 開發(fā)者服務(wù)器可以根據(jù)用戶標識來生成自定義登錄態(tài)(例如:
auth-token
),用于后續(xù)業(yè)務(wù)邏輯中前后端交互時識別用戶身份。
2.2 開發(fā)者后臺校驗與解密開放數(shù)據(jù)
靜默登錄成功后,微信服務(wù)器端會下發(fā)一個session_key
給服務(wù)端,而這個會在需要獲取微信開放數(shù)據(jù)的時候會用到。
為了確保開放接口返回用戶數(shù)據(jù)的安全性,微信會對明文數(shù)據(jù)進行簽名。開發(fā)者可以根據(jù)業(yè)務(wù)需要對數(shù)據(jù)包進行簽名校驗,確保數(shù)據(jù)的完整性。
- 小程序通過調(diào)用接口(如
wx.getUserInfo
)獲取數(shù)據(jù)時,如果用戶已經(jīng)授權(quán),接口會同時返回以下幾個字段。如用戶未授權(quán),會先彈出用戶彈窗,用戶點擊同意授權(quán),接口會同時返回以下幾個字段。相反如果用戶拒絕授權(quán),將調(diào)用失敗。
屬性 | 類型 | 說明 |
---|---|---|
userInfo | UserInfo | 用戶信息對象,不包含 openid 等敏感信息 |
rawData | string | 不包括敏感信息的原始數(shù)據(jù)字符串,用于計算簽名 |
signature | string | 使用 sha1( rawData + sessionkey ) 得到字符串,用于校驗用戶信息 |
encryptedData | string | 包括敏感數(shù)據(jù)在內(nèi)的完整用戶信息的加密數(shù)據(jù) |
iv | string | 加密算法的初始向量 |
cloudID | string | 敏感數(shù)據(jù)對應(yīng)的云 ID,開通云開發(fā)的小程序才會返回,可通過云調(diào)用直接獲取開放數(shù)據(jù) |
- 開發(fā)者將
signature
、rawData
發(fā)送到開發(fā)者服務(wù)器進行校驗。服務(wù)器利用用戶對應(yīng)的session_key
使用相同的算法計算出簽名signature2
,比對signature
與signature2
即可校驗數(shù)據(jù)的完整性。開發(fā)者服務(wù)器告訴前端開發(fā)者數(shù)據(jù)可信,即可安全使用用戶信息數(shù)據(jù)。 - 如果開發(fā)者想要獲取敏感數(shù)據(jù)(如openid,unionID),則將
encryptedData
和iv
發(fā)送到開發(fā)者服務(wù)器,由服務(wù)器使用session_key
(對稱解密密鑰)進行對稱解密,獲取敏感數(shù)據(jù)進行存儲并返回給前端開發(fā)者。
注意: 因為需要用戶主動觸發(fā)才能發(fā)起獲取手機號接口,所以該功能不由 API 來調(diào)用(即上述提到的wx.getUserInfo
是無法獲取手機號的),需用 button
組件的點擊來觸發(fā)。獲得encryptedData
和iv
,同樣發(fā)送給開發(fā)者服務(wù)器,由服務(wù)器使用session_key
(對稱解密密鑰)進行對稱解密,獲得對應(yīng)的手機號。
需要關(guān)注的是,2021年2月23日,微信團隊發(fā)布了《小程序登錄、用戶信息相關(guān)接口調(diào)整說明》,進行了如下調(diào)整:
- 2021年2月23日起,通過
wx.login
接口獲取的登錄憑證可直接換取unionID
。 - 2021年4月13日后發(fā)布新版本的小程序,無法通過
wx.getUserInfo
接口獲取用戶個人信息(頭像、昵稱、性別與地區(qū)),將直接獲取匿名數(shù)據(jù)。getUserInfo
接口獲取加密后的openID
與unionID
數(shù)據(jù)的能力不做調(diào)整。 - 新增
getUserProfile
接口(基礎(chǔ)庫2.10.4版本開始支持),可獲取用戶頭像、昵稱、性別及地區(qū)信息,開發(fā)者每次通過該接口獲取用戶個人信息均需用戶確認。
即開發(fā)者通過組件調(diào)用wx.getUserInfo
將不再彈出彈窗,直接返回匿名的用戶個人信息。如果要獲取用戶頭像、昵稱、性別及地區(qū)信息,需要改造成wx.getUserProfile
接口。
2.3 session_key 的有效期
開發(fā)者如果遇到因為 session_key
不正確而校驗簽名失敗或解密失敗,請關(guān)注下面幾個與 session_key
有關(guān)的注意事項。
wx.login
調(diào)用時,用戶的session_key
可能會被更新而致使舊session_key
失效(刷新機制存在最短周期,如果同一個用戶短時間內(nèi)多次調(diào)用wx.login
,并非每次調(diào)用都導(dǎo)致session_key
刷新)。開發(fā)者應(yīng)該在明確需要重新登錄時才調(diào)用wx.login
,及時通過auth.code2Session
接口更新服務(wù)器存儲的session_key
。- 微信不會把
session_key
的有效期告知開發(fā)者。我們會根據(jù)用戶使用小程序的行為對session_key
進行續(xù)期。用戶越頻繁使用小程序,session_key
有效期越長。 - 開發(fā)者在
session_key
失效時,可以通過重新執(zhí)行登錄流程獲取有效的session_key
。使用接口wx.checkSession
可以校驗session_key
是否有效,從而避免小程序反復(fù)執(zhí)行登錄流程。 - 當開發(fā)者在實現(xiàn)自定義登錄態(tài)時,可以考慮以
session_key
有效期作為自身登錄態(tài)有效期,也可以實現(xiàn)自定義的時效性策略。
3. 靜默登錄的調(diào)用時機
3.1 小程序啟動時調(diào)用
由于大部分情況都需要依賴登錄態(tài),在小程序啟動的時候(app.onLaunch()
)調(diào)用靜默登錄是最常見的手段。這里我們封裝一個login
函數(shù)如下所示,首先調(diào)用wx.checkSession
判斷session_key
是否過期,如果session_key
未過期且本地存在auth_token
自定義登錄態(tài),表示當前的靜默登錄態(tài)仍然有效,無需進行其它操作。否則,表示靜默登錄態(tài)失效或者新用戶從未發(fā)起過靜默登錄,那么發(fā)起靜默登錄流程。
public async login(): Promise<void> {
// 調(diào)用wx.checkSession判斷session_key是否過期
const hasSession = await checkSession();
// 本地已有可用登錄態(tài)且session_key未過期,resolve。
if (this.getAuthToken() && hasSession) return Promise.resolve();
// 否則,發(fā)起靜默登錄
await this.silentLogin();
}
復(fù)制代碼
但是由于原生的小程序啟動流程中, App,Page,Component 的生命周期鉤子函數(shù),都不支持異步阻塞。所以很有可能出現(xiàn)小程序頁面加載完成后,靜默登錄過程還沒有執(zhí)行完畢的情況,這會導(dǎo)致后續(xù)一些依賴登錄態(tài)的操作(比如請求發(fā)起)出錯。
3.2 接口請求發(fā)起時調(diào)用
保險起見,如果某些接口需要攜帶自定義登錄態(tài)進行鑒權(quán),則需要在請求發(fā)起時進行攔截,校驗登錄態(tài)。整個流程如下圖所示:
- 攔截 request:
- 判斷是否需要鑒權(quán):請求發(fā)起時,攔截請求,判斷請求是否需要添加
auth-token
,如若不需要,直接發(fā)起請求。如若需要,執(zhí)行第二步。 - 判斷是否需要發(fā)起靜默登錄:判斷 storage 中是否存在
auth-token
,如若不存在,發(fā)起靜默登錄,靜默登錄過程上文時序圖已經(jīng)闡述過了,總結(jié)就是獲取auth-token
并存入本地storage
。 - 請求頭部添加
auth-token
:添加auth-token
,發(fā)起請求。
- 判斷是否需要鑒權(quán):請求發(fā)起時,攔截請求,判斷請求是否需要添加
- 與服務(wù)端通信:發(fā)起請求,服務(wù)端處理請求返回結(jié)果。
- 攔截 response: 解析狀態(tài)碼
- 狀態(tài)碼為
AUTH_FAIL
:服務(wù)端返回code
為“鑒權(quán)失敗”,觸發(fā)這種情景的原因有兩個,一是接口需要鑒權(quán),但是發(fā)起請求時未攜帶auth-token
,二是auth-token
過期。“鑒權(quán)失敗”會重新發(fā)起靜默登錄,拿到新的auth-token
后發(fā)起請求,這個動作對用戶來說是無感知的。 - 狀態(tài)碼為
USER_WX_SESSIONKEY_EXPIRE
:服務(wù)器返回code
為“用戶登錄態(tài)過期”,這是針對用戶授權(quán)手機號登錄失敗定制的狀態(tài)碼,如果登錄態(tài)已過期,表示存儲在服務(wù)端的session_key
也是過期的,那么點擊授權(quán)手機號獲取的加密數(shù)據(jù)發(fā)送到服務(wù)端進行對稱解密,由于session_key
失效,無法解密出真正的手機號。因此需要重新發(fā)起靜默登錄,等待用戶重新點擊授權(quán)按鈕獲取新的加密數(shù)據(jù),然后發(fā)起新的解密請求 - 狀態(tài)碼為其它:比如
Success
或者其他業(yè)務(wù)請求錯誤的情況,不進行攔截,返回 response 讓業(yè)務(wù)代碼解析。
- 狀態(tài)碼為
3.3 wx.checkSession 罷工之謎
基于上述接口請求發(fā)起時調(diào)用的流程,很多人會有疑問,既然服務(wù)端會返回auth-token
過期的狀態(tài)碼,為啥不在請求發(fā)送前進行攔截,使用wx.checkSession
接口校驗登錄態(tài)是否過期(如下圖所示,增加紅框內(nèi)的步驟)?
這是因為,我們通過實驗發(fā)現(xiàn),在 session_key
已過期的情況下,wx.checkSession
有一定的幾率返回true
。即增加wx.checkSession
步驟并不能百分百保證登錄態(tài)不會過期,后續(xù)仍然需要對不同的狀態(tài)碼進行處理。
所以結(jié)論是:wx.checkSession
可靠性是不達 100% 的。
基于以上,我們需要對 session_key
的過期做一些容錯處理:
- 發(fā)起需要使用
session_key
的請求前,做一次wx.checkSession
操作,如果失敗了刷新登錄態(tài)。 - 后端使用
session_key
解密開放數(shù)據(jù)失敗之后,返回特定錯誤碼(如:USER_WX_SESSIONKEY_EXPIRE
),前端刷新登錄態(tài)。
3.4 并發(fā)處理
假設(shè)一個新用戶進入一個業(yè)務(wù)復(fù)雜的頁面,同時發(fā)起五個不同的業(yè)務(wù)請求,恰巧這五個請求都需要鑒權(quán),那么五個請求都會被攔截并發(fā)起靜默登錄。顯然,這樣的并發(fā)是不合理的。
基于此,我們設(shè)計了如下方案:
-
單隊列模式:
- 請求鎖:同一時間,只允許一個正在過程中的網(wǎng)絡(luò)請求。
- 等待隊列:請求被鎖定之后,同樣的請求都會被推入隊列,等待進行中的請求返回后,消費同一個結(jié)果。
-
熔斷機制:如果短時間內(nèi)多次調(diào)用,則停止響應(yīng)一段時間,類似于 TCP 慢啟動。
如上圖所示,首先refreshLogin
請求入隊,隊列中只有一個請求,發(fā)送該請求,同時保險絲計入次數(shù) 1,服務(wù)端返回請求結(jié)果,消費結(jié)果。接著又發(fā)起一個refreshLogin
請求,隊列中只有一個請求,發(fā)送該請求,同時保險絲計入次數(shù) 2。然后又連續(xù)發(fā)起三個請求,由于上一個請求還沒有執(zhí)行完成,將這三個請求入隊,等待上一個請求結(jié)果返回,隊列中的四個請求消費同一個結(jié)果。由于觸發(fā)熔斷,保險絲重置,停止響應(yīng)一段時間。
以上兩種方案通過裝飾器模式引入,代碼如下所示,refreshLogin
函數(shù)其實是slientLogin
函數(shù)的一層封裝,用于接口發(fā)起時調(diào)用。而前面提到的login
函數(shù)也是slientLogin
函數(shù)的一層封裝,用戶小程序啟動時調(diào)用。
@singleQueue({ name: 'refreshLogin' })
@fuseLine({ name: 'refreshLogin' })
public async refreshLogin(): Promise<void> {
try {
// 清除 Session
this.clearSession();
await this.silentLogin();
} catch (error) {
throw error;
}
}
復(fù)制代碼
4. 最后
讀到這里,相信你已經(jīng)了解「靜默登錄」和「用戶登錄」的區(qū)別。「靜默登錄」是獲取微信登錄態(tài)的過程,通過獲取微信提供的用戶身份標識,快速建立小程序內(nèi)的用戶體系。「用戶登錄」是用戶授權(quán)個人開放數(shù)據(jù)成為會員的過程,是指從游客態(tài)轉(zhuǎn)換成會員態(tài)的,擁有購買等操作權(quán)限。
相關(guān)案例查看更多
相關(guān)閱讀
- 報廢車
- asp網(wǎng)站
- 網(wǎng)站建設(shè)首頁
- 昆明做網(wǎng)站建設(shè)的公司排名
- 昆明小程序代建
- 專業(yè)網(wǎng)站建設(shè)公司
- 云南網(wǎng)站制作
- 云南企業(yè)網(wǎng)站
- 網(wǎng)站建設(shè)列表網(wǎng)
- 云南省建設(shè)廳網(wǎng)站官網(wǎng)
- 網(wǎng)站建設(shè)方案 doc
- 云南網(wǎng)站建設(shè)首選
- 云南網(wǎng)站建設(shè) 網(wǎng)絡(luò)服務(wù)
- 云南做軟件
- 汽車報廢回收管理軟件
- 昆明小程序開發(fā)
- 汽車報廢系統(tǒng)
- 快排推廣
- 開通微信小程序被騙
- 云南衛(wèi)視小程序
- 國內(nèi)知名網(wǎng)站建設(shè)公司排名
- 云南小程序開發(fā)課程
- 汽車回收管理
- 網(wǎng)站建設(shè)公司網(wǎng)站
- 云南網(wǎng)站建設(shè)專家
- 汽車報廢拆解管理系統(tǒng)
- 汽車報廢管理系統(tǒng)
- 軟件定制
- 昆明小程序公司
- 迪慶小程序開發(fā)