前(qian)段時(shi)間折(zhe)騰過(guo)一(yi)下小程(cheng)序(xu),對(dui)(dui)登錄態(tai)控(kong)(kong)制進(jin)(jin)行(xing)了一(yi)些嘗試和總結(jie),此文(wen)之(zhi)前(qian)在(zai)公眾(zhong)號進(jin)(jin)行(xing)過(guo)分享,回頭看(kan)看(kan),對(dui)(dui)目(mu)前(qian)也有(you)(you)一(yi)定的(de)指導意義,所以在(zai)掘(jue)金舊文(wen)重發,希望對(dui)(dui)掘(jue)友們有(you)(you)所幫助一(yi)、微信(xin)(xin)建(jian)議的(de)登錄態(tai)控(kong)(kong)制圖片來自(zi)微信(xin)(xin) ...
前(qian)段時間折騰過(guo)一(yi)下(xia)小程序,對登錄態控制進行了一(yi)些嘗試和總結,此文(wen)之前(qian)在公眾(zhong)號(hao)進行過(guo)分享(xiang),回(hui)頭看(kan)看(kan),對目前(qian)也有(you)一(yi)定的指導意義(yi),所以在掘金舊文(wen)重(zhong)發,希望對掘友們(men)有(you)所幫助

說明:
1)小程序(xu)內通過(guo)wx.login接口獲得code
2)將code傳(chuan)入后臺(tai),后臺(tai)對微信服務器發起一(yi)個https請求換取openid、session_key
3)后臺生成(cheng)一個自身的3rd_session(以此為key值保持openid和(he)session_key),返回給(gei)前端(duan)。 PS:微信(xin)方的openid和(he)session_key并沒有發(fa)回給(gei)前端(duan)小程序
4)小程(cheng)序拿到3rd_session之后保(bao)持(chi)在本地
5)小程(cheng)序請求登錄區內接口,通過wx.checkSession檢(jian)查登錄態,如果失效重新走上(shang)述登錄流程(cheng),否則待上(shang)3rd_session到后臺進行登錄驗證(zheng)
不是我反骨,而是我的微信小程序不需要獲取什么私密數據,用不到session_key,只需要一個openid,微信特別強調了, 為了自身應用安全,session_key 不應該在網絡上傳輸 ,可沒說不可以(yi)傳(chuan)輸openid,那么如(ru)果我將openid直(zhi)接返回給前端小程序(xu),會不會方便(bian)很多?下面我們來具體分析(xi)下:
永不(bu)過(guo)期的openid:
要知道,session_key有過(guo)期時(shi)(shi)間(jian),必須(xu)適(shi)時(shi)(shi)重新(xin)獲(huo)取,而針對每一(yi)個(ge)小程序(xu),唯(wei)一(yi)標識用戶(hu)的openid可不會過(guo)期,如(ru)果只在(zai)用戶(hu)第一(yi)次登(deng)錄的時(shi)(shi)候,通過(guo)后(hou)臺(tai)請求到(dao)openid,小程序(xu)緩存到(dao)本(ben)地,之(zhi)后(hou)每次請求都(dou)以這個(ge)openid作為(wei)唯(wei)一(yi)憑(ping)證(zheng),豈不是一(yi)本(ben)萬(wan)利~~
事實上,上面的做法忽略了(le)一個致(zhi)命的問題(ti),手機(ji)上是可(ke)以切換微信(xin)賬(zhang)(zhang)(zhang)(zhang)戶(hu)的,如(ru)果手機(ji)I上原先登錄了(le)賬(zhang)(zhang)(zhang)(zhang)戶(hu)A,已經保存了(le)用戶(hu)A的openid,有一天手機(ji)I上切換到(dao)了(le)賬(zhang)(zhang)(zhang)(zhang)戶(hu)B上,小程序檢測(ce)到(dao)openid存在,并不會(hui)重新獲取openid,那么賬(zhang)(zhang)(zhang)(zhang)戶(hu)B就請求到(dao)了(le)賬(zhang)(zhang)(zhang)(zhang)戶(hu)A的數據(ju),這就造(zao)成(cheng)數據(ju)亂象了(le)
登錄態(tai)過期(qi)后重新獲(huo)取openid:
上述的問題并不能打消我使用openid作為登錄憑證的念頭,只需要稍作改進,數據就不會亂竄:每次進入小程序通過wx.checkSession檢測登錄是否失效,如果失效重新獲取openid,要知道,手機切換了登錄賬號,登錄態一定會過期,這樣雖不能 一本萬利 ,但也足夠(gou)省心(xin)。
這(zhe)(zhe)個時候應(ying)(ying)該有(you)(you)一(yi)個然而,以(yi)永不過期(qi)(qi)的(de)(de)openid作為登(deng)錄憑證,并不是明智之舉(ju),一(yi)旦被人截獲,就(jiu)再也沒有(you)(you)翻身(shen)的(de)(de)機(ji)會(hui)了,而維護一(yi)個第(di)三發的(de)(de)session,至少擁有(you)(you)有(you)(you)效期(qi)(qi),這(zhe)(zhe)在(zai)很大程度上增加了安全(quan)性。并且,現在(zai)不需要使用session_key,不代(dai)表以(yi)后,保持系統(tong)的(de)(de)可擴展性,才能(neng)以(yi)不變(bian)(bian)應(ying)(ying)萬(wan)變(bian)(bian)
事實證明(ming),我(wo)還是應(ying)該接受它(ta)的建議
維護3rd_session需要一個內存數據庫,這里我選用(yong)了redis
維護(hu)會話態是(shi)內存(cun)數據庫的典型應用場景(jing),畢竟量小,并且(qie)要求速度快,這么一(yi)個(ge)小應用,當然也可以(yi)自己在內存(cun)中維護(hu)一(yi)個(ge)對象來進(jin)行會話id的處理,但是(shi)肯定難以(yi)跟一(yi)個(ge)成熟的系統(tong)相媲美
拋開代碼實現,這似(si)乎就是一句(ju)話可以(yi)概(gai)括的事情(qing),生成(cheng)一個唯(wei)一的隨機串sessionid,以(yi)此為key,openid和微信方的session_key為value存(cun)入redis,并(bing)把sessionid傳回給客戶端。
但是,翻遍小程序的官方文檔,除了一句據說的 wx.checkSession對開發者來說是透明的,并(bing)沒有(you)小程序(xu)登錄態何時過(guo)(guo)期(qi)的具體(ti)說明,如(ru)何才能同步(bu)前(qian)后(hou)端(duan)的會話過(guo)(guo)期(qi)時間呢?
一開始,我還是試圖尋(xun)找(zhao)小程序(xu)的登錄態時效
{"session_key":"...","expires_in":7200,"openid":"..."}
在code2session接口返回的數據中,有一個可疑的字段 expires_in ,它的值是7200,似乎存儲到redis中的sessionid設置為7200就可以同步了。可是實踐的結果再次讓人失望,不管是設置成7200還是 60*60*24 (一天),都出現(xian)了(le)小程序會話(hua)尚在有效期(qi),而服(fu)務器(qi)端會話(hua)已經過期(qi)的情(qing)況,這直接(jie)導(dao)致了(le)小程序帶著(zhu)已經緩存的sessionid到服(fu)務器(qi)端請求(qiu)接(jie)口,返回未登錄(lu)的情(qing)況
看來還是只有wx.checkSession才知道小程序會話(hua)啥時(shi)候過期,于是,作(zuo)戰方案(an)重新(xin)做了調整,如果wx.checkSession檢測到會話(hua)失效,那么(me)帶上已(yi)經(jing)緩存在本地的(de)sessionid(如果有的(de)話(hua)),重新(xin)發(fa)起(qi)登錄(lu)請(qing)求,后臺(tai)從code2session中拿到新(xin)的(de)請(qing)求結果后,生成新(xin)的(de)隨機sessionid并入庫reids,并且把(ba)老的(de)sessionid移(yi)除(如果有的(de)話(hua))
當(dang)然不(bu)(bu)移(yi)除也(ye)不(bu)(bu)會帶來什么(me)功能上的(de)影(ying)響(xiang),但(dan)是會存(cun)在兩個問題,首先,跟使用(yong)(yong)(yong)open_id作為登(deng)錄憑證一樣,舊的(de)sessionid永不(bu)(bu)過期,其次(ci),無用(yong)(yong)(yong)的(de)session數(shu)據占用(yong)(yong)(yong)redis資源,會拖垮訪問性能
我給小程序的(de)用戶數(shu)安了(le)一(yi)個高(gao)大(da)上的(de)名字“脫貧致富指數(shu)”,純屬娛樂,切勿當真(zhen)
為了統(tong)計(ji)使用小程序的(de)用戶數(shu),需(xu)要一個表來(lai)保存用戶數(shu)據(ju),后臺提供一個接口(kou),讓(rang)小程序將用戶數(shu)據(ju)傳上(shang)來(lai)進行一個注冊操作,當然可以把這個功能合并到(dao)登(deng)錄接口(kou)上(shang),每次登(deng)錄都(dou)把前端(duan)小程序獲得(de)的(de)微信(xin)用戶數(shu)據(ju)帶(dai)上(shang),如果(guo)發現數(shu)據(ju)庫中還沒有該(gai)用戶的(de)信(xin)息(xi),則(ze)進行入庫操作
不難發現,其實只需(xu)(xu)要用(yong)戶第一(yi)次(ci)登(deng)錄(lu)的時候注(zhu)(zhu)(zhu)冊(ce)一(yi)次(ci)就行(xing)了,所以上述方法雖然(ran)簡(jian)便,但是有點浪費帶寬,所以應該額外提供一(yi)個(ge)(ge)注(zhu)(zhu)(zhu)冊(ce)接口,登(deng)錄(lu)接口只需(xu)(xu)要返回一(yi)個(ge)(ge)用(yong)戶是否(fou)已經注(zhu)(zhu)(zhu)冊(ce)的標志,讓客戶端決定是否(fou)需(xu)(xu)要獲取用(yong)戶信息(xi),進行(xing)注(zhu)(zhu)(zhu)冊(ce)操作(當然(ran)后臺也不會讓同(tong)一(yi)個(ge)(ge)用(yong)戶重復入庫)
那(nei)么問題(ti)就變成如何(he)判斷用戶是第一次登錄了(le):
1)判(pan)斷(duan)登(deng)(deng)錄(lu)(lu)請求(qiu)中(zhong)有(you)沒有(you)帶(dai)上sessionid,如果(guo)沒帶(dai)上,肯定(ding)是第一次登(deng)(deng)錄(lu)(lu);如果(guo)帶(dai)上了就是登(deng)(deng)錄(lu)(lu)過的用(yong)戶(hu)?不,別忘了,前面說過,用(yong)戶(hu)可能會在同(tong)一設備切換(huan)賬(zhang)戶(hu),那(nei)就有(you)可能在登(deng)(deng)錄(lu)(lu)接口中(zhong)帶(dai)上了別人(ren)sessionid,那(nei)并不能表明用(yong)戶(hu)曾經登(deng)(deng)錄(lu)(lu)過
2)通(tong)過帶(dai)上來的(de)sessionid從redis中拿到openid,跟在code2session新請求到的(de)openid進行比(bi)對,如果一(yi)致,可以證(zheng)明用(yong)戶曾經登錄過,否(fou)則,仍需要用(yong)戶進行注冊(ce)
時間是(shi)浪(lang)費了那么一些,但是(shi)進過思考摸索,代碼肯定更完(wan)備了~~