微信小程序靜默登錄和維護(hù)自定義登錄態(tài)詳解
1.背景
在小程序中,openid是一個(gè)用戶(hù)對(duì)于一個(gè)小程序/公眾號(hào)的標(biāo)識(shí),開(kāi)發(fā)者可以通過(guò)這個(gè)標(biāo)識(shí)識(shí)別出用戶(hù),就如同你的身份證一樣。
2.什么是靜默登錄?
在普通的應(yīng)用中,用戶(hù)通過(guò)表單驗(yàn)證登錄建立用戶(hù)體系,這種常見(jiàn)的登錄方式一般是通過(guò)登錄頁(yè)面表單進(jìn)行登錄,對(duì)用戶(hù)來(lái)說(shuō)是有感的。
在小程序中,由于是基于微信,可以通過(guò)微信官方提供的API能力,使我們能夠無(wú)感知得獲取用戶(hù)身份標(biāo)識(shí)(openid),快速建立小程序內(nèi)的用戶(hù)體系,對(duì)用戶(hù)來(lái)說(shuō)是無(wú)感知的,因此是由程序來(lái)完成這個(gè)自動(dòng)的登陸過(guò)程。
2.1登錄流程時(shí)序
下圖取自微信官方
小程序端調(diào)用wx.login(),獲取code并且上傳到服務(wù)器
export async function doLogin() { if (isLogin) return false isLogin = true removeCache('token') const { code } = await wxp.login() const data = await login({ code }) setCache('token', data.data.token) isLogin = false return true } 服務(wù)端拿到code,調(diào)用auth.code2Session接口換取openid const getOpenid = async function (appid, secret, code) { const resData = await axios.get('https://api.weixin.qq.com/sns/jscode2session?appid=' + appid + '&secret=' + secret + '&js_code=' + code + '&grant_type=authorization_code'); return resData.data; }
總結(jié)流程:
- 小程序端調(diào)用wx.login(),獲取code并且上傳到服務(wù)器
- 服務(wù)器根據(jù)code,并且調(diào)用微信auth.code2Session接口換取openid
- 后臺(tái)服務(wù)器根據(jù)openid生成自定義token返回前端并且存儲(chǔ)起來(lái),后續(xù)業(yè)務(wù)邏輯用token來(lái)識(shí)別用戶(hù)身份
3.如何維護(hù)自定義登錄態(tài)
讓我們來(lái)看下官方的處理方式:
wx.checkSession({ success () { //session_key 未過(guò)期,并且在本生命周期一直有效 }, fail () { // session_key 已經(jīng)失效,需要重新執(zhí)行登錄流程 wx.login() //重新登錄 } })
由圖中我們可以知道,真正決定登錄態(tài)的是微信的checkSession接口。因此每次檢查用戶(hù)登錄態(tài)是否有效就先調(diào)用一個(gè)checkSession接口,如果session_key失效,再發(fā)起登錄流程。
4.靜默登錄整體流程
4.1app.onLaunch中發(fā)起登錄
由于大部分的接口調(diào)用都需要token驗(yàn)證,因此在小程序啟動(dòng)的周期函數(shù)app.onLaunch中發(fā)起靜默登錄最為合適不過(guò)了。
4.2處理小程序不支持異步阻塞
由于小程序的啟動(dòng)流程中,頁(yè)面級(jí)和組件級(jí)的生命周期函數(shù)都不支持異步阻塞;因此會(huì)造成一個(gè)情況,app.onLaunch中發(fā)起的wx.login還沒(méi)有成功的時(shí)候,頁(yè)面級(jí)的生命周期函數(shù)已經(jīng)向服務(wù)器發(fā)起請(qǐng)求。由于我們的接口設(shè)計(jì)大部分都是需要驗(yàn)證的,此時(shí)登錄還未成功,token也還沒(méi)有正確返回,因此頁(yè)面級(jí)的生命周期發(fā)起的數(shù)據(jù)獲取接口肯定是會(huì)報(bào)錯(cuò)的(例如返回了401)
4.2.1粗糙的方案
采用回調(diào)函數(shù)的方式
//app.js this.globalData.wxp.showLoading({ title: '登錄中...' }); await login(); this.globalData.hasLogin = true; if (this.checkLoginReadyCallback) { this.checkLoginReadyCallback(); } this.globalData.wxp.hideLoading(); 頁(yè)面的生命周期中 async onLoad() { if (app.globalData.hasLogin) { //如果已經(jīng)登錄了直接獲取數(shù)據(jù) this.getUserInfo(); this.getEvent(); } else { //未登錄定義下回調(diào)函數(shù),等app.js登錄成功之后進(jìn)行調(diào)用 app.checkLoginReadyCallback = async () => { this.getUserInfo(); this.getEvent(); }; } },
優(yōu)點(diǎn):簡(jiǎn)單粗暴
缺點(diǎn):代碼結(jié)構(gòu)差;如果是多個(gè)頁(yè)面為啟動(dòng)頁(yè),則需要多個(gè)頁(yè)面都定義回調(diào)函數(shù)(假設(shè)使用了小程序onShare模式)
4.2.2優(yōu)雅的方式
借助fly.js庫(kù),實(shí)現(xiàn)對(duì)請(qǐng)求進(jìn)行上鎖機(jī)制。流程:app.js中發(fā)起登錄,同時(shí)頁(yè)面中也會(huì)發(fā)起請(qǐng)求。在請(qǐng)求攔截器中判斷請(qǐng)求的接口是否為白名單(不需要token驗(yàn)證的接口)接口和token是否存在;如果都為false,鎖住當(dāng)前請(qǐng)求進(jìn)入請(qǐng)求隊(duì)列,執(zhí)行登錄流程。等待登錄流程成功之后解鎖請(qǐng)求隊(duì)列,繼續(xù)發(fā)起頁(yè)面級(jí)的請(qǐng)求任務(wù)。如下為請(qǐng)求攔截器中的代碼:
//攔截處理 fly.interceptors.request.use(async (request) => { //沒(méi)有token且請(qǐng)求不是白名單的都鎖住 if ( !getCache('token') && !whiteList.some((item) => request.url.startsWith(item)) ) { fly.lock() //去登陸 成功之后再u(mài)nlock await doLogin() fly.unlock() //解鎖后,會(huì)繼續(xù)發(fā)起請(qǐng)求隊(duì)列中的任務(wù) } if (getCache('token') && !fly.config.headers['Authorization']) { request.headers['Authorization'] = getCache('token') } request.headers['Content-Type'] = 'application/x-www-form-urlencoded' return request })
當(dāng)然,自定義登錄態(tài)也會(huì)存在過(guò)期的情況,我們可以在響應(yīng)攔截器中捕獲出錯(cuò)進(jìn)行處理:當(dāng)檢測(cè)到401token過(guò)期代碼時(shí),需要把請(qǐng)求隊(duì)列后面的請(qǐng)求都鎖死,防止多次出現(xiàn)401自定義登錄態(tài)過(guò)期的情況,然后發(fā)起登錄,登陸成功之后再進(jìn)行解鎖行為,觸發(fā)后續(xù)的請(qǐng)求隊(duì)列執(zhí)行,并且重新執(zhí)行本次由于token過(guò)期被服務(wù)器拒絕的接口,否則會(huì)造成請(qǐng)求失敗的情況(由于靜默登錄是用戶(hù)無(wú)感知的,突然出現(xiàn)身份驗(yàn)證信息過(guò)期會(huì)使用戶(hù)感覺(jué)到特別地奇怪,因此需要重新執(zhí)行本次請(qǐng)求操作而不是由用戶(hù)再次點(diǎn)擊或者其他的行為再發(fā)起):
// 響應(yīng)攔截 fly.interceptors.response.use( (response) => { //只將請(qǐng)求結(jié)果的data字段返回 return response.data }, async (err) => { if (err.status === 401) { //401之后,把后面的請(qǐng)求都鎖死 防止再次401 fly.lock() removeCache('token') //去登陸 成功之后再u(mài)nlock const isLoginSuccess = await doLogin() if (isLoginSuccess) { fly.unlock() } //新執(zhí)行本次由于token過(guò)期被服務(wù)器拒絕的接口 return fly.request(err.request) } } )
由于請(qǐng)求有可能是并發(fā)的,為了防止登錄被多次執(zhí)行,因此對(duì)doLogin函數(shù)進(jìn)行了小小的改造(盡管寫(xiě)得很不優(yōu)雅,但是能力有限,大佬們賜教了):
export async function doLogin() { //如果正在登錄中則不執(zhí)行 if (isLogin) return false isLogin = true //修改狀態(tài)為登錄中,反正重復(fù)多次登錄 removeCache('token') const { code } = await wxp.login() const data = await login({ code }) setCache('token', data.data.token) isLogin = false return true }
4.3 整體流程圖
5.寫(xiě)在最后
細(xì)節(jié)的讀者即可發(fā)現(xiàn),api請(qǐng)求中并未設(shè)置最大請(qǐng)求數(shù)量的(微信小程序最大支持五個(gè)api同時(shí)發(fā)起),這點(diǎn)是需要補(bǔ)充進(jìn)來(lái)的??傮w寫(xiě)下來(lái)作者覺(jué)得在實(shí)現(xiàn)方式上還有進(jìn)步的空間,作者能力有限,也是一邊學(xué)習(xí)一邊探討!
到此這篇關(guān)于微信小程序靜默登錄和維護(hù)自定義登錄態(tài)的文章就介紹到這了,更多相關(guān)微信小程序靜默登錄內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JavaScript實(shí)現(xiàn)谷歌瀏覽器插件開(kāi)發(fā)的方法詳解
對(duì)于瀏覽器插件相信大家都不陌生,誰(shuí)的瀏覽器不裝幾個(gè)好用的插件呢,更是有油猴這個(gè)強(qiáng)大的神器。所以本文就來(lái)用JavaScript開(kāi)發(fā)一個(gè)谷歌瀏覽器插件,感興趣的小伙伴可以了解一下2022-11-11Iphone手機(jī)、安卓手機(jī)瀏覽器控制默認(rèn)縮放大小的方法總結(jié)(附代碼)
這篇文章主要介紹了Iphone手機(jī)、安卓手機(jī)瀏覽器控制默認(rèn)縮放大小的方法,通過(guò)meta標(biāo)簽中添加屬性代碼展示控制瀏覽器的默認(rèn)縮放,具體操作步驟大家可查看下文的詳細(xì)講解,感興趣的小伙伴們可以參考一下。2017-08-08JavaScript中函數(shù)的四種調(diào)用方式總結(jié)
這篇文章主要為大家詳細(xì)介紹了JavaScript中函數(shù)的四種調(diào)用方式,文中的示例代碼講解詳細(xì),對(duì)我們深入掌握J(rèn)avaScript有一定的幫助,需要的可以參考下2023-10-10javaScript日期工具類(lèi)DateUtils詳解
這篇文章主要為大家詳細(xì)介紹了javaScript日期工具類(lèi)DateUtils,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12JavaScript中 ES6 generator數(shù)據(jù)類(lèi)型詳解
generator 是ES6引入的新的數(shù)據(jù)類(lèi)型,由function* 定義, (注意*號(hào)),接下來(lái)通過(guò)本文給大家介紹js中 ES6 generator數(shù)據(jù)類(lèi)型,非常不錯(cuò),感興趣的朋友一起學(xué)習(xí)吧2016-08-08JXTree對(duì)象,讀取外部xml文件數(shù)據(jù),生成樹(shù)的函數(shù)
JXTree對(duì)象,讀取外部xml文件數(shù)據(jù),生成樹(shù)的函數(shù)...2007-04-04