對(duì)于已經(jīng)熟悉了session原理的同學(xué)來說,我們都清楚:在瀏覽器端我們會(huì)存儲(chǔ)一個(gè)sessionId,用它來作為憑證,在服務(wù)器端得到有關(guān)本次瀏覽器與服務(wù)器會(huì)話的所有信息,這些信息是儲(chǔ)存在服務(wù)器端的存儲(chǔ)空間中的,它完全可以用來判斷一個(gè)瀏覽器端的登錄狀態(tài),因?yàn)樗怯煞?wù)器端來掌控的,是安全的。
那么瀏覽器端是用什么來存儲(chǔ)這個(gè)sessionId? 并且瀏覽器又是如何將sessionId傳回給服務(wù)器的呢?
大體上是有兩種方法的:
1、使用瀏覽器端實(shí)現(xiàn)的cookie功能,每次瀏覽器都會(huì)將服務(wù)器傳過來的cookie內(nèi)容按鍵值對(duì)的方式放到瀏覽器的緩存中,然后下次請(qǐng)求同一個(gè)服務(wù)器時(shí)又會(huì)將cookie內(nèi)容取出來送回服務(wù)器,當(dāng)然其中就有存儲(chǔ)在cookie中的sessionId。
2、使用URL重寫的方法,URL地址重寫是對(duì)客戶端不支持Cookie的解決方案。URL地址重寫的原理是將該用戶Session的id信息重寫 到URL地址中。服務(wù)器能夠解析重寫后的URL獲取Session的id。這樣即使客戶端不支持Cookie,也可以使用Session來記錄用戶狀態(tài)。
不到迫不得已的地步,我是不會(huì)考慮第二種,也就是使用URL重寫的方法的。
其實(shí),實(shí)際的應(yīng)用場(chǎng)景無非就是以下兩種,我們用病人看病的例子來理一理:
第一種,醫(yī)生給病人看病,一天有幾百個(gè)病人,不可能把所有的病人的病情都記得清清楚楚,所以就需要一個(gè)病歷本,病人下次來看病的時(shí)候醫(yī)生就可以根據(jù)病人的病歷本上記錄的信息來得到病人的病情狀況。(這種情況對(duì)應(yīng)的就是瀏覽器端只使用cookie與服務(wù)器進(jìn)行交互的情況)
第二種,有一些特殊的病人,他們是毒癮患者,正在接受戒毒的輔助治療,每個(gè)月需要到醫(yī)院領(lǐng)取少量的類似毒品的藥物來逐步減少毒品攝入量;醫(yī)院也給他一個(gè)病歷本,但是由于使用藥物的特殊性,為了防止某些不法分子偽造病歷本去領(lǐng)取違禁藥物,于是就給每個(gè)病歷本上使用GUID算法生成了一個(gè)獨(dú)一無二的病歷號(hào),在醫(yī)生這里也有一個(gè)本子,上面記錄了所有特殊病人的病歷號(hào),下次病人來拿違禁藥物的時(shí)候,醫(yī)生需要對(duì)照病人的病歷號(hào)來查詢是否存在這個(gè)病人和他需要領(lǐng)取藥品的具體量。這樣,就可以防止非法人員冒充和病人惡意更改藥品領(lǐng)取量的情況發(fā)生。(這種情況對(duì)應(yīng)的就是通過sessionId來查詢出本次瀏覽器與服務(wù)器的會(huì)話信息的情況,病歷本上的病歷號(hào)就相當(dāng)于sessionId)
上面寫了這么多關(guān)于session的原理內(nèi)容就是為了下面引出正題做準(zhǔn)備的,所以到這里還不明白session原理的同學(xué)可以繞道去找一些關(guān)于session的文章研究研究,再回來接著往下看。
推薦一篇大神的關(guān)于session原理的文章:
https://www.cnblogs.com/linguoguo/p/5106618.html
相關(guān)閱讀:品牌負(fù)面輿情監(jiān)控公關(guān)管理
下面展開正題,問題是這樣的:
在開發(fā)微信小程序的過程中需要實(shí)現(xiàn)一個(gè)小程序登陸的功能,由于小程序中與服務(wù)器的交互大部分使用的都是HTTP通信,所以完全可以仿照之前開發(fā)B/S的那一套登陸體系,利用上面提到的sessionId的方式在服務(wù)器端進(jìn)行登陸態(tài)的存儲(chǔ),進(jìn)行是否登錄的判斷。相對(duì)于以前的 服務(wù)器/瀏覽器 的開發(fā)模式,服務(wù)器/微信小程序 的開發(fā)模式有一個(gè)初級(jí)開發(fā)者需要注意的點(diǎn),就是:微信小程序是不會(huì)將HTTP報(bào)文頭中的COOKIE信息存入緩存中的,自然也就不會(huì)將COOKIE的內(nèi)容傳回給服務(wù)器端,簡(jiǎn)單的說就是 微信小程序端沒有幫你實(shí)現(xiàn)cookie機(jī)制。
所以,如果你想當(dāng)然的就認(rèn)為微信小程序已經(jīng)幫你在背后實(shí)現(xiàn)了cookie機(jī)制,那么你就會(huì)像我一樣,踩入了一個(gè)大坑。
public ActionResult SessionTest() { Session["TestValue"] = 1; return Json(new { message = "this is a test response" }); }
使用微信小程序的原生request方法寫的請(qǐng)求程序
sendRequest:function(){ wx.request({ url:'http://localhost:51112/Test/SessionTest', method:'POST', success:function(res){ console.log("進(jìn)行了一次請(qǐng)求"); }, fail:function(){ console.log("請(qǐng)求失敗"); } }); }
過程就是用小程序的這段代碼運(yùn)行,去請(qǐng)求服務(wù)器,服務(wù)器執(zhí)行的就是上面展示的那段action方法的代碼。
總共請(qǐng)求兩次,兩次請(qǐng)求的http報(bào)文頭如下:
第一次請(qǐng)求:
?。ㄕ?qǐng)求報(bào)文頭):
(應(yīng)答報(bào)文頭):
由于這是微信小程序第一次與服務(wù)器進(jìn)行交互,所以并沒有攜帶任何cookie的內(nèi)容,這是很正常的現(xiàn)象。等到服務(wù)器應(yīng)答瀏覽器時(shí),就給了瀏覽器一個(gè)sessionId,字段名為 ASP.NET_SessionId 值為 saqu0pv20q5jkd1q2dlmxcyg 。到這里我們?nèi)绻J(rèn)為微信小程序已經(jīng)實(shí)現(xiàn)了cookie機(jī)制,那么下一次向同一個(gè)域名進(jìn)行請(qǐng)求的時(shí)候,我們會(huì)在請(qǐng)求報(bào)文頭中看到會(huì)有一個(gè)cookie字段,里面會(huì)有上一次微信小程序從服務(wù)器那里得到的sessionId的值。
然而。。。
第二次請(qǐng)求:
(請(qǐng)求報(bào)文頭):
?。☉?yīng)答報(bào)文頭):
看到這里你就會(huì)發(fā)現(xiàn)它與你預(yù)想的完全不一樣了。。首先,請(qǐng)求報(bào)文頭中并沒有發(fā)現(xiàn)有上一次服務(wù)器給微信小程序端傳的sessionId;然后,服務(wù)器返回給小程序的報(bào)文頭中的 ASP.NET_SessionId 值變成了 cwugbbt0mmliha0ul4ccx4l2 并非原先的 saqu0pv20q5jkd1q2dlmxcyg 。
發(fā)生的一切都指向一個(gè)原因,那就是 小程序并沒有實(shí)現(xiàn)cookie的機(jī)制,導(dǎo)致小程序請(qǐng)求服務(wù)器的報(bào)文頭中并沒有攜帶sessionId,服務(wù)器拿不到sessionId,就會(huì)認(rèn)為這是另外一個(gè)還沒有對(duì)服務(wù)器請(qǐng)求過的客戶端,就新生成了一個(gè)sessionId給微信小程序端,所以小程序端就拿到了不同于上次的sessionId。
到現(xiàn)在,所有的現(xiàn)象以及現(xiàn)象背后的原因都解釋通了,那么接下來就是怎么解決問題。
其實(shí)解決問題的方法很簡(jiǎn)單,既然微信小程序端沒有實(shí)現(xiàn)cookie機(jī)制,那么就自己實(shí)現(xiàn)cookie機(jī)制唄。
思路:cookie機(jī)制最簡(jiǎn)單的功能無非就是將服務(wù)器返回的 應(yīng)答報(bào)文中cookie部分找個(gè)地方存起來,以后再向服務(wù)器發(fā)出請(qǐng)求時(shí)就將存儲(chǔ)的cookie內(nèi)容取出,填充到請(qǐng)求報(bào)文頭中。
存到一個(gè)地方,存到哪呢?有兩個(gè)合適的地方:
1、微信小程序的緩存。
2、微信小程序的全局變量中。
我選擇第二種,將cookie內(nèi)容存到微信小程序的全局變量中去,下面是我自己封裝的一個(gè)自動(dòng)攜帶cookie中的sessionId去請(qǐng)求的請(qǐng)求函數(shù)實(shí)現(xiàn):
在封裝之前,最好先去微信小程序的官網(wǎng)看一下 wx.request 函數(shù)的說明。點(diǎn)這里
//app.js App({ globalData: { cookie: '', //供小程序存儲(chǔ)cookie數(shù)據(jù)使用 } })
//帶著sessionId進(jìn)行請(qǐng)求,自動(dòng)獲取服務(wù)端返回的sessionId存入全局變量中 function RequestBySessionId(requestParam){ //三個(gè)默認(rèn)參數(shù)的值 var method = "GET"; var dataType = "json"; var responseType = "text"; //用戶輸入了參數(shù)就替換,沒輸入就使用默認(rèn)的 if ("method" in requestParam) { method = requestParam.method; } if ("dataType" in requestParam) { dataType = requestParam.dataType; } if ("responseType" in requestParam) { responseType = requestParam.responseType; } var url = requestParam.url; var data = requestParam.data; var success = requestParam.success; var fail = requestParam.fail; var complete = requestParam.complete; var cookieStr = ""; //請(qǐng)求報(bào)文頭中cookie的字符串 var Cookie = App.globalData.cookie; //獲取全局變量中的cookie內(nèi)容 cookieStr = Cookie; var header = {}; if ("header" in requestParam) { header = requestParam.header; header["Cookie"] = cookieStr; } else { header["Cookie"] = cookieStr; } wx.request({ url: url, method: method, responseType: responseType, dataType: dataType, data: data, header: header, //每次請(qǐng)求帶上sessionId success: function(res){ //先將檢查服務(wù)器返回報(bào)文頭中有無sessionId,有則存到全局變量中 var cookie = res.header["Set-Cookie"]; if (undefined != cookie) { var sessionPos; if ((sessionPos = cookie.indexOf("ASP.NET_SessionId=")) != -1) { //每次請(qǐng)求成功都將sessionId存入全局變量 App.globalData.cookie = cookie.substring(sessionPos, 42); } } //執(zhí)行正常的操作 success(res); }, fail: fail, complete: complete, }); }
經(jīng)過了這一波封裝,就等于是有了微信小程序中請(qǐng)求的”神器“了,麻麻再也不用擔(dān)心我把sessionId搞丟了。
下面咱就再一次使用封裝后的函數(shù)來進(jìn)行請(qǐng)求發(fā)送試驗(yàn):
改造后的微信小程序端請(qǐng)求代碼:
sendRequest:function(){ utils.RequestBySessionId({ url: 'http://localhost:51112/Test/SessionTest', method: 'POST', success: function (res) { console.log("進(jìn)行了一次請(qǐng)求"); }, fail: function () { console.log("請(qǐng)求失敗"); } }); }
同樣的過程:用小程序的這段代碼運(yùn)行,去請(qǐng)求服務(wù)器,服務(wù)器執(zhí)行的就是上面原先展示的那段action方法的代碼。
總共請(qǐng)求兩次,兩次請(qǐng)求的http報(bào)文頭如下:
第一次請(qǐng)求:
?。ㄕ?qǐng)求報(bào)文頭):
(應(yīng)答報(bào)文頭):
看到這次服務(wù)器返回的 ASP.NET_SessionId 值為 dodngz2ahcznp4r3hrmavd1c。
然后,注意了。。。
第二次請(qǐng)求:
(請(qǐng)求報(bào)文頭):
看到第二次請(qǐng)求報(bào)文頭中的cookie了嗎,里面就是 ASP.NET_SessionId= dodngz2ahcznp4r3hrmavd1c,
說明了,已經(jīng)成功將sessionId帶上了。。。
?。☉?yīng)答報(bào)文頭):
服務(wù)器返回的報(bào)文中已經(jīng)沒有sessionId了,因?yàn)橐呀?jīng)不需要了。。。