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