一、背景
最近在做一個面向三端【H5、IOS、安卓】的短信驗證碼登錄接口。發送短信驗證碼時,服務端通過 session 保存驗證碼的值。登錄時,從 session 獲取驗證碼和用戶輸入的驗證碼
相比較。在這過程中,H5 端 session 的設置、讀取都沒有問題。但是 IOS 、安卓端可以正常設置 session 值,但是在登錄時,讀取的 session 值為空,導致業務邏輯異常中斷。
二、原因
1、PC 端多個請求,會使用同一個 PHPSESSID ;
2、移動端【IOS 安卓】多個請求,會使用不同的PHPSESSID。比如發送驗證碼時的 PHPSESSID = t5arr13hh352gaekn255jvrk36,登錄時的 PHPSESSID = 44jh1hgcc07v0bi07i652vbnv1;
因為 PHPSESSID 的不一致,導致了移動端【IOS 安卓】不能正常讀取 session 值。
三、參考解決方案
移動端人為設置 PHPSESSID ,並保持有業務管理的接口中的 PHPSESSID 一致即可。
四、Session 原理
提到session,大家肯定會聯想到登錄,登錄成功后記錄登錄狀態,同時標記當前登錄用戶是誰。功能大體上就是這個樣子,但是今天要講的不是功能,而是實現。通過探討session的實現方式來發掘一些可能你之前不知道的有趣的事情。
為了記錄session,在客戶端和服務器端都要保存數據,客戶端記錄一個標記,服務器端不但存儲了這個標記同時還存儲了這個標記映射的數據。好吧,還是說點白話吧,在客戶端記錄的其實是一個sessionid,在服務器端記錄的是一個key-value形式的數據結構,這里的key肯定是指sessionid了,value就代表session的詳細內容。用戶在做http請求的時候,總是會把sessionid傳遞給服務器,然后服務器根據這個sessionid來查詢session的內容(也就是上面說到的value)。
現在我們重點關注一下sessionid,他是今天問題的關鍵所在。sessionid在客戶端(http的客戶端一般就是指瀏覽器了)是存儲在cookie中,當然也有例外(書本上肯定會提到也有保存在url中的)。
我們通過一個例子來闡述一下這個sessionid在session處理時的作用。首先假定這么一個場景,我們有一個cms(content management system,內容管理系統),這個應用有一個后台,用戶必須登錄才能進入后台進行文章發表等操作。首先是登錄流程,用戶在瀏覽器輸入用戶名、密碼,點擊登錄,瀏覽器會將用戶名密碼提交到服務器程序進行處理;服務器驗證用戶名、密碼正確后,會返回登錄成功信息,並且會修改服務器端的session內容,比如我們將用戶ID寫入session中,為了方便存儲這些session的內容會被序列化成字符串或者二進制保存在文件或者數據庫中,這時候大多數情況下服務器在對當前的http請求進行響應時,會返回一個新的sessionid要求瀏覽器寫入本地cookie中,對應的返回的http響應頭部信息應該會是是這個樣子的:set-cookie:PHPSESSID=xxxxxxx,瀏覽器解析到這個頭之后就會在當前生成一個cookie關聯當前的域名。
接着用戶登錄后台進行發表文章操作,登錄用戶填寫文章的標題、內容,然后點擊發送。這時候瀏覽器會生成一條到服務器的http請求,注意這個請求的頭部會將存儲sessionid的cookie內容發送過去,也就是說請求的http頭部信息中應該會有這么一段數據:cookie:PHPSESSID=xxxxxxx;other_cookie_name=yyyyyy;服務器接收到這個http請求之后,解析到cookie存在,且cookie中存在PHPSESSID這個cookie名字,然后就將PHPSESSID的值(也就是sessionid的值)取出來,根據這個PHPSESSID查詢服務器上有沒有對應的session內容,如果有則將其對應的值取出來進行反序列序列化(也就是將其轉成編程語言中的一個數據結果,比如在php中會得到一個$_SESSION數組,在j2ee中會得到類型為javax.servlet.http.HttpSession),方便在程序中進行讀取,最終服務器認定session中儲存的值存在,並且從反序列化得到的對象中讀取到了用戶ID屬性,然后就往cms數據庫的文章表中插入了一條數據,最終返回http響應,告訴瀏覽器操作成功了。