前段時(shí)間折騰過一下小程序,對(duì)登錄態(tài)控制進(jìn)行了一些嘗試和總結(jié),此文之前在公眾號(hào)進(jìn)行過分享,回頭看看,對(duì)目前也有一定的指導(dǎo)意義,所以在掘金舊文重發(fā),希望對(duì)掘友們有所幫助一、微信建議的登錄態(tài)控制圖片來自微信 ...
前段時(shí)間折騰過一下小程序,對(duì)登錄態(tài)控制進(jìn)行了一些嘗試和總結(jié),此文之前在公眾號(hào)進(jìn)行過分享,回頭看看,對(duì)目前也有一定的指導(dǎo)意義,所以在掘金舊文重發(fā),希望對(duì)掘友們有所幫助
說明:
1)小程序內(nèi)通過wx.login接口獲得code
2)將code傳入后臺(tái),后臺(tái)對(duì)微信服務(wù)器發(fā)起一個(gè)https請(qǐng)求換取openid、session_key
3)后臺(tái)生成一個(gè)自身的3rd_session(以此為key值保持openid和session_key),返回給前端。 PS:微信方的openid和session_key并沒有發(fā)回給前端小程序
4)小程序拿到3rd_session之后保持在本地
5)小程序請(qǐng)求登錄區(qū)內(nèi)接口,通過wx.checkSession檢查登錄態(tài),如果失效重新走上述登錄流程,否則待上3rd_session到后臺(tái)進(jìn)行登錄驗(yàn)證
不是我反骨,而是我的微信小程序不需要獲取什么私密數(shù)據(jù),用不到session_key,只需要一個(gè)openid,微信特別強(qiáng)調(diào)了, 為了自身應(yīng)用安全,session_key 不應(yīng)該在網(wǎng)絡(luò)上傳輸 ,可沒說不可以傳輸openid,那么如果我將openid直接返回給前端小程序,會(huì)不會(huì)方便很多?下面我們來具體分析下:
永不過期的openid:
要知道,session_key有過期時(shí)間,必須適時(shí)重新獲取,而針對(duì)每一個(gè)小程序,唯一標(biāo)識(shí)用戶的openid可不會(huì)過期,如果只在用戶第一次登錄的時(shí)候,通過后臺(tái)請(qǐng)求到openid,小程序緩存到本地,之后每次請(qǐng)求都以這個(gè)openid作為唯一憑證,豈不是一本萬利~~
事實(shí)上,上面的做法忽略了一個(gè)致命的問題,手機(jī)上是可以切換微信賬戶的,如果手機(jī)I上原先登錄了賬戶A,已經(jīng)保存了用戶A的openid,有一天手機(jī)I上切換到了賬戶B上,小程序檢測(cè)到openid存在,并不會(huì)重新獲取openid,那么賬戶B就請(qǐng)求到了賬戶A的數(shù)據(jù),這就造成數(shù)據(jù)亂象了
登錄態(tài)過期后重新獲取openid:
上述的問題并不能打消我使用openid作為登錄憑證的念頭,只需要稍作改進(jìn),數(shù)據(jù)就不會(huì)亂竄:每次進(jìn)入小程序通過wx.checkSession檢測(cè)登錄是否失效,如果失效重新獲取openid,要知道,手機(jī)切換了登錄賬號(hào),登錄態(tài)一定會(huì)過期,這樣雖不能 一本萬利 ,但也足夠省心。
這個(gè)時(shí)候應(yīng)該有一個(gè)然而,以永不過期的openid作為登錄憑證,并不是明智之舉,一旦被人截獲,就再也沒有翻身的機(jī)會(huì)了,而維護(hù)一個(gè)第三發(fā)的session,至少擁有有效期,這在很大程度上增加了安全性。并且,現(xiàn)在不需要使用session_key,不代表以后,保持系統(tǒng)的可擴(kuò)展性,才能以不變應(yīng)萬變
事實(shí)證明,我還是應(yīng)該接受它的建議
維護(hù)3rd_session需要一個(gè)內(nèi)存數(shù)據(jù)庫,這里我選用了redis
維護(hù)會(huì)話態(tài)是內(nèi)存數(shù)據(jù)庫的典型應(yīng)用場(chǎng)景,畢竟量小,并且要求速度快,這么一個(gè)小應(yīng)用,當(dāng)然也可以自己在內(nèi)存中維護(hù)一個(gè)對(duì)象來進(jìn)行會(huì)話id的處理,但是肯定難以跟一個(gè)成熟的系統(tǒng)相媲美
拋開代碼實(shí)現(xiàn),這似乎就是一句話可以概括的事情,生成一個(gè)唯一的隨機(jī)串sessionid,以此為key,openid和微信方的session_key為value存入redis,并把sessionid傳回給客戶端。
但是,翻遍小程序的官方文檔,除了一句據(jù)說的 wx.checkSession對(duì)開發(fā)者來說是透明的,并沒有小程序登錄態(tài)何時(shí)過期的具體說明,如何才能同步前后端的會(huì)話過期時(shí)間呢?
一開始,我還是試圖尋找小程序的登錄態(tài)時(shí)效
{"session_key":"...","expires_in":7200,"openid":"..."}
在code2session接口返回的數(shù)據(jù)中,有一個(gè)可疑的字段 expires_in
,它的值是7200,似乎存儲(chǔ)到redis中的sessionid設(shè)置為7200就可以同步了??墒菍?shí)踐的結(jié)果再次讓人失望,不管是設(shè)置成7200還是 60*60*24
(一天),都出現(xiàn)了小程序會(huì)話尚在有效期,而服務(wù)器端會(huì)話已經(jīng)過期的情況,這直接導(dǎo)致了小程序帶著已經(jīng)緩存的sessionid到服務(wù)器端請(qǐng)求接口,返回未登錄的情況
看來還是只有wx.checkSession才知道小程序會(huì)話啥時(shí)候過期,于是,作戰(zhàn)方案重新做了調(diào)整,如果wx.checkSession檢測(cè)到會(huì)話失效,那么帶上已經(jīng)緩存在本地的sessionid(如果有的話),重新發(fā)起登錄請(qǐng)求,后臺(tái)從code2session中拿到新的請(qǐng)求結(jié)果后,生成新的隨機(jī)sessionid并入庫reids,并且把老的sessionid移除(如果有的話)
當(dāng)然不移除也不會(huì)帶來什么功能上的影響,但是會(huì)存在兩個(gè)問題,首先,跟使用open_id作為登錄憑證一樣,舊的sessionid永不過期,其次,無用的session數(shù)據(jù)占用redis資源,會(huì)拖垮訪問性能
我給小程序的用戶數(shù)安了一個(gè)高大上的名字“脫貧致富指數(shù)”,純屬娛樂,切勿當(dāng)真
為了統(tǒng)計(jì)使用小程序的用戶數(shù),需要一個(gè)表來保存用戶數(shù)據(jù),后臺(tái)提供一個(gè)接口,讓小程序?qū)⒂脩魯?shù)據(jù)傳上來進(jìn)行一個(gè)注冊(cè)操作,當(dāng)然可以把這個(gè)功能合并到登錄接口上,每次登錄都把前端小程序獲得的微信用戶數(shù)據(jù)帶上,如果發(fā)現(xiàn)數(shù)據(jù)庫中還沒有該用戶的信息,則進(jìn)行入庫操作
不難發(fā)現(xiàn),其實(shí)只需要用戶第一次登錄的時(shí)候注冊(cè)一次就行了,所以上述方法雖然簡(jiǎn)便,但是有點(diǎn)浪費(fèi)帶寬,所以應(yīng)該額外提供一個(gè)注冊(cè)接口,登錄接口只需要返回一個(gè)用戶是否已經(jīng)注冊(cè)的標(biāo)志,讓客戶端決定是否需要獲取用戶信息,進(jìn)行注冊(cè)操作(當(dāng)然后臺(tái)也不會(huì)讓同一個(gè)用戶重復(fù)入庫)
那么問題就變成如何判斷用戶是第一次登錄了:
1)判斷登錄請(qǐng)求中有沒有帶上sessionid,如果沒帶上,肯定是第一次登錄;如果帶上了就是登錄過的用戶?不,別忘了,前面說過,用戶可能會(huì)在同一設(shè)備切換賬戶,那就有可能在登錄接口中帶上了別人sessionid,那并不能表明用戶曾經(jīng)登錄過
2)通過帶上來的sessionid從redis中拿到openid,跟在code2session新請(qǐng)求到的openid進(jìn)行比對(duì),如果一致,可以證明用戶曾經(jīng)登錄過,否則,仍需要用戶進(jìn)行注冊(cè)
時(shí)間是浪費(fèi)了那么一些,但是進(jìn)過思考摸索,代碼肯定更完備了~~
工作日 8:30-12:00 14:30-18:00
周六及部分節(jié)假日提供值班服務(wù)