1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        小程序靜默登錄方案設(shè)計(jì)

        共 12081字,需瀏覽 25分鐘

         ·

        2021-05-08 21:18

        作者 | 蔡小真
        鏈接 | https://juejin.cn/post/6933082931653148680

        1. 背景

        首先談?wù)勗谛〕绦虻拈_發(fā)中,如何借助微信的能力標(biāo)識(shí)一個(gè)用戶?

        微信官方提供了兩種標(biāo)識(shí):

        1. OpenId 是一個(gè)用戶對(duì)于一個(gè)小程序/公眾號(hào)的標(biāo)識(shí),開發(fā)者可以通過這個(gè)標(biāo)識(shí)識(shí)別出用戶。
        2. UnionId 是一個(gè)用戶對(duì)于同主體微信小程序/公眾號(hào)/APP 的標(biāo)識(shí),開發(fā)者需要在微信開放平臺(tái)下綁定相同賬號(hào)的主體。開發(fā)者可通過UnionId,實(shí)現(xiàn)多個(gè)小程序、公眾號(hào)、甚至 APP 之間的數(shù)據(jù)互通。

        同一個(gè)用戶的這兩個(gè) ID 對(duì)于同一個(gè)小程序來說是永久不變的,就算用戶刪了小程序,下次用戶進(jìn)入小程序,開發(fā)者依舊可以通過后臺(tái)的記錄標(biāo)識(shí)出來。那么如何獲取OpenIdUnionId呢?

        早期(2018 年 4 月之前)的小程序設(shè)計(jì)使用 wx.getUserInfo 接口,來獲取用戶信息。設(shè)計(jì)這個(gè)接口的初衷是希望開發(fā)者在真正需要用戶信息(如頭像、昵稱、手機(jī)號(hào)等)的情況下才去調(diào)取這個(gè)接口。但很多開發(fā)者為了拿到UnionId,會(huì)在小程序啟動(dòng)時(shí)直接調(diào)用這個(gè)接口,導(dǎo)致用戶在使用小程序的時(shí)候產(chǎn)生困擾,歸結(jié)起來有幾點(diǎn):

        1. 開發(fā)者在小程序首頁(yè)直接調(diào)用 wx.getUserInfo 進(jìn)行授權(quán),彈框獲取用戶信息,會(huì)使得一部分用戶點(diǎn)擊“拒絕”按鈕。
        2. 在開發(fā)者沒有處理用戶拒絕彈框的情況下,用戶必須授權(quán)頭像昵稱等信息才能繼續(xù)使用小程序,會(huì)導(dǎo)致某些用戶放棄使用該小程序。
        3. 用戶沒有很好的方式重新授權(quán),盡管微信官方增加了設(shè)置頁(yè)面,可以讓用戶選擇重新授權(quán),但很多用戶并不知道可以這么操作。

        微信官方也意識(shí)到了這個(gè)問題,針對(duì)獲取用戶信息更新了三個(gè)能力:

        1. 使用組件來獲取用戶信息。
        2. 若用戶滿足一定條件,則可以用wx.login 獲取到的 code 直接換到unionId
        3. wx.getUserInfo 不需要依賴 wx.login 就能調(diào)用得到數(shù)據(jù)。

        本文主要講述的是第二點(diǎn)能力,微信官方鼓勵(lì)開發(fā)者在不騷擾用戶的情況下合理獲得unionid,而僅在必要時(shí)才向用戶彈窗申請(qǐng)使用昵稱頭像,從而衍生出「靜默登錄」和「用戶登錄」兩種概念。

        2. 什么是靜默登錄?

        小程序可以通過微信官方提供的登錄能力方便地獲取微信提供的用戶身份標(biāo)識(shí),快速建立小程序內(nèi)的用戶體系。

        很多開發(fā)者會(huì)把 wx.loginwx.getUserInfo 捆綁調(diào)用當(dāng)成登錄使用,其實(shí) wx.login 已經(jīng)可以完成登錄,wx.getUserInfo 只是獲取額外的用戶信息。

        wx.login 獲取到 code 后,會(huì)發(fā)送到開發(fā)者后端,開發(fā)者后端通過接口去微信后端換取到 openidsessionKey(現(xiàn)在會(huì)將 unionid 也一并返回)后,把自定義登錄態(tài) 3rd_session(本業(yè)務(wù)命名為auth-token) 返回給前端,就已經(jīng)完成登錄行為了。wx.login 行為是靜默,不必授權(quán)的,用戶不會(huì)察覺。

        wx.getUserInfo 只是為了提供更優(yōu)質(zhì)的服務(wù)而存在,比如獲取用戶的手機(jī)號(hào)注冊(cè)會(huì)員,或者展示頭像昵稱,判斷性別,開發(fā)者可通過 unionId 和其他公眾號(hào)上已有的用戶畫像結(jié)合來提供歷史數(shù)據(jù)。因此開發(fā)者不必在用戶剛剛進(jìn)入小程序的時(shí)候就強(qiáng)制要求授權(quán)。

        2.1 靜默登錄流程時(shí)序

        官方給出了 wx.login 的最佳實(shí)踐如下:

        靜默登錄英文簡(jiǎn)稱為silentLogin,代碼如下所示:

          private async silentLogin(): Promise<void> {
            try {
              this.status.silentLogin.ing();

              // 獲取臨時(shí)登錄憑證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é)為以下三步:

        1. 小程序端調(diào)用 wx.login() 獲取 臨時(shí)登錄憑證code ,并回傳到開發(fā)者服務(wù)器。
        2. 服務(wù)器端調(diào)用 auth.code2Session 接口,換取 用戶唯一標(biāo)識(shí) OpenID會(huì)話密鑰 session_key。
        3. 開發(fā)者服務(wù)器可以根據(jù)用戶標(biāo)識(shí)來生成自定義登錄態(tài)(例如:auth-token),用于后續(xù)業(yè)務(wù)邏輯中前后端交互時(shí)識(shí)別用戶身份。

        2.2 開發(fā)者后臺(tái)校驗(yàn)與解密開放數(shù)據(jù)

        靜默登錄成功后,微信服務(wù)器端會(huì)下發(fā)一個(gè)session_key給服務(wù)端,而這個(gè)會(huì)在需要獲取微信開放數(shù)據(jù)的時(shí)候會(huì)用到。

        為了確保開放接口返回用戶數(shù)據(jù)的安全性,微信會(huì)對(duì)明文數(shù)據(jù)進(jìn)行簽名。開發(fā)者可以根據(jù)業(yè)務(wù)需要對(duì)數(shù)據(jù)包進(jìn)行簽名校驗(yàn),確保數(shù)據(jù)的完整性。

        1. 小程序通過調(diào)用接口(如 wx.getUserInfo)獲取數(shù)據(jù)時(shí),如果用戶已經(jīng)授權(quán),接口會(huì)同時(shí)返回以下幾個(gè)字段。如用戶未授權(quán),會(huì)先彈出用戶彈窗,用戶點(diǎn)擊同意授權(quán),接口會(huì)同時(shí)返回以下幾個(gè)字段。相反如果用戶拒絕授權(quán),將調(diào)用失敗。
        屬性類型說明
        userInfoUserInfo用戶信息對(duì)象,不包含 openid 等敏感信息
        rawDatastring不包括敏感信息的原始數(shù)據(jù)字符串,用于計(jì)算簽名
        signaturestring使用 sha1( rawData + sessionkey ) 得到字符串,用于校驗(yàn)用戶信息
        encryptedDatastring包括敏感數(shù)據(jù)在內(nèi)的完整用戶信息的加密數(shù)據(jù)
        ivstring加密算法的初始向量
        cloudIDstring敏感數(shù)據(jù)對(duì)應(yīng)的云 ID,開通云開發(fā)的小程序才會(huì)返回,可通過云調(diào)用直接獲取開放數(shù)據(jù)
        1. 開發(fā)者將 signature、rawData 發(fā)送到開發(fā)者服務(wù)器進(jìn)行校驗(yàn)。服務(wù)器利用用戶對(duì)應(yīng)的 session_key 使用相同的算法計(jì)算出簽名 signature2 ,比對(duì) signaturesignature2 即可校驗(yàn)數(shù)據(jù)的完整性。開發(fā)者服務(wù)器告訴前端開發(fā)者數(shù)據(jù)可信,即可安全使用用戶信息數(shù)據(jù)。
        2. 如果開發(fā)者想要獲取敏感數(shù)據(jù)(如 openid,unionID),則將encryptedDataiv發(fā)送到開發(fā)者服務(wù)器,由服務(wù)器使用session_key(對(duì)稱解密密鑰)進(jìn)行對(duì)稱解密,獲取敏感數(shù)據(jù)進(jìn)行存儲(chǔ)并返回給前端開發(fā)者。

        注意: 因?yàn)樾枰脩糁鲃?dòng)觸發(fā)才能發(fā)起獲取手機(jī)號(hào)接口,所以該功能不由 API 來調(diào)用(即上述提到的wx.getUserInfo是無法獲取手機(jī)號(hào)的),需用 button 組件的點(diǎn)擊來觸發(fā)。獲得encryptedDataiv,同樣發(fā)送給開發(fā)者服務(wù)器,由服務(wù)器使用session_key(對(duì)稱解密密鑰)進(jìn)行對(duì)稱解密,獲得對(duì)應(yīng)的手機(jī)號(hào)。

        需要關(guān)注的是,2021 年 2 月 23 日,微信團(tuán)隊(duì)發(fā)布了《小程序登錄、用戶信息相關(guān)接口調(diào)整說明》,進(jìn)行了如下調(diào)整:

        1. 2021 年 2 月 23 日起,通過wx.login接口獲取的登錄憑證可直接換取unionID。
        2. 2021 年 4 月 13 日后發(fā)布新版本的小程序,無法通過wx.getUserInfo接口獲取用戶個(gè)人信息(頭像、昵稱、性別與地區(qū)),將直接獲取匿名數(shù)據(jù)。getUserInfo接口獲取加密后的openIDunionID數(shù)據(jù)的能力不做調(diào)整。
        3. 新增getUserProfile接口(基礎(chǔ)庫(kù) 2.10.4 版本開始支持),可獲取用戶頭像、昵稱、性別及地區(qū)信息,開發(fā)者每次通過該接口獲取用戶個(gè)人信息均需用戶確認(rèn)。

        即開發(fā)者通過組件調(diào)用wx.getUserInfo將不再?gòu)棾鰪棿?,直接返回匿名的用戶個(gè)人信息。如果要獲取用戶頭像、昵稱、性別及地區(qū)信息,需要改造wx.getUserProfile接口。

        2.3 session_key 的有效期

        開發(fā)者如果遇到因?yàn)?session_key 不正確而校驗(yàn)簽名失敗或解密失敗,請(qǐng)關(guān)注下面幾個(gè)與 session_key 有關(guān)的注意事項(xiàng)。

        1. wx.login 調(diào)用時(shí),用戶的 session_key 可能會(huì)被更新而致使舊 session_key 失效(刷新機(jī)制存在最短周期,如果同一個(gè)用戶短時(shí)間內(nèi)多次調(diào)用 wx.login,并非每次調(diào)用都導(dǎo)致 session_key 刷新)。開發(fā)者應(yīng)該在明確需要重新登錄時(shí)才調(diào)用 wx.login,及時(shí)通過 auth.code2Session 接口更新服務(wù)器存儲(chǔ)的 session_key。
        2. 微信不會(huì)把 session_key 的有效期告知開發(fā)者。我們會(huì)根據(jù)用戶使用小程序的行為對(duì) session_key 進(jìn)行續(xù)期。用戶越頻繁使用小程序,session_key 有效期越長(zhǎng)。
        3. 開發(fā)者在 session_key 失效時(shí),可以通過重新執(zhí)行登錄流程獲取有效的 session_key。使用接口wx.checkSession可以校驗(yàn) session_key 是否有效,從而避免小程序反復(fù)執(zhí)行登錄流程。
        4. 當(dāng)開發(fā)者在實(shí)現(xiàn)自定義登錄態(tài)時(shí),可以考慮以 session_key 有效期作為自身登錄態(tài)有效期,也可以實(shí)現(xiàn)自定義的時(shí)效性策略。

        3 「登錄」架構(gòu)

        用戶登錄架構(gòu)

        「登錄」方案架構(gòu)如上圖所示,將所有登錄相關(guān)功能抽象到 「service 層」(本項(xiàng)目將其命名為session),供 「業(yè)務(wù)層」 調(diào)用。本文主要講述灰色內(nèi)容,其它模塊將在下一篇文章《小程序用戶登錄設(shè)計(jì)》中闡述。

        3.1 libs - 提供登錄相關(guān)的類方法供「業(yè)務(wù)層」調(diào)用

        1. 封裝session類,提供類方法供「業(yè)務(wù)層」調(diào)用。主要有以下幾種方法:
        方法名功能使用場(chǎng)景
        silentLogin發(fā)起靜默登錄-
        login登錄,silentLogin 方法的一層封裝用于小程序啟動(dòng)時(shí)發(fā)起靜默登錄
        refreshLogin刷新登錄態(tài),silentLogin 方法的一層封裝用于登錄態(tài)過期時(shí)發(fā)起靜默登錄
        ensureSessionKey驗(yàn)證 sessionKey 是否過期,過期則刷新登錄態(tài)綁定微信授權(quán)手機(jī)號(hào)時(shí)驗(yàn)證是否過期,過期則得重新彈窗授權(quán)
        1. 裝飾器:

          • fuse-line:熔斷機(jī)制,如果短時(shí)間內(nèi)多次調(diào)用,則停止響應(yīng)一段時(shí)間,類似于 TCP 慢啟動(dòng)。用于解決refreshLogin、login等方法的并發(fā)處理問題。
          • single-queue:?jiǎn)侮?duì)列模式,同一時(shí)間,只允許一個(gè)正在過程中的網(wǎng)絡(luò)請(qǐng)求。請(qǐng)求被鎖定之后,同樣的請(qǐng)求都會(huì)被推入隊(duì)列,等待進(jìn)行中的請(qǐng)求返回后,消費(fèi)同一個(gè)結(jié)果。用于解決refreshLogin、login等方法的并發(fā)處理問題。

        4. 靜默登錄的調(diào)用時(shí)機(jī)

        4.1 小程序啟動(dòng)時(shí)調(diào)用

        由于大部分情況都需要依賴登錄態(tài),在小程序啟動(dòng)的時(shí)候(app.onLaunch())調(diào)用靜默登錄是最常見的手段。這里我們封裝一個(gè)login函數(shù)如下所示,首先調(diào)用wx.checkSession判斷session_key是否過期,如果session_key未過期且本地存在auth_token自定義登錄態(tài),表示當(dāng)前的靜默登錄態(tài)仍然有效,無需進(jìn)行其它操作。否則,表示靜默登錄態(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ù)制代碼

        但是由于原生的小程序啟動(dòng)流程中, App,Page,Component 的生命周期鉤子函數(shù),都不支持異步阻塞。所以很有可能出現(xiàn)小程序頁(yè)面加載完成后,靜默登錄過程還沒有執(zhí)行完畢的情況,這會(huì)導(dǎo)致后續(xù)一些依賴登錄態(tài)的操作(比如請(qǐng)求發(fā)起)出錯(cuò)。

        4.2 接口請(qǐng)求發(fā)起時(shí)調(diào)用

        保險(xiǎn)起見,如果某些接口需要攜帶自定義登錄態(tài)進(jìn)行鑒權(quán),則需要在請(qǐng)求發(fā)起時(shí)進(jìn)行攔截,校驗(yàn)登錄態(tài),并刷新登錄。刷新登錄代碼如下所示:

          public async refreshLogin(): Promise<void> {
            try {
              // 清除 Session
              this.clearSession();
              // 發(fā)起靜默登錄
              await this.silentLogin();
            } catch (error) {
              throw error;
            }
          }
        復(fù)制代碼

        整個(gè)流程如下圖所示:

        • 攔截 request
          1. 判斷是否需要鑒權(quán):請(qǐng)求發(fā)起時(shí),攔截請(qǐng)求,判斷請(qǐng)求是否需要添加auth-token,如若不需要,直接發(fā)起請(qǐng)求。如若需要,執(zhí)行第二步。
          2. 判斷是否需要發(fā)起靜默登錄:判斷 storage 中是否存在auth-token,如若不存在,發(fā)起「刷新登錄」。
          3. 請(qǐng)求頭部添加auth-token:添加auth-token,發(fā)起請(qǐng)求。
        • 與服務(wù)端通信:發(fā)起請(qǐng)求,服務(wù)端處理請(qǐng)求返回結(jié)果。
        • 攔截 response: 解析狀態(tài)碼
          1. 狀態(tài)碼為AUTH_FAIL:服務(wù)端返回code為“鑒權(quán)失敗”,觸發(fā)這種情景的原因有兩個(gè),一是接口需要鑒權(quán),但是發(fā)起請(qǐng)求時(shí)未攜帶auth-token,二是auth-token過期。這時(shí)將上一次請(qǐng)求攜帶的auth-token與本地存儲(chǔ)的auth-token比較,如果不一致,表示登錄態(tài)已經(jīng)刷新過了,那么就直接重新發(fā)起請(qǐng)求。如果一致,發(fā)起刷新登錄,拿到新的auth-token后重新發(fā)起請(qǐng)求,這個(gè)動(dòng)作對(duì)用戶來說是無感知的。
          2. 狀態(tài)碼為USER_WX_SESSIONKEY_EXPIRE:服務(wù)器返回code為“用戶登錄態(tài)過期”,這是針對(duì)用戶授權(quán)手機(jī)號(hào)登錄失敗定制的狀態(tài)碼,如果登錄態(tài)已過期,表示存儲(chǔ)在服務(wù)端的session_key也是過期的,那么點(diǎn)擊授權(quán)手機(jī)號(hào)獲取的加密數(shù)據(jù)發(fā)送到服務(wù)端進(jìn)行對(duì)稱解密,由于session_key失效,無法解密出真正的手機(jī)號(hào)。因此需要重新發(fā)起靜默登錄,等待用戶重新點(diǎn)擊授權(quán)按鈕獲取新的加密數(shù)據(jù),然后發(fā)起新的解密請(qǐng)求
          3. 狀態(tài)碼為其它:比如Success或者其他業(yè)務(wù)請(qǐng)求錯(cuò)誤的情況,不進(jìn)行攔截,返回 response 讓業(yè)務(wù)代碼解析。

        4.3 wx.checkSession 罷工之謎

        基于上述接口請(qǐng)求發(fā)起時(shí)調(diào)用的流程,很多人會(huì)有疑問,既然服務(wù)端會(huì)返回auth-token過期的狀態(tài)碼,為啥不在請(qǐng)求發(fā)送前進(jìn)行攔截,使用wx.checkSession接口校驗(yàn)登錄態(tài)是否過期(如下圖所示,增加紅框內(nèi)的步驟)?

        這是因?yàn)?,我們通過實(shí)驗(yàn)發(fā)現(xiàn),在 session_key 已過期的情況下,wx.checkSession 有一定的幾率返回true。即增加wx.checkSession步驟并不能百分百保證登錄態(tài)不會(huì)過期,后續(xù)仍然需要對(duì)不同的狀態(tài)碼進(jìn)行處理。

        社區(qū)也有相關(guān)的反饋未得到解決:

        • 小程序解密手機(jī)號(hào),隔一小段時(shí)間后,checksession:ok,但是解密失敗
        • wx.checkSession 有效,但是解密數(shù)據(jù)失敗
        • checkSession 判斷 session_key 未失效,但是解密手機(jī)號(hào)失敗

        所以結(jié)論是:wx.checkSession可靠性是不達(dá) 100% 的。

        基于以上,我們需要對(duì) session_key 的過期做一些容錯(cuò)處理:

        1. 發(fā)起需要使用 session_key 的請(qǐng)求前,做一次 wx.checkSession 操作,如果失敗了刷新登錄態(tài)。
        2. 后端使用session_key解密開放數(shù)據(jù)失敗之后,返回特定錯(cuò)誤碼(如:USER_WX_SESSIONKEY_EXPIRE),前端刷新登錄態(tài)。

        4.4 并發(fā)處理

        我們知道,當(dāng)啟動(dòng)小程序時(shí),各種監(jiān)控、埋點(diǎn)數(shù)據(jù)上報(bào)都需要獲取用戶的個(gè)人信息,這些信息都得「靜默登錄」后才能獲取,因此會(huì)同時(shí)發(fā)起多個(gè)login請(qǐng)求。另一種情況下,假設(shè)一個(gè)新用戶進(jìn)入一個(gè)業(yè)務(wù)復(fù)雜的頁(yè)面,同時(shí)發(fā)起五個(gè)不同的業(yè)務(wù)請(qǐng)求,恰巧這五個(gè)請(qǐng)求都需要鑒權(quán),那么五個(gè)請(qǐng)求都會(huì)被攔截并發(fā)起refreshLogin請(qǐng)求。顯然,這樣的并發(fā)是不合理的。

        基于此,我們?cè)O(shè)計(jì)了如下方案:

        • 單隊(duì)列模式

          1. 請(qǐng)求鎖:同一時(shí)間,只允許一個(gè)正在過程中的網(wǎng)絡(luò)請(qǐng)求。

          2. 等待隊(duì)列:請(qǐng)求被鎖定之后,同樣的請(qǐng)求都會(huì)被推入隊(duì)列,等待進(jìn)行中的請(qǐng)求返回后,消費(fèi)同一個(gè)結(jié)果。

        • 熔斷機(jī)制:如果短時(shí)間內(nèi)多次調(diào)用,則停止響應(yīng)一段時(shí)間,類似于 TCP 慢啟動(dòng)。

        如上圖所示,首先refreshLogin請(qǐng)求入隊(duì),隊(duì)列中只有一個(gè)請(qǐng)求,發(fā)送該請(qǐng)求,同時(shí)保險(xiǎn)絲計(jì)入次數(shù) 1,服務(wù)端返回請(qǐng)求結(jié)果,消費(fèi)結(jié)果。接著又發(fā)起一個(gè)refreshLogin請(qǐng)求,隊(duì)列中只有一個(gè)請(qǐng)求,發(fā)送該請(qǐng)求,同時(shí)保險(xiǎn)絲計(jì)入次數(shù) 2。然后又連續(xù)發(fā)起三個(gè)請(qǐng)求,由于上一個(gè)請(qǐng)求還沒有執(zhí)行完成,將這三個(gè)請(qǐng)求入隊(duì),等待上一個(gè)請(qǐng)求結(jié)果返回,隊(duì)列中的四個(gè)請(qǐng)求消費(fèi)同一個(gè)結(jié)果。由于觸發(fā)自動(dòng)冷卻閾值,保險(xiǎn)絲重置。

        以上兩種方案通過裝飾器模式引入,代碼如下所示,refreshLogin函數(shù)其實(shí)是slientLogin函數(shù)的一層封裝,用于接口發(fā)起時(shí)調(diào)用。而前面提到的login函數(shù)也是slientLogin函數(shù)的一層封裝,用戶小程序啟動(dòng)時(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ù)制代碼

        到此,很多讀者可能對(duì)熔斷機(jī)制還不甚理解,熔斷的目的是為一個(gè)函數(shù)提供保險(xiǎn)絲保障,短時(shí)間內(nèi)多次調(diào)用,會(huì)熔斷一段時(shí)間,這段時(shí)間內(nèi)拒絕所有請(qǐng)求。如果在自動(dòng)冷卻閾值內(nèi),沒有請(qǐng)求通過,則重置保險(xiǎn)絲。代碼如下所示:

        export default function fuseLine({
          // 一次熔斷前重試次數(shù)
          tryTimes = 3,

          // 重試間隔,單位 ms
          restoreTime = 5000,

          // 自動(dòng)冷卻閾值,單位 ms
          coolDownThreshold = 1000,

          // 名稱
          name = 'unnamed',
        }: {
          tryTimes?: number;
          restoreTime?: number;
          name?: string;
          coolDownThreshold?: number;
        } = {}
        {
          // 請(qǐng)求鎖
          let fuseLocked = false;

          // 當(dāng)前重試次數(shù)
          let fuseTryTimes = tryTimes;

          // 自動(dòng)冷卻
          let coolDownTimer;

          // 重置保險(xiǎn)絲
          const reset = () => {
            fuseLocked = false;
            fuseTryTimes = tryTimes;
            logger.info(`${name}-保險(xiǎn)絲重置`);
          };

          const request = async () => {
            if (fuseLocked) throw new Error(`${name}-保險(xiǎn)絲已熔斷,請(qǐng)稍后重試`);

            // 已達(dá)最大重試次數(shù)
            if (fuseTryTimes <= 0) {
              fuseLocked = true;

              // 重置保險(xiǎn)絲
              setTimeout(() => reset(), restoreTime);

              throw new Error(`${name}-保險(xiǎn)絲熔斷!!`);
            }

            // 自動(dòng)冷卻系統(tǒng)
            if (coolDownTimer) clearTimeout(coolDownTimer);
            coolDownTimer = setTimeout(() => reset(), coolDownThreshold);

            // 允許當(dāng)前請(qǐng)求通過保險(xiǎn)絲,記錄 +1
            fuseTryTimes = fuseTryTimes - 1;
            logger.info(`${name}-通過保險(xiǎn)絲(${tryTimes - fuseTryTimes}/${tryTimes})`);
            return Promise.resolve();
          };

          return function(
            _target: Record<string, any>,
            _propertyName: string,
            descriptor: TypedPropertyDescriptor<(...args: any[]
        ) => any>,
          ) 
        {
            const method = descriptor.value;
            descriptor.value = async function(...args: any[]{
              await request();
              if (method) return method.apply(this, args);
            };
          };
        }
        復(fù)制代碼

        5. 最后

        讀到這里,相信你已經(jīng)了解「靜默登錄」和「用戶登錄」的區(qū)別?!胳o默登錄」是獲取微信登錄態(tài)的過程,通過獲取微信提供的用戶身份標(biāo)識(shí),快速建立小程序內(nèi)的用戶體系?!赣脩舻卿洝故怯脩羰跈?quán)個(gè)人開放數(shù)據(jù)成為會(huì)員的過程,是指從游客態(tài)轉(zhuǎn)換成會(huì)員態(tài)的,擁有購(gòu)買等操作權(quán)限。

        兩者并不是一個(gè)概念,「用戶登錄」會(huì)在下一篇文章《小程序用戶登錄架構(gòu)設(shè)計(jì)》中進(jìn)行闡述。

        瀏覽 62
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            好日屌视频 | 日本一级毛一片免费视频 | 国产午夜精品一区二区三区免费 | 涩涩的动漫在线观看 | 欧美日韩在线视频观看 | 五月激情深爱网 | 偷偷解开女同桌的内裤摸小说 | 总裁疯狂做爰小说片段 | 女人被强行糟蹋过程h电影 | 操逼网站进入 |