小程序可以通過微信官方提供的登錄能力方便地獲取微信提供的用戶身份標識openid,快速建立小程序內的用戶體系。
登錄流程時序:
1、首先,調用 wx.login獲取code ,判斷用戶是否授權讀取用戶信息,調用wx.getUserInfo讀取用戶數據 。
2、然后,由於小程序后台授權域名無法授權微信的域名,所以我們只能通過我們自己的服務器去調用微信服務器去獲取用戶信息。
3、然后,故我們將wx.login獲取code 和 wx.getUserInfo 獲取的encryptedData與iv 通過wx.request 請求傳入后台。
4、然后,調用接口獲取登錄憑證(code)進而換取用戶登錄態信息,包括用戶的唯一標識(openid) 及本次登錄的 會話密鑰(session_key)。
5、然后,用戶數據的加解密通訊需要依賴會話密鑰完成,code 換取 session_key,這是一個 HTTPS 接口,開發者服務器使用登錄憑證 code 獲取 session_key 和 openid。
6、最后,其中 session_key 是對用戶數據進行加密簽名的密鑰。為了自身應用安全,session_key 不應該在網絡上傳輸。
該圖中,“小程序”指的就是我們使用小程序框架寫的代碼部分,“第三方服務器”一般就是我們自己的后台服務程序,“微信服務器”是微信官方的API服務器。
下面我們來逐步分解一下這個流程圖。
步驟一:在客戶端獲取當前登錄微信用戶的登錄憑證(code)
在小程序中登錄的第一步,就是先獲取登錄憑證。我們可以使用wx.login()
方法並得到一個登錄憑證。
我們可以在小程序的App代碼中發起登錄憑證請求,也可以在其他任何Page頁面代碼中發起登錄憑證請求,主要根據你小程序的實際需要。
步驟二:將登錄憑證發往你的服務端,並在你的服務端使用該憑證向微信服務器換取該微信用戶的唯一標識(openid)和會話密鑰(session_key)
首先,我們使用wx.request()方法,請求我們自己實現的一個后台API,並將登錄憑證(code)攜帶過去,例如在我們前面代碼的基礎上增加:
你的后台服務接着需要使用這個傳遞過來的登錄憑證(code),去調用微信接口換取openid和session_key
我們先來介紹下openid,用過公眾號的童鞋應該對這個標識都不陌生了,在公眾平台里,用來標識每個用戶在訂閱號、服務號、小程序這三種不同應用的唯一標識,也就是說每個用戶在每個應用的openid都是不一致的,所以在小程序里,我們可以用openid來標識用戶的唯一性。
那么session_key是用來干嘛的呢?有了用戶標識,我們就需要讓該用戶進行登錄,那么 session_key 就保證了當前用戶進行會話操作的有效性,這個session_key是微信服務端給我們派發的。也就是說,我們可以用這個標識來間接地維護我們小程序用戶的登錄態,那么這個session_key是怎么拿到的呢?我們需要在自己的服務端請求微信提供的第三方接口 https://api.weixin.qq.com/sns/jscode2session
從這幾個參數,我們可以看出,要請求這個接口必須先調用wx.login()來獲取到用戶當前會話的code。那么為什么我們要在服務端來請求這個接口呢?其實是出於安全性的考量,如果我們在前端通過request調用此接口,就不可避免的需要將我們小程序的appid和小程序的secret暴露在外部,同時也將微信服務端下發的session_key暴露給“有心之人”,這就給我們的業務安全帶來極大的風險。除了需要在服務端進行session_key的獲取,我們還需要注意兩點:
- session_key和微信派發的code是一一對應的,同一code只能換取一次session_key。每次調用
wx.login()
,都會下發一個新的code和對應的session_key,為了保證用戶體驗和登錄態的有效性,開發者需要清楚用戶需要重新登錄時才去調用wx.login()
- session_key是有失效性的,即便是不調用wx.login,session_key也會過期,過期時間跟用戶使用小程序的頻率成正相關,但具體的時間長短開發者和用戶都是獲取不到的
步驟三:通過不可逆的哈希算法將session_key生成登錄態標識3rd_session(應該就是token)
前面說過通過 session_key 來“間接”地維護登錄態,所謂間接,也就是我們需要 自己維護用戶的登錄態信息 ,這里也是考慮到安全性因素,如果直接使用微信服務端派發的session_key來作為業務方的登錄態使用,會被“有心之人”用來獲取用戶的敏感信息,比如wx.getUserInfo()
這個接口呢,就需要session_key來配合解密微信用戶的敏感信息。
那么我們如果生成自己的登錄態標識呢,這里可以使用幾種常見的不可逆的哈希算法,比如md5、sha1等,將生成后的登錄態標識(這里我們統稱為'skey')返回給前端,並在前端維護這份登錄態標識(一般是存入storage)。而在服務端呢,我們會把生成的skey存在用戶對應的數據表中,前端通過傳遞skey來存取用戶的信息。
步驟四:在客戶端的本地storage保存Session ID(應該就是3rd_session,即token)
開發Web應用的時候,在客戶端(瀏覽器)中,我們通常將Session ID存放在cookie中,但是小程序沒有cookie機制,所以不能采用cookie了,但是小程序有本地的storage,所以我們可以使用storage來保存Session ID,以供后續的后台API調用所使用。
在之后,調用那些需要登錄后才有權限訪問的后台服務時,你可以將保存在storage中的Session ID取出並攜帶在請求中(可以放在header中攜帶,也可以放在querystring中,或是放在body中,根據你自己的需要來使用),傳遞到后台服務,后台代碼中獲取到該Session ID后,從redis中查找是否有該Session ID存在,存在的話,即確認該session是有效的,繼續后續的代碼執行,否則進行錯誤處理。
前面我們將skey存入前端的storage里,每次進行用戶數據請求時會帶上skey,那么如果此時session_key過期呢?所以我們需要調用到wx.checkSession()
這個API來校驗當前session_key是否已經過期,這個API並不需要傳入任何有關session_key的信息參數,而是微信小程序自己去調自己的服務來查詢用戶最近一次生成的session_key是否過期。如果當前session_key過期,就讓用戶來重新登錄,更新session_key,並將最新的skey存入用戶數據表中。
步驟五:支持emoji表情存儲
如果需要將用戶微信名存入數據表中,那么就確認數據表及數據列的編碼格式。因為用戶微信名可能會包含emoji圖標,而常用的UTF8編碼只支持1-3個字節,emoji圖標剛好是4個字節的編碼進行存儲。
代碼如下:
//app.js App({ onLaunch: function () { // 登錄 wx.login({ success: res => { // 在客戶端通過wx.login()方法獲取res.code // 發送 res.code 到后台換取 openId 驗證平台賬號是否登錄綁定過 var that = this; wx.request({ // 你的服務器攜帶res.code去請求微信服務器獲取openId和session_key method: 'GET', url: this.globalData.serverApi + "/mobileApi/user/checkBind?code="+res.code, header: { 'content-type': 'application/json' }, success (res) { if(res.data.code == 301){ //未登錄 var openId = res.data.openId; wx.reLaunch({ url: '/pages/login/login?openId='+openId }) }else if(res.data.code == 1){ //已登錄 that.globalData.userInfo = res.data.userInfo; that.globalData.token = res.data.token; var menuList = res.data.menuList; wx.setStorageSync('menuList', menuList); }else if(res.data.code == 0){ //獲取openId失敗 wx.showToast({ title: res.data.msg, icon: 'none', duration: 2000 }) } // 由於 checkBind 是網絡請求,可能會在 Page.onLoad 之后才返回 // 所以此處加入 callback 以防止這種情況 if (that.checkBindCallback) { that.checkBindCallback(res) } } }) } }) })