考慮一個問題:
如何抓取一個訪問受限的網頁?如校內好友的主頁,個人新鮮事頁面等。
顯然,通過瀏覽器,我們可以手動輸入用戶名密碼來訪問目標頁面,所謂“抓取”,只不過需要使用程序來模擬完成同樣的工作,因此需要了解“登陸”過程中到底發生了什么。
對未登錄用戶,服務器強制用戶跳轉到登陸頁面,用戶鍵入用戶名密碼並提交,服務器將用戶POST的信息與數據庫中信息比對,如通過則跳轉至landing page。那么在我們訪問其他頁面的時候,服務端如何判斷我們的身份呢?由於HTTP協議是無狀態的,顯然,服務器不可能直接知道我們在上一秒剛剛登錄成功。
最簡單的思路,就是用戶每個POST請求中,都需要帶上用戶名與密碼來標識自己的身份;這樣雖然可行,但大大加重了服務器的負擔(對於每個request都需要到數據庫驗證),也大大降低了用戶體驗(每個頁面都需要重新輸入用戶名密碼,每個頁面都帶有登錄表單)。
因此,誕生了一個解決方案:cookie。cookie,簡而言之就是在本地計算機保存一些用戶操作的歷史信息(當然包括登錄信息),並在用戶再次訪問該站點時瀏覽器通過HTTP協議將本地cookie內容發送給服務器,從而完成驗證,或繼續上一步操作。
進一步的,誕生了另外一種解決方案:session,簡而言之就是在服務器上保存用戶操作的歷史信息。但該方式下,仍然需要將發送請求的客戶端與session對象進行對應,所以可以借助cookie機制來獲取客戶端的標識(即session id),也可以通過GET方式將id提交給服務器。session id,即服務器上session對象文件的名稱,由服務器負責產生,保證隨機性與唯一性,相當於一個隨機密鑰,避免在握手或傳輸中暴露用戶真實密碼,類似的設計思想在SSO與OpenID中也經常用到。
再插入一個問題:為什么對於一些網站,關閉瀏覽器之后,session就失效了?
從上文可以知道,session一般通過cookie來保存session id,如果cookie設置為關閉瀏覽器就刪除(expire),那么無論如何設置session的超時機制,由於瀏覽器重新啟動時再也找不到原來的cookie了,因此服務器只能重新為其分配session id。
問題3:cookie和session的區別?
如上文所述,session和cookie的目的相同,都是為了克服http協議無狀態的缺陷,但完成的方法不同。session通過cookie,在客戶端保存session id,而將用戶的其他會話消息保存在服務端的session對象中,與此相對的,cookie需要將所有信息都保存在客戶端。因此cookie存在着一定的安全隱患,例如本地cookie中保存的用戶名密碼被破譯,或cookie被其他網站收集(例如:1. appA主動設置域B cookie,讓域B cookie獲取;2. XSS,在appA上通過javascript獲取document.cookie,並傳遞給自己的appB)。
在當初寫php App時,知道通過SSO可以從Session中獲取userid,但不知其所以然,於是遇到一個奇怪的問題:瀏覽器A標簽腳本執行過程中,打開B標簽訪問同一個腳本,會被pending,直到A執行完畢。原因該腳本執行了session_start(),而php session_start()后對該session的寫入是排他的,只有當腳本執行結束或顯式執行session_destroy()才能釋放session文件鎖。因為不知道session的工作原理,被困擾了整整一個工作日!類似的問題還有因為不了解Lamp中字符編碼的轉換規則,導致某些在gbk和gb2312差集中的文字無法入庫。
所以,知其然,還需要知其所以然。磨刀不誤砍柴工,授人以漁,做web開發之前,有必要將一些必要知識了解清楚,才不會在用到時候捉襟見肘,或是在調bug時候如無頭蒼蠅亂轉。做好自身建設,永遠比case by case地被動滿足來得高明。