最近發現小程式的登入邏輯還有一些新的心得,所以記錄一下。
一、官方微信小程式登入流程個人理解
在小程式官網裡面會提到一個小程式的登入邏輯,這是官方推薦的登入邏輯,也就是所謂的小程式登入態維護邏輯,這裡是官方的圖:
官方邏輯的個人理解:
- 使用者開啟微信小程式。
- 也可以是在需要的時候處理這個邏輯,不一定是開啟小程式的時候執行這個邏輯。
- 具體看業務需要,但是往往很多業務設計都是要使用者開啟小程式的時候,來執行這個邏輯的。
- 小程式通過
wx.login
獲取微信的 code,然後將這個 code 傳送給開發者伺服器(我們自己的開發伺服器)。 - 開發者伺服器接收到code 之後,會進行封裝處理,通過
code2Session
這個api 介面來獲取真正需要的微信使用者的登入態session_key
和openid
和unionid
。- 準確來說
session_key
才是真正的微信登入態資訊,但是把openid
和unionid
加起來一起理解,也可以籠統地理解為這些都是微信的登入態資訊。
- 準確來說
- 然後需要開發者伺服器自己生成一個自定義的登入態(例如業務 token或者 session)來儲存這些微信伺服器返回來的微信登入態相關資訊(
session_key
和openid
和unionid
),並且做關聯處理,然後返回給小程式客戶端。- 關聯處理就是你的自定義登入態和微信的登入態相關聯,這樣的話就不需要維護多個登入態,只需要維護一個就可以了。
- 關聯處理之後需要將這個自定義登入態資訊儲存起來,可以放到資料庫或者本地檔案或者 例如 redis 之類的快取服務裡面,以便方便後續使用,而不需要每次都請求微信伺服器(微信伺服器對這個請求的頻率是有限制的)。
- 注意這裡不返回微信登入態相關資訊,只返回自定義的登入態資訊。
- 自定義登入態的資訊不僅可以包含 token,也可以包含一些使用者許可權資訊,或者其他資訊,因為是自定義的登入態,維護也是很自定義的。
- 一般自定義的登入態的超時時長需要比微信的登入態要長。
- 小程式客戶端接收到返回的自定義登入態資訊,從而判斷使用者是否登入成功,登入成功的話,就將自定義登入態資訊儲存到本地的儲存。
- 本地的儲存可以是微信小程式提供的
app.globaldata
,也可以是localstoage
,注意,小程式不支援cookie
。 - 儲存到本地儲存的好處就是,後續使用的這個自定義登入態就不需要再次跟伺服器進行互動來獲取了,只需要呼叫本地儲存就行了,這裡是為了優化效能和避免浪費資源。
- 本地的儲存可以是微信小程式提供的
- 小程式客戶端訪問業務介面的時候,攜帶之前儲存到本地儲存的自定義登入態資訊進行對開發者伺服器(業務介面伺服器)訪問。
- 開發者伺服器的業務介面接收到請求,並且請求裡面攜帶了自定義的登入態,通過校驗之後,會返回相關資訊。
- 校驗是將小程式客戶端攜帶過來的自定義登入態和開發者伺服器快取起來的自定義登入態進行對比,會去確認是否和使用者的
openid
或者unionid
和session_key
相匹配。 - 如果匹配,就可以馬上返回相關資訊。
- 如果不匹配,就可以馬上返回相關資訊,告知小程式客戶端無法訪問業務介面。
- 如果匹配結果是自定義登入態超時了,就可以馬上返回相關資訊,告知小程式客戶端需要重新執行登入邏輯
- 如果是匹配結果是自定義登入態沒有超時,但是微信登入態超時了,那麼就會開發者伺服器就會再次發起
code2Session
進行微信登入態更新。
- 校驗是將小程式客戶端攜帶過來的自定義登入態和開發者伺服器快取起來的自定義登入態進行對比,會去確認是否和使用者的
整個微信小程式官方推薦的登入態維護流程就是這樣了,官方推薦使用自定義登入態來進行整個微信小程式的登入維護,既然寄人籬下,那麼就要按部就班,跟著政策走,這是最好的應對。
上面的圖裡面的一些術語解釋:
-
code
是微信使用者的登入憑證,開啟小程式登入的時候獲取的只屬於微信這個使用者的登入憑證,需要注意的是,這個登入憑證只供微信小程式使用的。 -
這個
code
的存活時間一般是5分鐘左右,這是一個臨時的登入憑證,他的最大作用就是確定是來源自哪一個微信使用者來開啟,是為了後續生成一個微信登入態session_key
而使用的。 -
session_key
是微信使用者在小程式裡面的登入態資訊,這是微信給這個使用者頒發的一個登入session
。- 他的返回格式
{"session_key",openid":"..."}
,官方說需要定期使用wx.checkSession
檢測,但是在實際的場景裡面其實也可以不用,用和不用主要看你怎麼設計你的微信小程式,這個後續會說。 - 早起官方是返回
expire_time
過期時間的,但是後面取消了,這裡有一個比較新的官方回覆:使用者越頻繁使用小程式,session_key有效期越長,…… | 微信開放社群,有效期是3天,但是這個不一定是固定的,具體看業務需求,總的原則就是維護一個自定義登入態,自定義登入需要和微信登入態關聯。
- 他的返回格式
openId
,使用者在微信裡面的唯一標識,但是需要跟unionid
進行一起理解。unioinId
,如果開發者擁有多個移動應用、網站應用、和公眾帳號(包括小程式),可通過unionid
來區分使用者的唯一性,因為只要是同一個微信開放平臺帳號下的移動應用、網站應用和公眾帳號(包括小程式),使用者的unionid
是唯一的。換句話說,同一使用者,對同一個微信開放平臺下的不同應用,unionid
是相同的。- 一般來說,
openId
就是微信使用者的唯一標識,但是因為微信產品很多,所以會出現多個微信產品使用不同的openId
來標識使用者,但是對於我們做業務接入的話,就買辦法使用了,所以建議是統一使用unioinid
,因為一般來說,一般的業務都會有公眾號,小程式聯合使用的,所以unionid
使用頻率較高。
- 一般來說,
3rd_session
是一般是指開發者伺服器的登入態,也就是自定義登入態,也就是我們自己公司的業務伺服器的登入態(微信官方推薦使用自定義登入態來管理整個微信小程式登入)。- 當小程式登入態過期了,自定義登入態沒過期的時候,那麼就需要在小程式開啟的時候先執行一次
wx.checkSession
來檢查,如果過期了,就本地執行登入操作,再讓開發者伺服器跟微信伺服器互動,獲取新的小程式登入態,然後關聯到自定義登入態。 - 當小程式登入態沒過期,自定義登入態過期了的時候,那麼小程式客戶端訪問業務介面的時候,業務介面會告訴小程式客戶端,你的自定義登入態超時了,然後小程式客戶端會重新執行登入邏輯,然後通知開發者服務重新生成新的自定義登入態,然後關聯之前還在使用的小程式登入態。
- 當二者都同時過期的時候,那就肯定要發起完整的重新登入了。
- 這樣的好處是自定義登入態不需要重複建立,也能跟小程式登入態一起維護管理,達到資源合理利用的效果。
- 當小程式登入態過期了,自定義登入態沒過期的時候,那麼就需要在小程式開啟的時候先執行一次
- 小程式的登入態可以使用微信返回的
session_key
作為代表理解。 - 自定義登入態是開發者伺服器建立的,也是業務的登入態,一般是指我們業務自己的
session
會話,官方說的3rd_session
就是這個。 - 一般自定義登入態的管理都會使用類似 redis 之類的東西來進行管理的,這裡也涉及到一個自定義登入態的快取策略,快取起來,在一定時間內不需要重新建立自定義登入態,達到優化效能的效果。
只有理解好官方的標準流程,才能更好地理解其他流程,或者其他人對官方流程的封裝和註解!
二、擴充套件微信小程式登入流程
上面是基本的登入流程理解,但是實際業務中還是會有一些地方需要補充的,但是我們理解的時候最好把他們分開,這樣會更清晰和簡單。
按照官方推薦的方式是使用自定義登入態來管理整個微信小程式登入流程的,雖然說是通過一個自定義登入態來管理,但是其實裡面是有變通的。
這裡有2種方式來做:
-
方式一:小程式開啟的時候先檢查小程式本地是否有儲存的自定義登入態,
-
如果沒有,則代表是首次登入,要自動執行完整的登入流程,
-
如果有,則需要判斷這個自定義登入態是否過期,這裡判斷的方式有2種,一種是開發者伺服器提供一個介面來檢查,一種是在這個自定義登入態資料裡面加上過期時間,判斷是否過期。
- 過期,則自動發起完整的登入流程。
- 不過期,就繼續使用本地儲存的自定義登入態。
-
-
方式二:小程式開啟的時候先發起
wx.checkSession
檢查微信登入態是否過期:- 如果過期,則發起完整的登入流程。
- 如果不過期,則繼續使用本地儲存的自定義登入態。(如果本地的自定義登入態沒有的話,那麼也是要強制發起完整的登入流程的)
上面說的方式都是開啟小程式的時候做的,但是也要考慮到一種情況,就是自定義登入態在使用者使用小程式的過程中過期了,那麼這時候也是需要強制執行完整的登入流程的。
相對來說方式二比較好,方式二的好處是不需要小程式服務端來參與校驗,而是在小程式端呼叫API。這種方式實際上是將維護登入態的機制委託給了微信維護的session_key,開發者不需要在自定義的登入態中儲存有效期資訊。騰訊雲開發的wafer專案便採取了這種方法。
三、繼續擴充套件微信小程式登入流程
上面提到的方式二是在開啟小程式的時候做的,這裡是在每次發起 HTTP 請求的時候做的,這個是不一樣的邏輯。
當小程式客戶端訪問介面的時候,開發者伺服器返回說你的自定義登入態過期了,你就需要重新發起完整的登入,但這裡有2個點是需要注意的:
- 由於 HTTP 是無狀態的,所以不能跟蹤狀態,發起 HTTP 請求然後伺服器返回自定義登入態過期的結果,然後就完結了,沒辦法暫存起原來的 HTTP 請求,以便當我們有可用的自定義登入態的時候,繼續使用。所以我們需要在發起 HTTP 請求的時候做判斷,看是否需要發起完整的登入流程。
- 在小程式裡面,經常會有訪問開發者伺服器的情況,但是如果每個介面都需要這樣直接判斷的話,就會產生很多冗餘程式碼,而且沒辦法維護,萬一登入流程改變了,就要全部程式碼改一遍了。
所以這裡就需要進行封裝一下,Random Notes這裡就提到了,思路是正確的,不過實現的話可以按自己需要實現,他這裡有2個地方描述得很好,所以我搬過來借鑑了。
這是一個巨集觀的,每個請求都要檢查一下登入態的邏輯圖:
圖片引用自:blog.imtouch.info
每個請求都會檢查一次登入態,不管是自定義登入態還是微信登入態,反正只要發起跟開發者伺服器互動的 HTTP 請求的之前都會檢查。
細分邏輯的話就會變成這樣:
圖片引用自:blog.imtouch.info
細分邏輯之後看起來會更加清晰,並且這裡的邏輯是微信登入態和自定義登入態都會檢查一遍,任何一個登入態過期都會發起完整的登入邏輯,其實我覺得這裡也可以只判斷自定義登入態的狀態也可以的。
不過由此也帶來一個問題,就是如果一個頁面裡面有多個請求同時發起的話,那麼就會出現同時檢查到登入態過期,然後同時發起登入的情況,這個問題可能會導致開發者伺服器的校驗登入資料出現問題,他這裡用到 promise.race
來解決,具體可以看他的文章裡面介紹,我後面也會繼續寫一下這個處理的介紹。
參考文件: