微信小程序 unionid 登錄解決方案

第三方登錄模塊使開發者能快捷靈活的擁有自己的用戶系統,是 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。

微信掃描二維碼進入 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》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章