1. <dd id="mhwbf"><track id="mhwbf"></track></dd>

  2. <em id="mhwbf"></em><dd id="mhwbf"></dd>
  3. <rp id="mhwbf"><object id="mhwbf"></object></rp>

    1. <em id="mhwbf"><ruby id="mhwbf"></ruby></em>
      <button id="mhwbf"><acronym id="mhwbf"></acronym></button>

          <dd id="mhwbf"></dd>

          小程序用戶登錄架構設計

          來源:掘金
          2021-04-02
          3173
          導語:通過調用 wx.login 獲取到 code ,將其發送到開發者后端,開發者后端通過接口去微信后端換取到 openid 和 sessionKey(現在會將 unionid 也一并返回)后,然后把自定義登錄態 3rd_session(本業務命名為auth-token) 返回給前端,就已經完成登錄行為了。 作者:蔡小真 鏈接:https://juejin.cn/post/6945264484491460638 來源:掘金 著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
          閱讀本文大概需要7分鐘

          1. 背景

          上一篇文章《小程序靜默登錄方案設計》提到過,小程序可以通過微信官方提供的登錄能力方便地獲取微信提供的用戶身份標識,快速建立小程序內的用戶體系。

          即「靜默登錄」,通過調用 wx.login 獲取到 code ,將其發送到開發者后端,開發者后端通過接口去微信后端換取到 openidsessionKey(現在會將 unionid 也一并返回)后,然后把自定義登錄態 3rd_session(本業務命名為auth-token) 返回給前端,就已經完成登錄行為了。

          理論上,開發者后端可以通過 openid識別用戶,也能通過unionid關聯同主體的多個小程序、公眾號、app,實現數據互通,從而為每一個用戶創建獨一無二的uid(本業務自定義的用戶 id),在「微信生態」中建立成熟用戶體系。

          然而,對于復雜的電商跨端應用,比如pc、h5、小程序,不同渠道注冊的uid是不同的,用戶登錄后難以對各個渠道的交易、促銷、收藏等數據進行整合。因此,要實現跨端的用戶體系數據互通,就需要提供一個唯一的用戶標識——手機號。這便是本文重點講述的「用戶登錄」,即「游客態」轉變成「會員態」的過程。

          2. 「用戶登錄」流程

          上一篇文章《小程序靜默登錄方案設計》中提過,當新用戶第一次進入小程序時,便會觸發「靜默登錄」,這個過程對用戶是無感知的。但此時開發者服務端已經為該用戶定義了uid,并下發auth-token給小程序端,對于一些需要鑒權的請求,服務端可以根據請求攜帶的auth-token精確識別是哪個用戶發起的行為。

          然而,類似加購、下單、領券等用戶行為,涉及到跨端數據的整合,在執行用戶操作之前,會判斷用戶是否登錄,如若用戶未登錄,則跳轉登錄頁面,整個流程如下所示:

          登錄流程圖

          比如在「用戶中心」頁面點擊「我的訂單」,由于此時用戶未登錄,跳轉到登錄頁面,可以選擇以下兩種登錄方式:

          1. 選擇 「微信授權登錄」,彈出授權手機號信息彈窗,點擊「允許」,此時用戶登錄成功。
          2. 選擇 「手機快捷登錄」,輸入手機號,使用 「驗證碼」 或者 「密碼」 進行登錄,登錄成功跳轉回到「用戶中心」頁面。

          上述步驟已經完成了「用戶登錄」,用戶可以正常的執行加購、領券、下單等操作。 為了提升用戶體驗,需要對 「會員信息」 進行維護 ,比如昵稱、頭像、性別、生日等信息,最簡單的方法是 獲取「微信授權用戶信息」。觸發時機分為以下兩種:

          1. 用戶第一次選擇 「微信授權登錄」 成功后跳轉授權用戶信息頁面,點擊 「授權用戶信息」,彈出授權用戶信息彈窗。點擊「允許」,跳轉回「用戶中心」頁面。
          2. 在「用戶中心」頁面點擊頭像昵稱區域,彈出授權用戶信息彈窗,點擊「允許」,更新「會員信息」并跳轉用戶信息編輯頁面。

          3. 「用戶登錄」方案設計

          3.1 架構

          用戶登錄架構

          「用戶登錄」方案架構如上圖所示,將所有登錄相關功能抽象到 「service 層」(本項目將其命名為session),供 「業務層」 調用。該 「service 層」 主要分為以下兩個模塊:

          3.1.1 libs - 提供登錄相關的類方法供「業務層」調用

          1. 封裝session類,提供類方法供「業務層」調用。主要有以下幾種方法:
          方法名功能使用場景
          silentLogin發起靜默登錄-
          login登錄,silentLogin方法的一層封裝用于小程序啟動時發起靜默登錄
          refreshLogin刷新登錄態,silentLogin方法的一層封裝用于登錄態過期時發起靜默登錄
          ensureSessionKey驗證sessionKey是否過期,過期則刷新登錄態綁定微信授權手機號時驗證是否過期,過期則得重新彈窗授權
          bindPhone綁定微信授權手機號微信授權手機號彈窗點擊「允許」觸發
          updateUser綁定微信授權用戶信息微信授權用戶信息點擊「允許」觸發
          getCurrentAuthStep獲取當前用戶登錄所屬階段詳見下文
          mustAuth各種觸發場景攔截判斷是否需要登錄詳見下文

          當然,session類中還封裝了一些方法用于與storage交互,比如獲取storage中的auth-token用于各種鑒權請求攜帶等等。session類也提供的一些拓展方法,比如注銷賬號、解綁手機號等等用于后續需求迭代。

          1. 裝飾器:

            • must-authmustAuth類方法的裝飾器,便于業務層各種場景觸發登錄。
            • fuse-line熔斷機制,如果短時間內多次調用,則停止響應一段時間,類似于 TCP 慢啟動。用于解決refreshLogin、login等方法的并發處理問題。
            • single-queue單隊列模式,同一時間,只允許一個正在過程中的網絡請求。請求被鎖定之后,同樣的請求都會被推入隊列,等待進行中的請求返回后,消費同一個結果。用于解決refreshLogin、login等方法的并發處理問題。

          3.1.2 ui - 提供通用組件供業務層調用

          1. 基礎組件user-containerphone-container分別是獲取「微信授權用戶信息」和獲取「微信授權手機號」的純 UI 單元組件,給通用組件使用。
          2. behavior 類:拿到授權數據后需要發送給服務端進行存儲,也需要執行一些跳轉邏輯判斷,這些都抽象成行為類封裝在auth-flow中,供通用組件使用。
          3. 通用組件: 共用一個行為類,區別在于auth-flow-container用于頁面,auth-flow-popup用于彈窗。如下所示,小程序只有微信授權功能,則可以通過彈窗完成授權。如小程序同時提供手機號驗證碼和密碼登錄等功能,則需跳轉特定登錄頁面。
          登錄流程-彈窗

          3.2 libs

          3.2.1 用戶身份定義

          用戶登錄階段

          綜上所示,用戶登錄的階段可以分為以下三步:

          // 用戶登錄的階段
          export enum AuthStepType {
            // 階段一:游客態:靜默登錄成功,未綁定手機號,無用戶信息
            ONE = 1,
            // 階段二:會員態:用戶登錄成功,已綁定手機號,無用戶信息
            TWO = 2,
            // 階段三:會員信息態:用戶登錄成功,已綁定手機號,有用戶信息
            THREE = 3,
          }
          復制代碼

          那么如何判斷用戶此時處于哪個步驟,基于「靜默登錄」的啟發,原本「靜默登錄」成功開發者后端會將自定義登錄態 auth-token返回給前端,此處請求可以攜帶返回「用戶信息」,同auth-token一起命名為session存儲在本地storage。當「用戶登錄」或者「更新用戶信息」時,會同步更新storagekeysession的數據,從而通過這些用戶數據判斷當前用戶處于哪一個登錄階段。

          以下表格列出了session存儲的部分重要的屬性以及在三個階段屬性對應的值。

          屬性定義游客態會員態會員信息態
          authToken自定義登錄態'0d5bad172...''0d5bad172...''0d5bad172...'
          uid用戶 id'001''001''001'
          busiIdentity用戶身份定義'VISIT''MEMBER''MEMBER'
          nickName用戶昵稱'''u_a1bk45''rileycai'
          headUrl頭像鏈接'''''www.xx.com/image/...'
          phone手機號碼'''17600888888''17600888888'
          ...其它用戶信息.........

          注意: 會員態和會員信息態的busiIdentity值均為MEMBER,區分會員態和會員信息態可以通過用戶昵稱和頭像等字段,比如用戶登錄成功會為用戶生成以'u_'開頭的默認昵稱和默認為空的用戶頭像鏈接。

          判斷用戶此時處于哪個步驟的代碼如下:

            // 獲取當前授權階段
            public getCurrentAuthStep(): AuthStepType {
              // 切換賬號登錄的時候,始終返回AuthStepType.ONE
              const loginMode = this.getLoginMode();
              if (loginMode === LoginMode.SWITCH_ACCOUNT) return AuthStepType.ONE;
          
              // 用戶身份定義非會員返回AuthStepType.ONE
              const userInfo = this.getUser();
              if (userInfo?.busiIdentity !== 'MEMBER') return AuthStepType.ONE;
          
              // 初次登錄,未授權用戶信息,返回AuthStepType.TWO
              if (userInfo.nickName.substring(0, 2) === 'u_' && !userInfo.headUrl)
                return AuthStepType.TWO;
          
              // 都有,返回AuthStepType.THREE
              return AuthStepType.THREE;
            }
          復制代碼

          3.2.2 用戶登錄觸發場景

          前面提到過,「用戶登錄」的 目的是為了整合各個渠道的交易、促銷、收藏等數據,針對電商小程序,目前總結的需要用戶登錄的場景如下所示:

          用戶登錄場景

          即當用戶登錄小程序時,可以正常瀏覽瀏覽商品,只有觸發某些特定行為,比如領券、加購、收藏、下單等,才會判斷用戶是否處于登錄狀態,如未登錄,跳轉登錄頁面。

          如下所示,封裝mustAuth方法進行攔截,未登錄則跳轉登錄頁面:

          export default class Session {
            ...
            public mustAuth({
              mustAuthStep = AuthStepType.TWO, // 傳人參數,需要授權的LEVEL
            } = {}): Promise<void> {
              // 當前階段處于會員態(2)或者會員信息態(3),執行resolve操作
              if (this.getCurrentAuthStep() >= mustAuthStep) return Promise.resolve();
              // 當前階段處于游客態(1),跳轉登錄頁
              Navigator.gotoPage('/login/home');
              // 執行reject操作
              return Promise.reject();
            }
          }
          復制代碼

          上述代碼是跳轉頁面攔截,對于彈窗而言,需要把彈窗注入base-page(每個頁面都需要引入的通用組件,封裝每個頁面都需要使用的通用方法,比如錯誤處理等)中,通過 id 查找到彈窗組件,并進行調用。

          export default class Session {
            ...
             public mustAuth({
              mustAuthStep = AuthStepType.TWO, // 需要授權的LEVEL
              popupCompName = 'auth-flow-popup',
            } = {}): Promise<void> {
              // 當前階段處于會員態(2)或者會員信息態(3),執行resolve操作
              if (this.getCurrentAuthStep() >= mustAuthStep) return Promise.resolve();
              // 獲取彈窗組件
              const pages = getCurrentPages();
              const curPage = pages[pages.length - 1];
              const context = curPage.$$basePage || curPage;
              const popupComp = context.selectComponent(`#${popupCompName}`);
              // 容錯處理
              if (!popupComp) {
                return Promise.reject(
                  new Error(
                    "當前頁面未找到 #auth-popup 組件,請參考 'doc/登錄組件的使用方式.md'",
                  ),
                );
              }
              // 調用彈窗組件方法
              popupComp.setMustAuthStep(mustAuthStep);
              popupComp.nextStep();
              // 等待授權成功回調
              return this.waitAuth();
            }
          }
          
          復制代碼

          各個業務使用時可以通過session.mustAuth().then(() => {...});進行調用,為了提高使用體驗,也可以使用裝飾器@mustAuth()來修飾各個業務需求 類的方法,裝飾器源碼如下:

          /**
           * 登錄檢查裝飾器,使用該裝飾器的方法,會先執行授權檢查,如果未授權,將跳轉登錄頁面
           */
          export default function mustAuth(option = {}) {
            return function(
              _target: Record<string, any>,
              _propertyName: string,
              descriptor: TypedPropertyDescriptor<(...args: any[]) => any>,
            ) {
              const method = descriptor.value;
              descriptor.value = function(...args: any[]) {
                if (!session) return;
                // 登錄攔截
                return session.mustAuth(option).then(() => {
                  if (method) return method.apply(this, args);
                });
              };
            };
          }
          復制代碼

          3.3 UI

          3.3.1 基礎組件

          1. phone-container 組件

          因為需要用戶主動觸發才能發起獲取微信授權手機號接口,需用 button 組件的點擊來觸發。組件代碼如下所示:

          // index.wxml
           <button class="reset-button" open-type="getPhoneNumber" bindgetphonenumber="getPhoneNumber" hover-class="none" disabled="{{disabled}}"><slot></slot></button>
          
          // index.ts
          export default class PhoneContainer extends BaseComponent {
            getPhoneNumber(
              e: WechatMiniprogram.Event<WechatMiniprogram.GetPhoneNumberCallbackResult>,
            ) {
              this.triggerEvent('getphonenumber', { ...e.detail,  authType: AuthType.PHONE,});
            }
          }
          復制代碼

          phone-container是一個純 UI 組件,通過triggerEvent事件將獲取手機號數據傳遞給父組件,

          2. user-container 組件

          user-container組件是獲取微信授權用戶信息的純 UI 組件,之前通過<button open-type="getUserInfo" bindgetUserInfo="getUserInfo"/>的方式進行獲取。2021 年 2 月 23 日,微信團隊發布了《小程序登錄、用戶信息相關接口調整說明》,新增getUserProfile接口替代原來的wx.getUserInfo,來獲取用戶頭像、昵稱、性別及地區信息,也是通過button 組件的點擊來觸發。兩者的區別如下圖所示:

          獲取用戶信息接口區別

          2012 年 4 月 13 日之前,使用wx.getUserInfo彈出授權彈窗時,如果用戶點擊允許授權,那么會記錄用戶的行為,下次再點擊時,不會彈窗而是直接將授權結果返回。4 月 13 日之后后,使用wx.getUserProfile,開發者每次通過該接口獲取用戶個人信息均需用戶確認,因此需要妥善保管用戶授權的頭像昵稱,避免重復彈窗。

          3.3.2 行為類

          如下圖所示,auth-flow行為類主要封裝用戶、小程序、服務端三者之間的交互邏輯。

          用戶行為

          在「微信授權登錄」過程中,小程序拿到加密的encryptedDataiv數據,將其和攜帶的auth-token一起發送給開發者服務器,服務端通過auth-token鑒權識別這個用戶,并使用靜默登錄成功獲取的session_key(對稱解密密鑰)對encryptedDataiv數據進行對稱解密,獲取該用戶的手機號,將手機號與uid綁定,此時該用戶成功注冊會員,并將會員信息返回給小程序端。

          小程序端更新本地storage存儲的session數據,此時busiIdentity的值已經從VISIT更新為MEMBER,用戶身份轉變為會員態,登錄成功。

          在「授權用戶信息」的過程中,小程序調用wx.getUserProfile方法拿到用戶數據,并將這些數據與攜帶的auth-token一起發送給開發者服務器,服務端通過auth-token鑒權識別這個用戶,更新該用戶的信息并將新的會員數據返回給小程序端。

          小程序端更新本地storage存儲的session數據,此時用戶昵稱和頭像均已更新,用戶身份轉變為會員信息態,授權成功。

          眼尖的讀者一定觀察到了,時序圖中還對微信頭像做了轉存。這是因為用戶在微信端修改微信頭像后,之前「授權用戶信息」獲取的微信頭像鏈接就會失效,因此開發者應該在自己獲取用戶信息后,將頭像保存下來,避免微信頭像 URL 失效后的異常情況。

          3.3.3 通用組件

          通用組件是對基礎組件和行為類的二次封裝,主要是為業務層提供彈窗登錄和頁面登錄兩種能力。

          4. 總結

          我們將用戶登錄能力從業務層中抽象出來,統一封裝在service層,便于復用。本文主要講述的是service層的架構,對于業務層的邏輯實現并沒有多加累贅。下列表格以小程序端為例,簡述了「靜默登錄」和「用戶登錄」整套方案的前后端邏輯實現。

          業務場景用戶感知前端處理邏輯后端處理邏輯補充說明
          掃碼搜索等各種方式進入小程序1、判斷:當前小程序是否緩存了登錄態auth-token 且使用wx.checkSeesion檢查當前用戶在小程序中登錄態是否過期,過期執行步驟 2;
          2、使用wx.login獲取認證信息,請求后端wxLogin接口獲取微信小程序認證默認綁定的用戶身份以及登錄態auth-token。
          1、解析微信加密信息獲取認證身份openidunionId;
          2、查找openid是否已經綁定了對應的用戶,若綁定直接返回并為其生成對應的登錄態auth-token;
          3、新用戶會根據openid為其自動生成一個用戶身份uid(見右補充說明)。
          a、存在聚合根標識unionId && 有用戶信息:將已有聚合根用戶對應的exUid直接映射到當前uid下;
          b、存在聚合根標識unionId && 無用戶信息:根據unionId生成對應的賬號,但和opneid對應的uid一致;
          c、不存在聚合根標識:直接為對應openid初始化一個uid。
          收藏、加購、下單、領券等操作攔截跳轉1、判斷: 當前用戶身份處于游客態,跳轉登錄頁面。對應域服務后端接口可以根據請求攜帶的auth-token進行鑒權,判斷用戶是否有操作權限-
          用戶登錄 或者 切換賬號選擇:
          1、授權微信手機號登錄;
          2、輸入手機號并使用驗證碼/密碼登錄
          1、用戶選擇授權手機號登錄,后端會根據上一次靜默登錄的sesssionKey解密,如果解密失敗需要重新走一遍靜默登錄后再讓客戶重試。
          2、用戶選擇通過驗證碼登錄時,需關注驗證碼時效和重試機制,并有錯誤處理邏輯;
          3、用戶選擇密碼登錄時,后臺會返回賬戶未注冊或賬號密碼不對等錯誤,需要有獨立邏輯跳轉驗證碼注冊或找回密碼
          4、以上三種方式都需要攜帶auth-token進行鑒權
          1、根據auth-token獲取當前的渠道基本認證賬戶openid-unionId-uid;
          2、授權手機號登錄時需要先解密出手機號,此時不需要校驗,輸入手機號登錄時需要會走「密碼」或「驗證碼」校驗,密碼校驗會攔截賬號不存在或密碼錯誤的場景;
          3、根據手機號判斷當前聚合根下是否存在對應的手機號渠道賬號(綁定流程見右補充說明)。
          4、返回登錄結果。
          a、手機號已存在:將已存在的用戶exUid綁定至當前登錄態賬號;
          b、手機號不存在 && 用戶身份是游客:將手機號和游客對應的uid進行綁定
          c、手機號不存在 && 用戶身份是會員:為手機號生成一個新的newUid,并將當前登錄的 openid 渠道賬戶綁定至該newUid。

          作者:蔡小真
          鏈接:https://juejin.cn/post/6945264484491460638
          來源:掘金
          著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
          免費獲取專屬 《策劃方案 》及報價
          免費體驗我們的業務系統、OA系統、在線教育、電商系統、智慧辦公等產品定制化方案,助力您的信息化發展之路
          即時交流
          在線咨詢 電話咨詢
          在線咨詢
          產品經理

          一對一產品經理

          180 8812 7777
          電話咨詢

          電話咨詢

          0871-6718 6978
          到訪面聊
          返回頂部
          欧美人妻久久精品,久久国产色av免费看,女人国产香蕉久久精品,伊人久久综在合线亚洲