溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

微信小程序登錄前端設計與實現的示例分析

發布時間:2021-01-28 10:31:33 來源:億速云 閱讀:219 作者:小新 欄目:移動開發

小編給大家分享一下微信小程序登錄前端設計與實現的示例分析,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

今天微信小程序開發欄目介紹小程序登錄的前端設計與實現。

一. 前言

對于登錄/注冊的設計如此精雕細琢的目的,當然是想讓這個作為應用的基礎能力,有足夠的健壯性,避免出現全站性的阻塞。

同時要充分考慮如何解耦和封裝,在開展新的小程序的時候,能更快的去復用能力,避免重復采坑。

登錄注冊這模塊,就像個冰山,我們以為它就是「輸入賬號密碼,就完成登錄了」,但實際下面還有各種需要考慮的問題。

微信小程序登錄前端設計與實現的示例分析

在此,跟在座的各位分享一下,最近做完一個小程序登錄/注冊模塊之后,沉淀下來的一些設計經驗和想法。

二. 業務場景

在用戶瀏覽小程序的過程中,由業務需要,往往需要獲取用戶的一些基本信息,常見的有:

  1. 微信昵稱

  2. 微信手機號

而不同的產品,對于用戶的信息要求不盡相同,也會有不一樣的授權流程。

第一種,常見于電商系統中,用戶購買商品的時候,為了識別用戶多平臺的賬號,往往用手機號去做一個聯系,這時候需要用戶去授權手機號。

微信小程序登錄前端設計與實現的示例分析

第二種,為了讓用戶信息得到基本的初始化,往往需要更進一步獲取用戶信息:如微信昵稱,unionId 等,就需要詢問用戶授權。

微信小程序登錄前端設計與實現的示例分析

第三種,囊括第一種,第二種。

微信小程序登錄前端設計與實現的示例分析

三. 概念

秉著沉淀一套通用的小程序登錄方案和服務為目標,我們去分析一下業務,得出變量。

在做技術設計之前,講點必要的廢話,對一些概念進行基本調頻。

2.1 關于「登錄」

登錄在英文中是 「login」,對應的還有 「logout」。而登錄之前,你需要擁有一個賬號,就要 「register」(or sign up)。

話說一開始的產品是沒有登錄/注冊功能的,用的人多了就慢慢有了。出于產品本身的需求,需要對「用戶」進行身份識別。

在現實社會中,我們每個人都有一個身份ID:身份證。當我到了16歲的時候,第一次去公安局領身份證的時候,就完成了一次「注冊」行為。然后我去網吧上網,身份證刷一下,完成了一次「登錄」行為。

那么對于虛擬世界的互聯網來說,這個身份證明就是「賬號+密碼」。

常見的登錄/注冊方式有:

  1. 賬號密碼注冊

    在互聯網的早期,個人郵箱和手機覆蓋度小。所以,就需要用戶自己想一個賬號名,我們注冊個QQ號,就是這種形式。

    微信小程序登錄前端設計與實現的示例分析

  2. 郵箱地址注冊

    千禧年之后,PC互聯網時代快速普及,我們都創建了屬于自己的個人郵箱。加上QQ也自帶郵箱賬號。由于郵箱具有個人私密性,且能夠進行信息的溝通,因此,大部分網站開始采用郵箱賬號作為用戶名來進行注冊,并且會在注冊的過程中要求登錄到相應郵箱內查收激活郵件,驗證我們對該注冊郵箱的所有權。

    微信小程序登錄前端設計與實現的示例分析

  3. 手機號碼注冊

    在互聯網普及之后,智能手機與移動互聯網發展迅猛。手機也成為每個人必不可少的移動設備,同時移動互聯網也已經深深融入每個人的現代生活當中。所以,相較于郵箱,目前手機號碼與個人的聯系更加緊密,而且越來越多的移動應用出現,采用手機號碼作為用戶名的注冊方式也得到了廣泛的使用。

    微信小程序登錄前端設計與實現的示例分析

到了 2020 年,微信用戶規模達 12 億。那么,微信賬號,起碼在中國,已成為新一代互聯網世界的「身份標識」。

而對微信小程序而言,天然就能知道當前用戶的微信賬號ID。微信允許小程序應用,能在用戶無感知的情況下,悄無聲息的「登錄」到我們的小程序應用中去,這個就是我們經常稱之為的「靜默登錄」。

其實微信小程序的登錄,跟傳統 Web 應用的「單點登錄」本質是一樣的概念。

  1. 單點登錄:在 A 站登錄了,C 站和 B 站能實現快速的「靜默登錄」。

  2. 微信小程序登錄:在微信中,登錄了微信賬號,那么在整個小程序生態中,都可以實現「靜默登錄」。

由于 Http 本來是無狀態的,業界基本對于登錄態的一般做法:

  1. cookie-session:常用于瀏覽器應用中

  2. access token:常用于移動端等非瀏覽器應用

在微信小程序來說,對于「JS邏輯層」并不是一個瀏覽器環境,自然沒有 Cookie,那么通常會使用 access token 的方式。

2.2 關于「授權」

對于需要更進一步獲取用的用戶昵稱、用戶手機號等信息的產品來說。微信出于用戶隱私的考慮,需要用戶主動同意授權。小程序應用才能獲取到這部分信息,這就有了目前流行的小程序「授權用戶信息」、「授權手機號」的交互了。

出于不同的用戶信息敏感度不同的考慮,微信小程序對于不同的用戶信息提供「授權」的方式不盡相同:

  1. 調用具體 API 方式,彈窗授權。

    1. 例如調用 wx.getLocation() 的時候,如果用戶未授權,則會彈出地址授權界面。

    2. 如果拒絕了,就不會再次彈窗,wx.getLocation()直接返回失敗。

  2. <button open-type="xxx" /> 方式。

    1. 僅支持:用戶敏感信息,用戶手機號,需要配合后端進行對稱加解密,方能拿到數據。

    2. 用戶已拒絕,再次點擊按鈕,仍然會彈窗。

  3. 通過 wx.authorize(),提前詢問授權,之后需要獲取相關信息的時候不用再次彈出授權。

四. 詳細設計

梳理清楚了概念之后,我們模塊的劃分上,可以拆分為兩大塊:

  1. 登錄:負責與服務端創建起一個會話,這個會話實現靜默登錄以及相關的容錯處理等,模塊命名為:Session

  2. 授權:負責與用戶交互,獲取與更新信息,以及權限的控制處理等,模塊命名為:Auth

3.1 登錄的實現

3.1.1 靜默登錄

微信小程序登錄前端設計與實現的示例分析

微信官方提供的登錄方案,總結為三步:

  1. 前端通過 wx.login() 獲取一次性加密憑證 code,交給后端。

  2. 后端把這個 code 傳輸給微信服務器端,換取用戶唯一標識 openId 和授權憑證 session_key。(用于后續服務器端和微信服務器的特殊 API 調用,具體看:微信官方文檔-服務端獲取開放數據)。

  3. 后端把從微信服務器獲取到的用戶憑證與自行生成的登錄態憑證(token),傳輸給前端。前端保存起來,下次請求的時候帶給后端,就能識別哪個用戶。

如果只是實現這個流程的話,挺簡單的。

但要實現一個健壯的登錄過程,還需要注意更多的邊界情況:

  1. 收攏 wx.login() 的調用

    由于 wx.login() 會產生不可預測的副作用,例如會可能導致session_key失效,從而導致后續的授權解密場景中的失敗。我們這里可以提供一個像 session.login() 的方法,掌握 wx.login() 控制權,對其做一系列的封裝和容錯處理。

  2. 調用的時機

    通常我們會在應用啟動的時候( app.onLaunch() ),去發起靜默登錄。但這里會由小程序生命周期設計問題而導致的一個異步問題:加載頁面的時候,去調用一個需要登錄態的后端 API 的時候,前面異步的靜態登錄過程有可能還沒有完成,從而導致請求失敗。

    當然也可以在第一個需要登錄態的接口調用的時候以異步阻塞的方式發起登錄調用,這個需要結合良好設計的接口層。

    以上講到的兩種場景的詳細設計思路下文會講到。

  3. 并發調用的問題

    在業務場景中,難免會出現多處代碼需要觸發登錄,如果遇到極端情況,這多處代碼同時間發起調用。那就會造成短時間多次發起登錄過程,盡管之前的請求還沒有完成。針對這種情況,我們可以以第一個調用為阻塞,后續調用等待結果,就像精子和卵子結合的過程。

  4. 未過期調用的問題

    如果我們的登錄態未過期,完全可以正常使用的,默認情況就不需再去發起登錄過程了。這時候我們可以默認情況下先去檢查登錄態是否可用,不能用,我們再發起請求。然后還可以提供一個類似 session.login({ force: true })的參數去強行發起登錄。

3.1.2 靜默登錄異步狀態的處理

1. 應用啟動的時候調用

因為大部分情況都需要依賴登錄態,我們會很自然而然的想到把這個調用的時機放到應用啟動的時候( app.onLaunch() )來調用。

但是由于原生的小程序啟動流程中, App,Page,Component 的生命周期鉤子函數,都不支持異步阻塞。

那么我們很容易會遇到 app.onLaunch 發起的「登錄過程」在 page.onLoad 的時候還沒有完成,我們就無法正確去做一些依賴登錄態的操作。

針對這種情況,我們設計了一個狀態機的工具:status

微信小程序登錄前端設計與實現的示例分析

基于狀態機,我們就可以編寫這樣的代碼:

import { Status } from '@beautywe/plugin-status';// on app.jsApp({    status: {       login: new Status('login');
    },    onLaunch() {
        session            // 發起靜默登錄調用
            .login()            // 把狀態機設置為 success
            .then(() => this.status.login.success())      
            // 把狀態機設置為 fail
            .catch(() => this.status.login.fail());
    },
});// on page.jsPage({    onLoad() {      const loginStatus = getApp().status.login;      
      // must 里面會進行狀態的判斷,例如登錄中就等待,登錄成功就直接返回,登錄失敗拋出等。
      loginStatus().status.login.must(() => {        // 進行一些需要登錄態的操作...
      });
    },
});復制代碼

2. 在「第一個需要登錄態接口」被調用的時候去發起登錄

更進一步,我們會發現,需要登錄態的更深層次的節點是在發起的「需要登錄態的后端 API 」的時候。

那么我們可以在調用「需要登錄態的后端 API」的時候再去發起「靜默登錄」,對于并發的場景,讓其他請求等待一下就好了。

以 fly.js 作為 wx.request() 封裝的「網絡請求層」,做一個簡單的例子:

// 發起請求,并表明該請求是需要登錄態的fly.post('https://...', params, { needLogin: true });// 在 fly 攔截器中處理邏輯fly.interceptors.request.use(async (req)=>{  // 在請求需要登錄態的時候
  if (req.needLogin !== false) {    // ensureLogin 核心邏輯是:判斷是否已登錄,如否發起登錄調用,如果正在登錄,則進入隊列等待回調。
    await session.ensureLogin();    
    // 登錄成功后,獲取 token,通過 headers 傳遞給后端。
    const token = await session.getToken();    Object.assign(req.headers, { [AUTH_KEY_NAME]: token });
  }  
  return req;
});復制代碼

3.1.3 自定義登錄態過期的容錯處理

當自定義登錄態過期的時候,后端需要返回特定的狀態碼,例如:AUTH_EXPIRED 、 AUTH_INVALID 等。

前端可以在「網絡請求層」去監聽所有請求的這個狀態碼,然后發起刷新登錄態,再去重放失敗的請求:

// 添加響應攔截器fly.interceptors.response.use(    (response) => {      const code = res.data;        
      // 登錄態過期或失效
      if ( ['AUTH_EXPIRED', 'AUTH_INVALID'].includes(code) ) {      
        // 刷新登錄態
        await session.refreshLogin();        
        // 然后重新發起請求
        return fly.request(request);
      }
    }
)復制代碼

那么如果并發的發起多個請求,都返回了登錄態失效的狀態碼,上述代碼就會被執行多次。

我們需要對 session.refreshLogin() 做一些特殊的容錯處理:

  1. 請求鎖:同一時間,只允許一個正在過程中的網絡請求。

  2. 等待隊列:請求被鎖定之后,調用該方法的所有調用,都推入一個隊列中,等待網絡請求完成之后共用返回結果。

  3. 熔斷機制:如果短時間內多次調用,則停止響應一段時間,類似于 TCP 慢啟動。

示例代碼:

class Session {  // ....
  
  // 刷新登錄保險絲,最多重復 3 次,然后熔斷,5s 后恢復
  refreshLoginFuseLine = REFRESH_LOGIN_FUSELINE_DEFAULT;
  refreshLoginFuseLocked = false;
  refreshLoginFuseRestoreTime = 5000;  // 熔斷控制
  refreshLoginFuse(): Promise<void> {    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('刷新登錄-保險絲熔斷!!');
    }
  }  // 并發回調隊列
  refreshLoginQueueMaxLength = 100;
  refreshLoginQueue: any[] = [];
  refreshLoginLocked = false;  // 刷新登錄態
  refreshLogin(): Promise<void> {    return Promise.resolve()    
      // 回調隊列 + 熔斷 控制
      .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;
      })      // 通過前置控制之后,發起登錄過程
      .then(() => {        this.clearSession();
        wx.showLoading({ title: '刷新登錄態中', 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;
          });
      });  // ...}復制代碼

3.1.4 微信 session_key 過期的容錯處理

我們從上面的「靜默登錄」之后,微信服務器端會下發一個 session_key 給后端,而這個會在需要獲取微信開放數據的時候會用到。

微信小程序登錄前端設計與實現的示例分析

session_key 是有時效性的,以下摘自微信官方描述:

會話密鑰 session_key 有效性

開發者如果遇到因為 session_key 不正確而校驗簽名失敗或解密失敗,請關注下面幾個與 session_key 有關的注意事項。

  1. wx.login 調用時,用戶的 session_key 可能會被更新而致使舊 session_key 失效(刷新機制存在最短周期,如果同一個用戶短時間內多次調用 wx.login,并非每次調用都導致 session_key 刷新)。開發者應該在明確需要重新登錄時才調用 wx.login,及時通過 auth.code2Session 接口更新服務器存儲的 session_key。

  2. 微信不會把 session_key 的有效期告知開發者。我們會根據用戶使用小程序的行為對 session_key 進行續期。用戶越頻繁使用小程序,session_key 有效期越長。

  3. 開發者在 session_key 失效時,可以通過重新執行登錄流程獲取有效的 session_key。使用接口 wx.checkSession可以校驗 session_key 是否有效,從而避免小程序反復執行登錄流程。

  4. 當開發者在實現自定義登錄態時,可以考慮以 session_key 有效期作為自身登錄態有效期,也可以實現自定義的時效性策略。

翻譯成簡單的兩句話:

  1. session_key 時效性由微信控制,開發者不可預測。

  2. wx.login 可能會導致 session_key 過期,可以在使用接口之前用 wx.checkSession 檢查一下。

而對于第二點,我們通過實驗發現,偶發性的在 session_key 已過期的情況下,wx.checkSession 會概率性返回 true

社區也有相關的反饋未得到解決:

  • 小程序解密手機號,隔一小段時間后,checksession:ok,但是解密失敗

  • wx.checkSession有效,但是解密數據失敗

  • checkSession判斷session_key未失效,但是解密手機號失敗

所以結論是:wx.checkSession可靠性是不達 100% 的。

基于以上,我們需要對 session_key 的過期做一些容錯處理:

  1. 發起需要使用 session_key 的請求前,做一次 wx.checkSession 操作,如果失敗了刷新登錄態。

  2. 后端使用 session_key 解密開放數據失敗之后,返回特定錯誤碼(如:DECRYPT_WX_OPEN_DATA_FAIL),前端刷新登錄態。

示例代碼:

// 定義檢查 session_key 有效性的操作const ensureSessionKey = async () => {  const hasSession = await new Promise(resolve => {
    wx.checkSession({      success: () => resolve(true),      fail: () => resolve(false),
    });
  });  
  if (!hasSession) {
    logger.info('sessionKey 已過期,刷新登錄態');    // 接上面提到的刷新登錄邏輯
    return session.refreshLogin();
  }  return Promise.resolve();
}// 在發起請求的時候,先做一次確保 session_key 最新的操作(以 fly.js 作為網絡請求層為例)const updatePhone = async (params) => {  await ensureSessionKey();  const res = await fly.post('https://xxx', params);
}// 添加響應攔截器, 監聽網絡請求返回fly.interceptors.response.use(    (response) => {      const code = res.data;        
      // 登錄態過期或失效
      if ( ['DECRYPT_WX_OPEN_DATA_FAIL'].includes(code)) {        // 刷新登錄態
        await session.refreshLogin();        
        // 由于加密場景的加密數據由用戶點擊產生,session_key 可能已經更改,需要用戶重新點擊一遍。
        wx.showToast({ title: '網絡出小差了,請稍后重試', icon: 'none' });
      }
    }
)

3.2 授權的實現

3.2.1 組件拆分與設計

在用戶信息和手機號獲取的方式上,微信是以 <button open-type='xxx' /> 的方式,讓用戶主動點擊授權的。

那么為了讓代碼更解耦,我們設計這樣三個組件:

  1. <user-contaienr getUserInfo="onUserInfoAuth">: 包裝點擊交互,通過 <slot> 支持點擊區域的自定義UI。

  2. <phone-container getPhonenNmber="onPhoneAuth"> : 與 <user-container> 同理。

  3. <auth-flow>: 根據業務需要,組合 <user-container>、<phone-container> 組合來定義不同的授權流程。

以開頭的業務場景的流程為例,它有這樣的要求:

  1. 有多個步驟。

  2. 如果中途斷掉了,可以從中間接上。

  3. 有些場景中,只要求達到「用戶信息授權」,而不需要完成「用戶手機號」。

微信小程序登錄前端設計與實現的示例分析

那么授權的階段可以分三層:

// 用戶登錄的階段export enum AuthStep {  // 階段一:只有登錄態,沒有用戶信息,沒有手機號
  ONE = 1,  // 階段二:有用戶信息,沒有手機號
  TWO = 2,  // 階段三:有用戶信息,有手機號
  THREE = 3,
}復制代碼

AuthStep 的推進過程是不可逆的,我們可以定義一個 nextStep 函數來封裝 AuthStep 更新的邏輯。外部使用的話,只要無腦調用 nextStep 方法,等待回調結果就行。

示例偽代碼:

// auth-flow componentComponent({  // ...
  
  data: {    // 默認情況下,只需要到達階段二。
    mustAuthStep: AuthStep.TWO
  },  
  // 允許臨時更改組件的需要達到的階段。
  setMustAuthStep(mustAuthStep: AuthStep) {    this.setData({ mustAuthStep });
  },  
  // 根據用戶當前的信息,計算用戶處在授權的階段
  getAuthStep() {    let currAuthStep;    
    // 沒有用戶信息,尚在第一步
    if (!session.hasUser() || !session.hasUnionId()) {
      currAuthStep = AuthStepType.ONE;
    }    // 沒有手機號,尚在第二步
    if (!session.hasPhone()) {
      currAuthStep = AuthStepType.TWO;
    }    // 都有,尚在第三步
    currAuthStep = AuthStepType.THREE;    return currAuthStep;
  }  
  // 發起下一步授權,如果都已經完成,就直接返回成功。
  nextStep(e) {    const { mustAuthStep } = this.data;    const currAuthStep = this.updateAuthStep();  
    // 已完成授權
    if (currAuthStep >= mustAuthStep || currAuthStep === AuthStepType.THREE) {      // 更新全局的授權狀態機,廣播消息給訂閱者。
      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 });
  },  
  // ...});復制代碼

那么我們的 <auth-flow> 中就可以根據 currAuthStepmustAuthStep 來去做不同的 UI 展示。需要注意的是使用 <user-container>、<phone-container> 的時候連接上 nextStep(e) 函數。

示例偽代碼:

<view class="auth-flow">

  <!-- 已完成授權 -->
  <block wx:if="{{currAuthStep === mustAuthStep || currAuthStep === AuthStep.THREE}}">
    <view>已完成授權</view>
  </block>

  <!-- 未完成授權,第一步:授權用戶信息 -->
  <block wx:elif="{{currAuthStep === AuthStep.ONE}}">
    <user-container bind:getuserinfo="nextStep">
      <view>授權用戶信息</view>
    </user-container>
  </block>

  <!-- 未完成授權,第二步:授權手機號 -->
  <block wx:elif="{{currAuthStep === AuthStep.TWO}}">
    <phone-container bind:getphonenumber="nextStep">
      <view>授權手機號</view>
    </phone-container>
  </block>
  </view>復制代碼

3.2.2 權限攔截的處理

到這里,我們制作好了用來承載授權流程的組件 <auth-flow> ,那么接下來就是決定要使用它的時機了。

我們梳理需要授權的場景:

  1. 點擊某個按鈕,例如:購買某個商品。

    對于這種場景,常見的是通過彈窗完成授權,用戶可以選擇關閉。

    微信小程序登錄前端設計與實現的示例分析

  2. 瀏覽某個頁面,例如:訪問個人中心。

    對于這種場景,我們可以在點擊跳轉某個頁面的時候,進行攔截,彈窗處理。但這樣的缺點是,跳轉到目標頁面的地方可能會很多,每個都攔截,難免會錯漏。而且當目標頁面作為「小程序落地頁面」的時候,就避免不了。

    這時候,我們可以通過重定向到授權頁面來完成授權流程,完成之后,再回來。

    微信小程序登錄前端設計與實現的示例分析

那么我們定義一個枚舉變量:

// 授權的展示形式export enum AuthDisplayMode {  // 以彈窗形式
  POPUP = 'button',  // 以頁面形式
  PAGE = 'page',
}復制代碼

我們可以設計一個 mustAuth 方法,在點擊某個按鈕,或者頁面加載的時候,進行授權控制。

偽代碼示例:

class Session {  // ...
  
  mustAuth({
    mustAuthStep = AuthStepType.TWO, // 需要授權的LEVEL,默認需要獲取用戶資料
    popupCompName = 'auth-popup',	// 授權彈窗組件的 id
    mode = AuthDisplayMode.POPUP, // 默認以彈窗模式
  } = {}): Promise<void> {    
    // 如果當前的授權步驟已經達標,則返回成功
    if (this.currentAuthStep() >= mustAuthStep) return Promise.resolve();    // 嘗試獲取當前頁面的 <auth-popup id="auth-popup" /> 組件實例
    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,然后調用 <auth-popup> 的 nextStep 方法,進行進一步的授權。
    popupComp.setMustAuthStep(mustAuthStep);
    popupComp.nextStep();    // 等待成功回調或者失敗回調
    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(() => {      // 開始處理回調邏輯...
    });
  }
})復制代碼

當然,如果項目使用了 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) {    // 開始處理回調邏輯...
  }
});復制代碼

3.3. 前后端交互協議整理

作為一套可復用的小程序登錄方案,當然需要去定義好前后端的交互協議。

那么整套登錄流程下來,需要的接口有這么幾個:

微信小程序登錄前端設計與實現的示例分析

  1. 靜默登錄 silentLogin

    1. 后端利用 code 跟微信客戶端換取用戶標識,然后注冊并登錄用戶,返回自定義登錄態 token 給前端

    2. token 前端會存起來,每個請求都會帶上

    3. userInfo 需要包含nicknamephone字段,前端用于計算當前用戶的授權階段。當然這個狀態的記錄可以放在后端,但是我們認為放在前端,會更加靈活。

    4. token: 自定義登錄態憑證

    5. userInfo: 用戶信息

    6. code: 產自 wx.login()

    7. 入參:

    8. 出參:

    9. 說明:

  2. 更新用戶信息 updateUser

    1. 后端解密微信開放數據,獲取隱蔽數據,如:unionId

    2. 后端支持更新包括 nickname等用戶基本信息。

    3. 前端會把 userInfo 信息更新到 session 中,用于計算授權階段。

    4. userInfo:更新后的最新用戶信息

    5. nickname: 用戶昵稱

    6. encrypt: 微信開放數據相關的 iv, encryptedData

    7. 以及其他如性別地址等非必要字段

    8. 入參:

    9. 出參:

    10. 說明:

  3. 更新用戶手機號 updatePhone

    1. 后端解密開放式局,獲取手機號,并更新到用戶信息中。

    2. 前端會把 userInfo 信息更新到 session 中,用于計算授權階段。

    3. userInfo:更新后的最新用戶信息

    4. encrypt:微信開放數據相關的 iv, encryptedData

    5. 入參:

    6. 出參:

    7. 說明:

  4. 解綁手機號 unbindPhone

    1. 入參:-

    2. 出參:-

    3. 說明:后端解綁用戶手機號,成功與否,走業務定義的前后端協議。

  5. 登錄 logout

    1. 入參:-

    2. 出參:-

    3. 說明:后端主動過期登錄態,成功與否,走業務定義的前后端協議。

五. 架構圖

最后我們來梳理一下整體的「登錄服務」的架構圖:

微信小程序登錄前端設計與實現的示例分析

由「登錄服務」和「底層建設」組合提供的通用服務,業務層只需要去根據產品需求,定制授權的流程 <auth-flow> ,就能滿足大部分場景了。

六. 總結

本篇文章通過一些常見的登錄授權場景來展開來描述細節點。

整理了「登錄」、「授權」的概念。

然后分別針對「登錄」介紹了一些關鍵的技術實現:

  1. 靜默登錄

  2. 靜默登錄異步狀態的處理

  3. 自定義登錄態過期的容錯處理

  4. 微信 session_key 過期的容錯處理

而對于「授權」,會有設計UI部分的邏輯,還需要涉及到組件的拆分:

  1. 組件拆分與設計

  2. 權限攔截的處理

然后,梳理了這套登錄授權方案所依賴的后端接口,和給出最簡單的參考協議。

最后,站在「秉著沉淀一套通用的小程序登錄方案和服務為目標」的角度,梳理了一下架構層面上的分層。

  1. 業務定制層

  2. 登錄服務層

  3. 底層建設

以上是“微信小程序登錄前端設計與實現的示例分析”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

亚洲午夜精品一区二区_中文无码日韩欧免_久久香蕉精品视频_欧美主播一区二区三区美女