小編給大家分享一下微信小程序登錄前端設計與實現(xiàn)的示例分析,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
公司主營業(yè)務:成都網(wǎng)站設計、網(wǎng)站制作、移動網(wǎng)站開發(fā)等業(yè)務。幫助企業(yè)客戶真正實現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競爭能力。創(chuàng)新互聯(lián)建站是一支青春激揚、勤奮敬業(yè)、活力青春激揚、勤奮敬業(yè)、活力澎湃、和諧高效的團隊。公司秉承以“開放、自由、嚴謹、自律”為核心的企業(yè)文化,感謝他們對我們的高要求,感謝他們從不同領域給我們帶來的挑戰(zhàn),讓我們激情的團隊有機會用頭腦與智慧不斷的給客戶帶來驚喜。創(chuàng)新互聯(lián)建站推出鄰水免費做網(wǎng)站回饋大家。對于登錄/注冊的設計如此精雕細琢的目的,當然是想讓這個作為應用的基礎能力,有足夠的健壯性,避免出現(xiàn)全站性的阻塞。
同時要充分考慮如何解耦和封裝,在開展新的小程序的時候,能更快的去復用能力,避免重復采坑。
登錄注冊這模塊,就像個冰山,我們以為它就是「輸入賬號密碼,就完成登錄了」,但實際下面還有各種需要考慮的問題。
在此,跟在座的各位分享一下,最近做完一個小程序登錄/注冊模塊之后,沉淀下來的一些設計經(jīng)驗和想法。
在用戶瀏覽小程序的過程中,由業(yè)務需要,往往需要獲取用戶的一些基本信息,常見的有:
微信昵稱
微信手機號
而不同的產(chǎn)品,對于用戶的信息要求不盡相同,也會有不一樣的授權流程。
第一種,常見于電商系統(tǒng)中,用戶購買商品的時候,為了識別用戶多平臺的賬號,往往用手機號去做一個聯(lián)系,這時候需要用戶去授權手機號。
第二種,為了讓用戶信息得到基本的初始化,往往需要更進一步獲取用戶信息:如微信昵稱,unionId
等,就需要詢問用戶授權。
第三種,囊括第一種,第二種。
秉著沉淀一套通用的小程序登錄方案和服務為目標,我們?nèi)シ治鲆幌聵I(yè)務,得出變量。
在做技術設計之前,講點必要的廢話,對一些概念進行基本調(diào)頻。
登錄在英文中是 「login」,對應的還有 「logout」。而登錄之前,你需要擁有一個賬號,就要 「register」(or sign up)。
話說一開始的產(chǎn)品是沒有登錄/注冊功能的,用的人多了就慢慢有了。出于產(chǎn)品本身的需求,需要對「用戶」進行身份識別。
在現(xiàn)實社會中,我們每個人都有一個身份ID:身份證。當我到了16歲的時候,第一次去公安局領身份證的時候,就完成了一次「注冊」行為。然后我去網(wǎng)吧上網(wǎng),身份證刷一下,完成了一次「登錄」行為。
那么對于虛擬世界的互聯(lián)網(wǎng)來說,這個身份證明就是「賬號+密碼」。
常見的登錄/注冊方式有:
賬號密碼注冊
在互聯(lián)網(wǎng)的早期,個人郵箱和手機覆蓋度小。所以,就需要用戶自己想一個賬號名,我們注冊個QQ號,就是這種形式。
郵箱地址注冊
千禧年之后,PC互聯(lián)網(wǎng)時代快速普及,我們都創(chuàng)建了屬于自己的個人郵箱。加上QQ也自帶郵箱賬號。由于郵箱具有個人私密性,且能夠進行信息的溝通,因此,大部分網(wǎng)站開始采用郵箱賬號作為用戶名來進行注冊,并且會在注冊的過程中要求登錄到相應郵箱內(nèi)查收激活郵件,驗證我們對該注冊郵箱的所有權。
手機號碼注冊
在互聯(lián)網(wǎng)普及之后,智能手機與移動互聯(lián)網(wǎng)發(fā)展迅猛。手機也成為每個人必不可少的移動設備,同時移動互聯(lián)網(wǎng)也已經(jīng)深深融入每個人的現(xiàn)代生活當中。所以,相較于郵箱,目前手機號碼與個人的聯(lián)系更加緊密,而且越來越多的移動應用出現(xiàn),采用手機號碼作為用戶名的注冊方式也得到了廣泛的使用。
到了 2020 年,微信用戶規(guī)模達 12 億。那么,微信賬號,起碼在中國,已成為新一代互聯(lián)網(wǎng)世界的「身份標識」。
而對微信小程序而言,天然就能知道當前用戶的微信賬號ID。微信允許小程序應用,能在用戶無感知的情況下,悄無聲息的「登錄」到我們的小程序應用中去,這個就是我們經(jīng)常稱之為的「靜默登錄」。
其實微信小程序的登錄,跟傳統(tǒng) Web 應用的「單點登錄」本質是一樣的概念。
單點登錄:在 A 站登錄了,C 站和 B 站能實現(xiàn)快速的「靜默登錄」。
微信小程序登錄:在微信中,登錄了微信賬號,那么在整個小程序生態(tài)中,都可以實現(xiàn)「靜默登錄」。
由于 Http 本來是無狀態(tài)的,業(yè)界基本對于登錄態(tài)的一般做法:
cookie-session:常用于瀏覽器應用中
access token:常用于移動端等非瀏覽器應用
在微信小程序來說,對于「JS邏輯層」并不是一個瀏覽器環(huán)境,自然沒有Cookie
,那么通常會使用access token
的方式。
對于需要更進一步獲取用的用戶昵稱、用戶手機號等信息的產(chǎn)品來說。微信出于用戶隱私的考慮,需要用戶主動同意授權。小程序應用才能獲取到這部分信息,這就有了目前流行的小程序「授權用戶信息」、「授權手機號」的交互了。
出于不同的用戶信息敏感度不同的考慮,微信小程序對于不同的用戶信息提供「授權」的方式不盡相同:
調(diào)用具體 API 方式,彈窗授權。
例如調(diào)用wx.getLocation()
的時候,如果用戶未授權,則會彈出地址授權界面。
如果拒絕了,就不會再次彈窗,wx.getLocation()
直接返回失敗。
方式。
僅支持:用戶敏感信息,用戶手機號,需要配合后端進行對稱加解密,方能拿到數(shù)據(jù)。
用戶已拒絕,再次點擊按鈕,仍然會彈窗。
通過wx.authorize()
,提前詢問授權,之后需要獲取相關信息的時候不用再次彈出授權。
梳理清楚了概念之后,我們模塊的劃分上,可以拆分為兩大塊:
登錄:負責與服務端創(chuàng)建起一個會話,這個會話實現(xiàn)靜默登錄以及相關的容錯處理等,模塊命名為:Session
授權:負責與用戶交互,獲取與更新信息,以及權限的控制處理等,模塊命名為:Auth
微信官方提供的登錄方案,總結為三步:
前端通過wx.login()
獲取一次性加密憑證 code,交給后端。
后端把這個 code 傳輸給微信服務器端,換取用戶標識openId
和授權憑證session_key
。(用于后續(xù)服務器端和微信服務器的特殊 API 調(diào)用,具體看:微信官方文檔-服務端獲取開放數(shù)據(jù))。
后端把從微信服務器獲取到的用戶憑證與自行生成的登錄態(tài)憑證(token),傳輸給前端。前端保存起來,下次請求的時候帶給后端,就能識別哪個用戶。
如果只是實現(xiàn)這個流程的話,挺簡單的。
但要實現(xiàn)一個健壯的登錄過程,還需要注意更多的邊界情況:
收攏wx.login()
的調(diào)用:
由于wx.login()
會產(chǎn)生不可預測的副作用,例如會可能導致session_key
失效,從而導致后續(xù)的授權解密場景中的失敗。我們這里可以提供一個像session.login()
的方法,掌握wx.login()
控制權,對其做一系列的封裝和容錯處理。
調(diào)用的時機:
通常我們會在應用啟動的時候(app.onLaunch()
),去發(fā)起靜默登錄。但這里會由小程序生命周期設計問題而導致的一個異步問題:加載頁面的時候,去調(diào)用一個需要登錄態(tài)的后端 API 的時候,前面異步的靜態(tài)登錄過程有可能還沒有完成,從而導致請求失敗。
當然也可以在第一個需要登錄態(tài)的接口調(diào)用的時候以異步阻塞的方式發(fā)起登錄調(diào)用,這個需要結合良好設計的接口層。
以上講到的兩種場景的詳細設計思路下文會講到。
并發(fā)調(diào)用的問題:
在業(yè)務場景中,難免會出現(xiàn)多處代碼需要觸發(fā)登錄,如果遇到極端情況,這多處代碼同時間發(fā)起調(diào)用。那就會造成短時間多次發(fā)起登錄過程,盡管之前的請求還沒有完成。針對這種情況,我們可以以第一個調(diào)用為阻塞,后續(xù)調(diào)用等待結果,就像精子和卵子結合的過程。
未過期調(diào)用的問題:
如果我們的登錄態(tài)未過期,完全可以正常使用的,默認情況就不需再去發(fā)起登錄過程了。這時候我們可以默認情況下先去檢查登錄態(tài)是否可用,不能用,我們再發(fā)起請求。然后還可以提供一個類似session.login({ force: true })
的參數(shù)去強行發(fā)起登錄。
1. 應用啟動的時候調(diào)用
因為大部分情況都需要依賴登錄態(tài),我們會很自然而然的想到把這個調(diào)用的時機放到應用啟動的時候(app.onLaunch()
)來調(diào)用。
但是由于原生的小程序啟動流程中,App
,Page
,Component
的生命周期鉤子函數(shù),都不支持異步阻塞。
那么我們很容易會遇到app.onLaunch
發(fā)起的「登錄過程」在page.onLoad
的時候還沒有完成,我們就無法正確去做一些依賴登錄態(tài)的操作。
針對這種情況,我們設計了一個狀態(tài)機的工具:status
基于狀態(tài)機,我們就可以編寫這樣的代碼:
import { Status } from '@beautywe/plugin-status';// on app.jsApp({ status: { login: new Status('login'); }, onLaunch() { session // 發(fā)起靜默登錄調(diào)用 .login() // 把狀態(tài)機設置為 success .then(() => this.status.login.success()) // 把狀態(tài)機設置為 fail .catch(() => this.status.login.fail()); }, });// on page.jsPage({ onLoad() { const loginStatus = getApp().status.login; // must 里面會進行狀態(tài)的判斷,例如登錄中就等待,登錄成功就直接返回,登錄失敗拋出等。 loginStatus().status.login.must(() => { // 進行一些需要登錄態(tài)的操作... }); }, });復制代碼
2. 在「第一個需要登錄態(tài)接口」被調(diào)用的時候去發(fā)起登錄
更進一步,我們會發(fā)現(xiàn),需要登錄態(tài)的更深層次的節(jié)點是在發(fā)起的「需要登錄態(tài)的后端 API 」的時候。
那么我們可以在調(diào)用「需要登錄態(tài)的后端 API」的時候再去發(fā)起「靜默登錄」,對于并發(fā)的場景,讓其他請求等待一下就好了。
以 fly.js 作為wx.request()
封裝的「網(wǎng)絡請求層」,做一個簡單的例子:
// 發(fā)起請求,并表明該請求是需要登錄態(tài)的fly.post('https://...', params, { needLogin: true });// 在 fly 攔截器中處理邏輯fly.interceptors.request.use(async (req)=>{ // 在請求需要登錄態(tài)的時候 if (req.needLogin !== false) { // ensureLogin 核心邏輯是:判斷是否已登錄,如否發(fā)起登錄調(diào)用,如果正在登錄,則進入隊列等待回調(diào)。 await session.ensureLogin(); // 登錄成功后,獲取 token,通過 headers 傳遞給后端。 const token = await session.getToken(); Object.assign(req.headers, { [AUTH_KEY_NAME]: token }); } return req; });復制代碼
當自定義登錄態(tài)過期的時候,后端需要返回特定的狀態(tài)碼,例如:AUTH_EXPIRED
、AUTH_INVALID
等。
前端可以在「網(wǎng)絡請求層」去監(jiān)聽所有請求的這個狀態(tài)碼,然后發(fā)起刷新登錄態(tài),再去重放失敗的請求:
// 添加響應攔截器fly.interceptors.response.use( (response) => { const code = res.data; // 登錄態(tài)過期或失效 if ( ['AUTH_EXPIRED', 'AUTH_INVALID'].includes(code) ) { // 刷新登錄態(tài) await session.refreshLogin(); // 然后重新發(fā)起請求 return fly.request(request); } } )復制代碼
那么如果并發(fā)的發(fā)起多個請求,都返回了登錄態(tài)失效的狀態(tài)碼,上述代碼就會被執(zhí)行多次。
我們需要對session.refreshLogin()
做一些特殊的容錯處理:
請求鎖:同一時間,只允許一個正在過程中的網(wǎng)絡請求。
等待隊列:請求被鎖定之后,調(diào)用該方法的所有調(diào)用,都推入一個隊列中,等待網(wǎng)絡請求完成之后共用返回結果。
熔斷機制:如果短時間內(nèi)多次調(diào)用,則停止響應一段時間,類似于 TCP 慢啟動。
示例代碼:
class Session { // .... // 刷新登錄保險絲,最多重復 3 次,然后熔斷,5s 后恢復 refreshLoginFuseLine = REFRESH_LOGIN_FUSELINE_DEFAULT; refreshLoginFuseLocked = false; refreshLoginFuseRestoreTime = 5000; // 熔斷控制 refreshLoginFuse(): Promise{ if (this.refreshLoginFuseLocked) { return Promise.reject('刷新登錄-保險絲已熔斷,請稍后'); } if (this.refreshLoginFuseLine > 0) { this.refreshLoginFuseLine = this.refreshLoginFuseLine - 1; return Promise.resolve(); } else { this.refreshLoginFuseLocked = true; setTimeout(() => { this.refreshLoginFuseLocked = false; this.refreshLoginFuseLine = REFRESH_LOGIN_FUSELINE_DEFAULT; logger.info('刷新登錄-保險絲熔斷解除'); }, this.refreshLoginFuseRestoreTime); return Promise.reject('刷新登錄-保險絲熔斷!!'); } } // 并發(fā)回調(diào)隊列 refreshLoginQueueMaxLength = 100; refreshLoginQueue: any[] = []; refreshLoginLocked = false; // 刷新登錄態(tài) refreshLogin(): Promise { return Promise.resolve() // 回調(diào)隊列 + 熔斷 控制 .then(() => this.refreshLoginFuse()) .then(() => { if (this.refreshLoginLocked) { const maxLength = this.refreshLoginQueueMaxLength; if (this.refreshLoginQueue.length >= maxLength) { return Promise.reject(`refreshLoginQueue 超出容量:${maxLength}`); } return new Promise((resolve, reject) => { this.refreshLoginQueue.push([resolve, reject]); }); } this.refreshLoginLocked = true; }) // 通過前置控制之后,發(fā)起登錄過程 .then(() => { this.clearSession(); wx.showLoading({ title: '刷新登錄態(tài)中', mask: true }); return this.login() .then(() => { wx.hideLoading(); wx.showToast({ icon: 'none', title: '登錄成功' }); this.refreshLoginQueue.forEach(([resolve]) => resolve()); this.refreshLoginLocked = false; }) .catch(err => { wx.hideLoading(); wx.showToast({ icon: 'none', title: '登錄失敗' }); this.refreshLoginQueue.forEach(([, reject]) => reject()); this.refreshLoginLocked = false; throw err; }); }); // ...}復制代碼
我們從上面的「靜默登錄」之后,微信服務器端會下發(fā)一個session_key
給后端,而這個會在需要獲取微信開放數(shù)據(jù)的時候會用到。
而session_key
是有時效性的,以下摘自微信官方描述:
會話密鑰 session_key 有效性
開發(fā)者如果遇到因為 session_key 不正確而校驗簽名失敗或解密失敗,請關注下面幾個與 session_key 有關的注意事項。
wx.login 調(diào)用時,用戶的 session_key可能會被更新而致使舊 session_key 失效(刷新機制存在短周期,如果同一個用戶短時間內(nèi)多次調(diào)用 wx.login,并非每次調(diào)用都導致 session_key 刷新)。開發(fā)者應該在明確需要重新登錄時才調(diào)用 wx.login,及時通過 auth.code2Session 接口更新服務器存儲的 session_key。
微信不會把 session_key 的有效期告知開發(fā)者。我們會根據(jù)用戶使用小程序的行為對 session_key 進行續(xù)期。用戶越頻繁使用小程序,session_key 有效期越長。
開發(fā)者在 session_key 失效時,可以通過重新執(zhí)行登錄流程獲取有效的 session_key。使用接口 wx.checkSession可以校驗 session_key 是否有效,從而避免小程序反復執(zhí)行登錄流程。
當開發(fā)者在實現(xiàn)自定義登錄態(tài)時,可以考慮以 session_key 有效期作為自身登錄態(tài)有效期,也可以實現(xiàn)自定義的時效性策略。
翻譯成簡單的兩句話:
session_key
時效性由微信控制,開發(fā)者不可預測。
wx.login
可能會導致session_key
過期,可以在使用接口之前用 wx.checkSession
檢查一下。
而對于第二點,我們通過實驗發(fā)現(xiàn),偶發(fā)性的在session_key
已過期的情況下,wx.checkSession
會概率性返回true
社區(qū)也有相關的反饋未得到解決:
小程序解密手機號,隔一小段時間后,checksession:ok,但是解密失敗
wx.checkSession有效,但是解密數(shù)據(jù)失敗
checkSession判斷session_key未失效,但是解密手機號失敗
所以結論是:wx.checkSession
可靠性是不達 100% 的。
基于以上,我們需要對session_key
的過期做一些容錯處理:
發(fā)起需要使用session_key
的請求前,做一次wx.checkSession
操作,如果失敗了刷新登錄態(tài)。
后端使用session_key
解密開放數(shù)據(jù)失敗之后,返回特定錯誤碼(如:DECRYPT_WX_OPEN_DATA_FAIL
),前端刷新登錄態(tài)。
示例代碼:
// 定義檢查 session_key 有效性的操作const ensureSessionKey = async () => { const hasSession = await new Promise(resolve => { wx.checkSession({ success: () => resolve(true), fail: () => resolve(false), }); }); if (!hasSession) { logger.info('sessionKey 已過期,刷新登錄態(tài)'); // 接上面提到的刷新登錄邏輯 return session.refreshLogin(); } return Promise.resolve(); }// 在發(fā)起請求的時候,先做一次確保 session_key 新的操作(以 fly.js 作為網(wǎng)絡請求層為例)const updatePhone = async (params) => { await ensureSessionKey(); const res = await fly.post('https://xxx', params); }// 添加響應攔截器, 監(jiān)聽網(wǎng)絡請求返回fly.interceptors.response.use( (response) => { const code = res.data; // 登錄態(tài)過期或失效 if ( ['DECRYPT_WX_OPEN_DATA_FAIL'].includes(code)) { // 刷新登錄態(tài) await session.refreshLogin(); // 由于加密場景的加密數(shù)據(jù)由用戶點擊產(chǎn)生,session_key 可能已經(jīng)更改,需要用戶重新點擊一遍。 wx.showToast({ title: '網(wǎng)絡出小差了,請稍后重試', icon: 'none' }); } } )
在用戶信息和手機號獲取的方式上,微信是以 的方式,讓用戶主動點擊授權的。
那么為了讓代碼更解耦,我們設計這樣三個組件:
: 包裝點擊交互,通過
支持點擊區(qū)域的自定義UI。
: 與
同理。
: 根據(jù)業(yè)務需要,組合
、
組合來定義不同的授權流程。
以開頭的業(yè)務場景的流程為例,它有這樣的要求:
有多個步驟。
如果中途斷掉了,可以從中間接上。
有些場景中,只要求達到「用戶信息授權」,而不需要完成「用戶手機號」。
那么授權的階段可以分三層:
// 用戶登錄的階段export enum AuthStep { // 階段一:只有登錄態(tài),沒有用戶信息,沒有手機號 ONE = 1, // 階段二:有用戶信息,沒有手機號 TWO = 2, // 階段三:有用戶信息,有手機號 THREE = 3, }復制代碼
AuthStep
的推進過程是不可逆的,我們可以定義一個nextStep
函數(shù)來封裝 AuthStep 更新的邏輯。外部使用的話,只要無腦調(diào)用nextStep
方法,等待回調(diào)結果就行。
示例偽代碼:
// auth-flow componentComponent({ // ... data: { // 默認情況下,只需要到達階段二。 mustAuthStep: AuthStep.TWO }, // 允許臨時更改組件的需要達到的階段。 setMustAuthStep(mustAuthStep: AuthStep) { this.setData({ mustAuthStep }); }, // 根據(jù)用戶當前的信息,計算用戶處在授權的階段 getAuthStep() { let currAuthStep; // 沒有用戶信息,尚在第一步 if (!session.hasUser() || !session.hasUnionId()) { currAuthStep = AuthStepType.ONE; } // 沒有手機號,尚在第二步 if (!session.hasPhone()) { currAuthStep = AuthStepType.TWO; } // 都有,尚在第三步 currAuthStep = AuthStepType.THREE; return currAuthStep; } // 發(fā)起下一步授權,如果都已經(jīng)完成,就直接返回成功。 nextStep(e) { const { mustAuthStep } = this.data; const currAuthStep = this.updateAuthStep(); // 已完成授權 if (currAuthStep >= mustAuthStep || currAuthStep === AuthStepType.THREE) { // 更新全局的授權狀態(tài)機,廣播消息給訂閱者。 return getApp().status.auth.success(); } // 第一步:更新用戶信息 if (currAuthStep === AuthStepType.ONE) { // 已有密文信息,更新用戶信息 if (e) session.updateUser(e); // 更新到視圖層,展示對應UI,等待獲取用戶信息 else this.setData({ currAuthStep }); return; } // 第二步:更新手機信息 if (currAuthStep === AuthStepType.TWO) { // 已有密文信息,更新手機號 if (e) this.bindPhone(e); // 未有密文信息,彈出獲取窗口 else this.setData({ currAuthStep }); return; } console.warn('auth.nextStep 錯誤', { currAuthStep, mustAuthStep }); }, // ...});復制代碼
那么我們的
中就可以根據(jù)currAuthStep
和mustAuthStep
來去做不同的 UI 展示。需要注意的是使用
、
的時候連接上nextStep(e)
函數(shù)。
示例偽代碼:
復制代碼 已完成授權 授權用戶信息 授權手機號
到這里,我們制作好了用來承載授權流程的組件
,那么接下來就是決定要使用它的時機了。
我們梳理需要授權的場景:
點擊某個按鈕,例如:購買某個商品。
對于這種場景,常見的是通過彈窗完成授權,用戶可以選擇關閉。
瀏覽某個頁面,例如:訪問個人中心。
對于這種場景,我們可以在點擊跳轉某個頁面的時候,進行攔截,彈窗處理。但這樣的缺點是,跳轉到目標頁面的地方可能會很多,每個都攔截,難免會錯漏。而且當目標頁面作為「小程序落地頁面」的時候,就避免不了。
這時候,我們可以通過重定向到授權頁面來完成授權流程,完成之后,再回來。
那么我們定義一個枚舉變量:
// 授權的展示形式export enum AuthDisplayMode { // 以彈窗形式 POPUP = 'button', // 以頁面形式 PAGE = 'page', }復制代碼
我們可以設計一個mustAuth
方法,在點擊某個按鈕,或者頁面加載的時候,進行授權控制。
偽代碼示例:
class Session { // ... mustAuth({ mustAuthStep = AuthStepType.TWO, // 需要授權的LEVEL,默認需要獲取用戶資料 popupCompName = 'auth-popup', // 授權彈窗組件的 id mode = AuthDisplayMode.POPUP, // 默認以彈窗模式 } = {}): Promise{ // 如果當前的授權步驟已經(jīng)達標,則返回成功 if (this.currentAuthStep() >= mustAuthStep) return Promise.resolve(); // 嘗試獲取當前頁面的 組件實例 const pages = getCurrentPages(); const curPage = pages[pages.length - 1]; const popupComp = curPage.selectComponent(`#${popupCompName}`); // 組件不存在或者顯示指定頁面,跳轉到授權頁面 if (!popupComp || mode === AuthDisplayMode.PAGE) { const curRoute = curPage.route; // 跳轉到授權頁面,帶上當前頁面路由,授權完成之后,回到當前頁面。 wx.redirectTo({ url: `authPage?backTo=${encodeURIComponent(curRoute)}` }); return Promise.resolve(); } // 設置授權 LEVEL,然后調(diào)用 的 nextStep 方法,進行進一步的授權。 popupComp.setMustAuthStep(mustAuthStep); popupComp.nextStep(); // 等待成功回調(diào)或者失敗回調(diào) return new Promise((resolve, reject) => { const authStatus = getApp().status.auth; authStatus.onceSuccess(resolve); authStatus.onceFail(reject); }); } // ...}復制代碼
那么我們就能在按鈕點擊,或者頁面加載的時候進行授權攔截:
Page({ onLoad() { session.mustAuth().then(() => { // 開始初始化頁面... }); } onClick(e) { session.mustAuth().then(() => { // 開始處理回調(diào)邏輯... }); } })復制代碼
當然,如果項目使用了 TS 的話,或者支持 ES7 Decorator 特性的話,我們可以為mustAuth
提供一個裝飾器版本:
export function mustAuth(option = {}) { return function( _target, _propertyName, descriptor, ) { // 劫持目標方法 const method = descriptor.value; // 重寫目標方法 descriptor.value = function(...args: any[]) { return session.mustAuth(option).then(() => { // 登錄完成之后,重放原來方法 if (method) return method.apply(this, args); }); }; }; }復制代碼
那么使用方式就簡單一些了:
Page({ @mustAuth(); onLoad() { // 開始初始化頁面... } @mustAuth(); onClick(e) { // 開始處理回調(diào)邏輯... } });復制代碼
作為一套可復用的小程序登錄方案,當然需要去定義好前后端的交互協(xié)議。
那么整套登錄流程下來,需要的接口有這么幾個:
靜默登錄 silentLogin
后端利用 code 跟微信客戶端換取用戶標識,然后注冊并登錄用戶,返回自定義登錄態(tài)token
給前端
token
前端會存起來,每個請求都會帶上
userInfo 需要包含nickname
和phone
字段,前端用于計算當前用戶的授權階段。當然這個狀態(tài)的記錄可以放在后端,但是我們認為放在前端,會更加靈活。
token: 自定義登錄態(tài)憑證
userInfo: 用戶信息
code: 產(chǎn)自 wx.login()
入?yún)ⅲ?/p>
出參:
說明:
更新用戶信息 updateUser
后端解密微信開放數(shù)據(jù),獲取隱蔽數(shù)據(jù),如:unionId
等
后端支持更新包括nickname
等用戶基本信息。
前端會把 userInfo 信息更新到session
中,用于計算授權階段。
userInfo:更新后的新用戶信息
nickname: 用戶昵稱
encrypt: 微信開放數(shù)據(jù)相關的iv
,encryptedData
以及其他如性別地址等非必要字段
入?yún)ⅲ?/p>
出參:
說明:
更新用戶手機號 updatePhone
后端解密開放式局,獲取手機號,并更新到用戶信息中。
前端會把 userInfo 信息更新到session
中,用于計算授權階段。
userInfo:更新后的新用戶信息
encrypt:微信開放數(shù)據(jù)相關的iv
,encryptedData
入?yún)ⅲ?/p>
出參:
說明:
解綁手機號 unbindPhone
入?yún)ⅲ?
出參:-
說明:后端解綁用戶手機號,成功與否,走業(yè)務定義的前后端協(xié)議。
登錄 logout
入?yún)ⅲ?
出參:-
說明:后端主動過期登錄態(tài),成功與否,走業(yè)務定義的前后端協(xié)議。
最后我們來梳理一下整體的「登錄服務」的架構圖:
由「登錄服務」和「底層建設」組合提供的通用服務,業(yè)務層只需要去根據(jù)產(chǎn)品需求,定制授權的流程
,就能滿足大部分場景了。
本篇文章通過一些常見的登錄授權場景來展開來描述細節(jié)點。
整理了「登錄」、「授權」的概念。
然后分別針對「登錄」介紹了一些關鍵的技術實現(xiàn):
靜默登錄
靜默登錄異步狀態(tài)的處理
自定義登錄態(tài)過期的容錯處理
微信session_key
過期的容錯處理
而對于「授權」,會有設計UI部分的邏輯,還需要涉及到組件的拆分:
組件拆分與設計
權限攔截的處理
然后,梳理了這套登錄授權方案所依賴的后端接口,和給出最簡單的參考協(xié)議。
最后,站在「秉著沉淀一套通用的小程序登錄方案和服務為目標」的角度,梳理了一下架構層面上的分層。
業(yè)務定制層
登錄服務層
底層建設
以上是“微信小程序登錄前端設計與實現(xiàn)的示例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學習更多知識,歡迎關注創(chuàng)新互聯(lián)行業(yè)資訊頻道!