基于(yú)一個(gè)好玩兒的(de)聊天室理解小程序 session 管理
發表時(shí)間:2021-3-31
發布人(rén):融晨科技
浏覽次數:97
panda-chat-room
小程序版 websocket 聊天室。 從服務器到(dào)小程序客戶端配置基礎教程。
在(zài)本教程内我們将在(zài)小程序内實現一個(gè)基本的(de) websocket 聊天室, 計劃實現以(yǐ)下功能:
- 微信用戶登錄「 小程序 session 管理 」:ballot_box_with_check:
- 用戶間文本交流 「 websocket 實現 」:ballot_box_with_check:
- 用戶間發送圖片等富媒體信息 「 文件的(de)儲存及相關邏輯 」:ballot_box_with_check:
小程序已挂,原因是(shì)個(gè)人(rén)開發者無法提交信息交流類小程序, 不(bù)過在(zài)本地(dì / de)運行 demo 還是(shì)沒問題的(de)。
寫的(de)有纰漏的(de)地(dì / de)方還請大(dà)家指出(chū),在(zài) SF 下留言或在(zài) 本項目 git 内提 issue ,我們一起進步 ^o^
聊天室基礎配置
小程序端的(de)聊天室信息流其實非常簡單, 而(ér)本教程就(jiù)借助一個(gè)好玩兒的(de)小程序聊天室來(lái)進一步理解小程序中的(de) session 實現。
我在(zài)服務器端環境搭建及配置主要(yào / yāo)參考騰訊雲實驗 基于(yú) CentOS 搭建微信小程序服務
我們在(zài)此先要(yào / yāo)理解小程序端爲(wéi / wèi)何無法實現 session, 以(yǐ)及如何在(zài)小程序實現 websocket 通信。
小程序并非嵌套在(zài)微信内的(de) html5 網頁, 它并不(bù)是(shì)從 url 訪問到(dào)的(de)。 我們隻能自己實現類似會話的(de)東西, 好在(zài)官方已經提供了(le/liǎo)相應的(de)套件來(lái)實現 session。 即 wafer-client-sdk 和(hé / huò) node 中間件 wafer-node-session , 我們依照文檔就(jiù)能簡單地(dì / de)實現 session。
騰訊雲 wafer 項目下有很多相似項目「大(dà)部分需要(yào / yāo)配合騰訊雲進行一鍵部署」, 如果我們隻需要(yào / yāo)實現小程序 session 管理的(de)話, wafer-client-sdk 和(hé / huò) node 中間件 wafer-node-session 即可。
在(zài)服務器端我們使用了(le/liǎo) ws 包來(lái)實現 websocket ,沒有使用 socket.io 的(de)原因是(shì) socket.io 需要(yào / yāo)客戶端有額外的(de)腳本才能實現通信。
在(zài)小程序端我們引入 wafer-client-sdk 套件使服務器可以(yǐ)獲取 session。
主要(yào / yāo)邏輯分爲(wéi / wèi)幾個(gè)簡單函數, 當然你需要(yào / yāo)先配置請求的(de)服務器域名和(hé / huò)小程序賬号密碼。
// 引入 session 套件, 裏面封裝了(le/liǎo) wx.login, wx.getUserInfo 等操作
const wafer = require('../../vendors/wafer-client-sdk/index')
// 用于(yú)登錄使服務器獲得 session, 然後服務器返回的(de) session 裏就(jiù)會包含用戶信息了(le/liǎo), 用來(lái)在(zài) websocket 裏返回發信息用戶的(de)頭像 url
function login(){
.....
}
// 用于(yú)有新信息時(shí)更新數據, msg 指信息, ad 指 websocket 傳回的(de)信息 id, 用于(yú) scroll-into-view 滾動
pushMsg(msg, ad) {
.....
}
// 用于(yú)監聽 websocket 連接
listen(){
.....
}
// 用于(yú)小程序發送 websocket 信息
send(){
.....
}
基本就(jiù)是(shì)這(zhè)些, 關于(yú) websocket 通信過程是(shì)這(zhè)樣的(de):
- 客戶端發送信息給服務器 m1
- 服務器收到(dào)信息後根據條件返回給客戶端 m2
- 每個(gè)客戶端收到(dào) m2 後更新視圖
當然最開始是(shì)要(yào / yāo)與服務器端 websocket 連接的(de), 隻有每個(gè)連接了(le/liǎo)的(de)客戶端才可以(yǐ)交流信息。
小程序 session 解析
對于(yú) session 的(de)實現我們在(zài)服務器端使用了(le/liǎo) wafer-node-session 即爲(wéi / wèi)連接提供 session 能力。 在(zài)小程序端我們配套使用了(le/liǎo) wafer-client-sdk , 這(zhè)裏面封裝了(le/liǎo) wx.request、 wx.login 等邏輯, 實現了(le/liǎo)小程序端的(de)用戶登錄、session 設置。
關于(yú)小程序端的(de) session 獲取問題主要(yào / yāo)有如下幾個(gè)步驟
- wx.login 獲取 code
- wx.request 發送 code 給自己的(de)服務器
- 服務器收到(dào) code 配合 appId 和(hé / huò) appSecret 發送給微信服務器換取 openId 和(hé / huò) sessionKey
- wx.getUserInfo 會得到(dào) rawData、signature、encryptedData、 iv, 我們需要(yào / yāo)把它們發送到(dào)自己服務器。 我們構建自己的(de) signature2 = sha1(sessionKey + rawData) , 比對 signature 和(hé / huò) signature2 就(jiù)完成了(le/liǎo)數據校驗
- 服務端通過 aes-128-cbc 算法對稱解密 encryptedData 和(hé / huò) iv 然後得到(dào) userInfo 這(zhè)次得到(dào)的(de) userInfo 裏還包含 openId 等信息 「如果在(zài)微信開放平台綁定小程序就(jiù)會得到(dào) unionId」
- 服務端構建 req.session 對象并返回給小程序,裏面包含 id、 userInfo、 sessionKey「小程序傳到(dào)服務器的(de)」、skey 「服務器自己根據sessionKey + appId + appSecret 生成, 有過期時(shí)間」。 而(ér)我們自己生成的(de) skey 是(shì)有設置過期時(shí)間的(de), 但小程序端的(de) session 也(yě)有自己的(de)過期時(shí)間 「應該是(shì)微信按使用小程序的(de)頻率來(lái)動态設置過期時(shí)間的(de)。 wafer 會自動調用 wx.checkSession 檢查是(shì)否過期, 過期了(le/liǎo)就(jiù) wx.login」。
在(zài)我們的(de) demo 中就(jiù)出(chū)現了(le/liǎo)服務器 session 已經過期而(ér)本地(dì / de) session 還沒過期的(de)情況。 而(ér) websocket 每次發送信息都需要(yào / yāo)從 req.session 内獲取用戶頭像, 所以(yǐ)會導緻 websocket 連接失敗。 但是(shì)在(zài)小程序端 session 未過期,即在(zài)服務器端的(de) sessionKey 和(hé / huò)小程序的(de) sessionKey 不(bù)一緻了(le/liǎo) 「客戶端 sessionKey 還在(zài)而(ér)服務器的(de) sessionKey 已經過期銷毀」, 導緻比對失敗。 那怎麽辦呢? 重新請求呗! 但是(shì)因爲(wéi / wèi) wafer 封裝了(le/liǎo) session 管理 「小程序端 session 過期後才會重新請求」 存在(zài) session 緩存的(de)緣故, 小程序并沒有重新發送信息給自己的(de)服務器進而(ér)生成新的(de) sessionKey, 所以(yǐ)我們在(zài)每一次 wx.sendSocketMessage 發信息的(de)時(shí)候都要(yào / yāo)檢查服務器端的(de) session 情況, 這(zhè)裏需要(yào / yāo)做簡單的(de)判斷「websocket 信息有錯誤就(jiù)清除本地(dì / de) session」讓小程序重新請求服務器。
websocket 信息發送
既然要(yào / yāo)發送信息「即産生數據」, 那麽這(zhè)些信息都儲存在(zài)哪裏呢? 在(zài)發送文本信息時(shí), 服務器端收到(dào)數據後隻做簡單地(dì / de)處理便返回給小程序, 這(zhè)時(shí)的(de)數據應該是(shì)儲存在(zài)服務器内存中。 因爲(wéi / wèi) websocket 在(zài)收到(dào)請求後簡單處理了(le/liǎo)字符串信息直接返回給小程序, 那我們發送其它富媒體信息時(shí),也(yě)可以(yǐ)以(yǐ)二進制的(de)方式發送給 websocket 服務器, 然後重新返回給客戶端 「即 websocket 隻做文件中轉」,相關實現 websocket-stream 。 貌似看起來(lái)很複雜,在(zài)這(zhè)裏我使用了(le/liǎo)國(guó)内的(de) paas 服務商 leanCloud 的(de)儲存服務 「即小程序端把發送的(de)文件儲存在(zài)雲端,返回一個(gè)文件地(dì / de)址」,然後我們把這(zhè)個(gè)文件信息進行标注「即隻發送文件的(de) url 信息, 小程序端判斷請求是(shì)否是(shì)文件進而(ér)顯示」。 當然你也(yě)可以(yǐ)發送視頻或者音頻, 把他(tā)們都保存在(zài)雲端, 隻發送其相應的(de) url 即可。 我們這(zhè)裏的(de) websocket 服務器隻做一個(gè)文件中轉的(de)功能, 而(ér)文件的(de)存儲交給雲端來(lái)負責。
panda-chat-room 項目源碼
項目源碼 :https://github.com/hiscc/panda-chat-room