微信小程式 unionid 登入解決方案

LeanCloud發表於2019-04-26

第三方登入模組使開發者能快捷靈活的擁有自己的使用者系統,是 LeanCloud 最受歡迎的功能之一。隨著第三方平臺的演化,特別是微信小程式的流行,LeanCloud 第三方登入模組也一直在改進:

  • v2.0*:增加微信小程式一鍵登入功能。支援開發者不寫任何後端程式碼實現微信小程式使用者系統與 LeanCloud 使用者系統的關聯。
  • v3.6:增加 unionid 登入介面。支援開發者使用 unionid 關聯一個微信開發者帳號下的多個應用從而共享一套 LeanCloud 使用者系統。 這兩個功能各自都非常簡單可靠,但是其中重疊的部分需求卻是一個難題:「如何在小程式中支援 unionid 登入,既能得到 unionid 登入機制的靈活性,又保留一鍵登入功能的便利性」。

在最近釋出的 JavaScript SDK v3.13 中包含了微信小程式 unionid 登入支援。我們根據不同的需求設計了不同的解決方案。

* 這裡的版本指開始支援該功能的 JavaScript SDK 版本。

一鍵登入

LeanCloud 的使用者系統支援一鍵使用微信使用者身份登入。要使用一鍵登入功能,需要先設定小程式的 AppID 與 AppSecret:

1.登入 微信公眾平臺,在 設定 > 開發設定 中獲得 AppID 與 AppSecret。 前往 LeanCloud 控制檯 > 元件 > 社交,儲存「微信小程式」的 AppID 與 AppSecret。 這樣你就可以在應用中使用AV.User.loginWithWeapp()方法來使用當前使用者身份登入了。

AV.User.loginWithWeapp().then(user => {
  this.globalData.user = user;
}).catch(console.error);
複製程式碼

使用一鍵登入方式登入時,LeanCloud 會將該使用者的小程式 openidsession_key 等資訊儲存在對應的 user.authData.lc_weapp 屬性中,你可以在控制檯的 _User 表中看到:

{
  "authData": {
    "lc_weapp": {
      "session_key": "2zIDoEEUhkb0B5pUTzsLVg==",
      "expires_in": 7200,
      "openid": "obznq0GuHPxdRYaaDkPOHk785DuA"
    }
  }
}
複製程式碼

如果使用者是第一次使用此應用,呼叫登入 API 會建立一個新的使用者,你可以在 控制檯 > 儲存 中的 _User 表中看到該使用者的資訊,如果使用者曾經使用該方式登入過此應用(存在對應 openid 的使用者),再次呼叫登入 API 會返回同一個使用者。

使用者的登入狀態會儲存在客戶端中,可以使用 AV.User.current() 方法來獲取當前登入的使用者,下面的例子展示瞭如何為登入使用者儲存額外的資訊:

// 假設已經通過 AV.User.loginWithWeapp() 登入
// 獲得當前登入使用者
const user = AV.User.current();
// 呼叫小程式 API,得到使用者資訊
wx.getUserInfo({
  success: ({userInfo}) => {
    // 更新當前使用者的資訊
    user.set(userInfo).save().then(user => {
      // 成功,此時可在控制檯中看到更新後的使用者資訊
      this.globalData.user = user;
    }).catch(console.error);
  }
});
複製程式碼

authData 預設只有對應使用者可見,開發者可以使用 masterKey 在雲引擎中獲取該使用者的 openidsession_key 進行支付、推送等操作。詳情的示例請參考 支付

小程式的登入態(session_key)存在有效期,可以通過 wx.checkSession() 方法檢測當前使用者登入態是否有效,失效後可以通過呼叫 AV.User.loginWithWeapp() 重新登入。

使用 unionid

微信開放平臺使用 unionid 來區分使用者的唯一性,也就是說同一個微信開放平臺帳號下的移動應用、網站應用和公眾帳號(包括小程式),使用者的 unionid 都是同一個,而 openid 會是多個。如果你想要實現多個小程式之間,或者小程式與使用微信開放平臺登入的應用之間共享使用者系統的話,則需要使用 unionid 登入。

要在小程式中使用 unionid 登入,請先確認已經在 微信開放平臺 繫結了該小程式

在小程式中有很多途徑可以 獲取到 unionid。不同的 unionid 獲取方式,接入 LeanCloud 使用者系統的方式也有所不同。

一鍵登入時靜默獲取 unionid

當滿足以下條件時,一鍵登入 API AV.User.loginWithWeapp() 能靜默地獲取到使用者的 unionid 並用 unionid + openid 進行匹配登入。

  • 微信開放平臺帳號下存在同主體的公眾號,並且該使用者已經關注了該公眾號。
  • 微信開放平臺帳號下存在同主體的公眾號或移動應用,並且該使用者已經授權登入過該公眾號或移動應用。 要啟用這種方式,需要在一鍵登入時指定引數 preferUnionId 為 true:
AV.User.loginWithWeapp({
  preferUnionId: true,
});
複製程式碼

使用 unionid 登入後,使用者的 authData 中會增加 _weixin_unionid 一項(與 lc_weapp 平級):

{
  "authData": {
    "lc_weapp": {
      "session_key": "2zIDoEEUhkb0B5pUTzsLVg==",
      "expires_in": 7200,
      "openid": "obznq0GuHPxdRYaaDkPOHk785DuA",
      "unionid": "ox7NLs5BlEqPS4glxqhn5kkO0UUo"
    },
    "_weixin_unionid": {
      "uid": "ox7NLs5BlEqPS4glxqhn5kkO0UUo"
    }
  }
}
複製程式碼

用 unionid + openid 登入時,會按照下面的步驟進行使用者匹配:

  1. 如果已經存在對應 unionid(authData._weixin_unionid.uid)的使用者,則會直接作為這個使用者登入,並將所有資訊(openidsession_keyunionid 等)更新到該使用者的 authData.lc_ewapp 中。
  2. 如果不存在匹配 unionid 的使用者,但存在匹配 openid(authData.lc_weapp.openid)的使用者,則會直接作為這個使用者登入,並將所有資訊(session_keyunionid 等)更新到該使用者的 authData.lc_ewapp 中,同時將 unionid 儲存到 authData._weixin_unionid.uid 中。
  3. 如果不存在匹配 unionid 的使用者,也不存在匹配 openid 的使用者,則建立一個新使用者,將所有資訊(session_keyunionid 等)更新到該使用者的 authData.lc_ewapp 中,同時將 unionid 儲存到 authData._weixin_unionid.uid 中。

不管匹配的過程是如何的,最終登入使用者的 authData 都會是上面這種結構。

LeanTodo Demo 便是使用這種方式登入的,如果你已經關注了其關聯的公眾號(搜尋 AVOSCloud,或通過小程式關於頁面的相關公眾號連結訪問),那麼你在登入後會在 LeanTodo Demo 的 設定 - 使用者 頁面看到當前使用者的 authData 中已經繫結了 unionid。

微信小程式 unionid 登入解決方案

微信掃描二維碼進入 Demo

需要注意的是:

  • 如果使用者不符合上述靜默獲取 unionid 的條件,那麼就算指定了 preferUnionId 也不會使用 unionid 登入。
  • 如果使用者符合上述靜默獲取 unionid 的條件,但沒有指定 preferUnionId,那麼該次登入不會使用 unionid 登入,但仍然會將獲取到的 unionid 作為一般欄位寫入該使用者的 authData.lc_weapp 中。此時使用者的 authData 會是這樣的:
{
  "authData": {
    "lc_weapp": {
      "session_key": "2zIDoEEUhkb0B5pUTzsLVg==",
      "expires_in": 7200,
      "openid": "obznq0GuHPxdRYaaDkPOHk785DuA",
      "unionid": "ox7NLs5BlEqPS4glxqhn5kkO0UUo"
    }
  }
}
複製程式碼
通過其他方式獲取 unionid 後登入

如果開發者自行獲得了使用者的 unionid(例如通過解密 wx.getUserInfo 獲取到的使用者資訊),可以在小程式中呼叫 AV.User.loginWithWeappWithUnionId() 投入 unionid 完成登入授權:

AV.User.loginWithWeappWithUnionId(unionid, {
  asMainAccount: true
}).then(console.log, console.error);
複製程式碼
通過其他方式獲取 unionid 與 openid 後登入

如果開發者希望更靈活的控制小程式的登入流程,也可以自行在服務端實現 unionid 與 openid 的獲取,然後呼叫通用的第三方 unionid 登入介面指定平臺為 lc_weapp 來登入:

const unionid = '';
const authData = {
  openid: '',
  session_key: ''
};
const platform = 'lc_weapp';
AV.User.loginWithAuthDataAndUnionId(authData, platform, unionid, {
  asMainAccount: true
}).then(console.log, console.error);
複製程式碼

相對上面提到的一些 Weapp 相關的登入 API,loginWithAuthDataAndUnionId 是更加底層的第三方登入介面,不依賴小程式執行環境,因此這種方式也提供了更高的靈活度:

  • 可以在服務端獲取到 unionid 與 openid 等資訊後返回給小程式客戶端,在客戶端呼叫 AV.User.loginWithAuthDataAndUnionId 來登入。
  • 也可以在服務端獲取到 unionid 與 openid 等資訊後直接呼叫 AV.User.loginWithAuthDataAndUnionId 登入,成功後得到登入使用者的 sessionToken 後返回給客戶端,客戶端再使用該 sessionToken 直接登入。
關聯第二個小程式

這種用法的另一種常見場景是關聯同一個開發者帳號下的第二個小程式。

因為一個 LeanCloud 應用預設關聯一個微信小程式(對應的平臺名稱是 lc_weapp),使用小程式系列 API 的時候也都是預設關聯到 authData.lc_weapp 欄位上。如果想要接入第二個小程式,則需要自行獲取到 unionid 與 openid,然後將其作為一個新的第三方平臺登入。這裡同樣需要用到 AV.User.loginWithAuthDataAndUnionId 方法,但與關聯內建的小程式平臺(lc_weapp)有一些不同:

  • 需要指定一個新的 platform
  • 需要將 openid 儲存為 uid(內建的微信平臺做了特殊處理可以直接用 openid 而這裡是作為通用第三方 OAuth 平臺儲存因此需要使用標準的 uid 欄位)。

這裡我們以新的平臺 weapp2 為例:

const unionid = '';
const openid = '';
const authData = {
  uid: openid,
  session_key: ''
};
const platform = 'weapp2';
AV.User.loginWithAuthDataAndUnionId(authData, platform, unionid, {
  asMainAccount: true
}).then(console.log, console.error);
複製程式碼
獲取 unionid 後與現有使用者關聯

如果一個使用者已經登入,現在通過某種方式獲取到了其 unionid(一個常見的使用場景是使用者完成了支付操作後在服務端通過 getPaidUnionId 得到了 unionid)希望與之關聯,可以在小程式中使用 AV.User#associateWithWeappWithUnionId()

const user = AV.User.current(); // 獲取當前登入使用者
user.associateWithWeappWithUnionId(unionid, {
  asMainAccount: true
}).then(console.log, console.error);
複製程式碼
啟用其他登入方式

上述的登入 API 對接的是小程式的使用者系統,所以使用這些 API 建立的使用者無法直接在小程式之外的平臺上登入。如果需要使用 LeanCloud 使用者系統提供的其他登入方式,如用手機號驗證碼登入、郵箱密碼登入等,在小程式登入後設定對應的使用者屬性即可:

// 小程式登入
AV.User.loginWithWeapp().then(user => {
  // 設定並儲存手機號
  user.setMobilePhoneNumber('13000000000');
  return user.save();
}).then(user => {
  // 傳送驗證簡訊
  return AV.User.requestMobilePhoneVerify(user.getMobilePhoneNumber());
}).then({
  // 使用者填寫收到簡訊驗證碼後再呼叫 AV.User.verifyMobilePhone(code) 完成手機號的繫結
  // 成功後使用者的 mobilePhoneVerified 欄位會被置為 true
  // 此後使用者便可以使用手機號加動態驗證碼登入了
}).catch(console.error);
複製程式碼

驗證手機號碼功能要求在 控制檯 > 儲存 > 設定 > 使用者賬號 啟用「使用者註冊時,向註冊手機號碼傳送驗證簡訊」。

繫結現有使用者

如果你的應用已經在使用 LeanCloud 的使用者系統,或者使用者已經通過其他方式註冊了你的應用(比如在 Web 端通過使用者名稱密碼註冊),可以通過在小程式中呼叫 AV.User#associateWithWeapp() 來關聯已有的賬戶:

// 首先,使用使用者名稱與密碼登入一個已經存在的使用者
AV.User.logIn('username', 'password').then(user => {
  // 將當前的微信使用者與當前登入使用者關聯
  return user.associateWithWeapp();
}).catch(console.error);
複製程式碼

更多內容歡迎檢視《在微信小程式與小遊戲中使用 LeanCloud》

相關文章