本文目標
在 Web 的世界裡,Session 已經是被廣泛使用的一種技術,大多數的 Web 開發者,肯定都能運用自如。在現今的各類 Web 應用程式的開發框架和工具中,Session 也已經被包裝成容易使用的模式,所以本文也就不再多討論 Session 怎麼去使用,而是著重在 Session 的原理和實現上。要知道,無論是使用哪一種語言,這 Session 概念上都是通用的,沒有什麼不一樣。
Session 是什麼?
Session 之所以會存在,是因為 HTTP 為 stateless 的設計,Server 和 Client 不會一直保持連線狀態,也不會有雙方狀態的即時更新,所以,Server 並不知道 Client 的狀態(像是是否已經登入)。因此,後來的網站開發者,採用 Session 這樣的設計來解決這問題。有趣的是,Session 機制在最早的 CGI 年代,是完全要程式設計師徒手寫出來的,搞死了很多人,不像現在的網站開發者有現成的許多解決方案可以選擇,不必完全了解也可以使用。
Session 的原理
很多人在討論 Session 時,不免與 Cookie 牽扯上關係,反而不小心偏了重點,所以在了解 Session 時必須有個觀念,就算沒有 Cookie 的存在,Session 機制也可以正常運作。Cookie 在 Session 機制中,可以扮演許多角色,也可以對原始的 Session 機制做許多改良,但不用急著過早討論。從 Session 為什麼被需要來切入,自運作原理來了解,反而比較實際也易懂。最後再來討論 Cookie 在裡面所扮演的角色,才會比較清楚。
簡單來說,Session 的機制就像是你去飲料店下了單以後,得到號碼牌,然後你走開幾步,店員就忘了你是誰。所以,如果你想去取飲料,你就得靠這張號碼牌,去跟店員領,店員會跟據這號碼牌,認定你是顧客、是否點過餐、知道你點了什麼東西,然後可以接著給你屬於你的飲料。
理解 Session 的原理後,回到 HTTP 上就是一樣的。只是,在網頁技術中,有兩種方法讓 Client 取得號碼牌,一個是用 Cookie,另一個是直接輸出並嵌入頁面之中的方法(就是要你把號碼背起來)。
拿號碼牌去 Server 要資料,主要也分為兩種方法,Cookie 和運用標準的 Query string/POST body方法。(其實只要能把號碼傳到 Server 上,任何方法都行)
只不過,因為實作上的困難度考量,還有現今的 Browser 預設都支援 Cookie,所以在現有的網站框架中,都預設採用 Cookie 來發號碼牌和兌換資料。Cookie 的交換會在建立連線時,在背景自動完成,因此開發者不必考慮Client/Server的號碼牌交換問題。因為 Browser 會在建立連線後,第一時間就自動在背景把 Cookie 上傳到 Server,Server 也在回傳資料時,第一時間自動把 Cookie 回傳給 Client。
所以,除非是有必要(像是 Browser/Client 不支援 Cookie 的情況),才會保留另一種實作。
BTW, 飲料店拿號碼牌的例子應該很容易解釋清楚 Session 的原理。
Cookie-based Session
這邊要注意的是,有一種 Session 會讓很多很多人感到混亂,就是 cookie-based session。問題在於名稱上的誤導,你可能會想,既然幾乎所有的 Session 機制都會用到 cookie,是否都可以稱做 cookie-based session?
其實大多數人說的 cookie-based session ,指的是儲存資料方式的不同,不是指領號碼牌時,是否有用到 cookie。(不過如果早個十年,當時講的 cookie-based session,可能多半指的就是領號碼牌時,有沒有用到 cookie。摔碗!)
很多人在討論 Cookie 與 Session 的關係時,兩種用到 Cookie 的地方,時常混在一起講,這就不免要雞同鴨講筆戰一番了。
事實上,在最原始的 Session 設計,大多開發者都將資料存在 Server 上,也就是你點了什麼飲料,都是記錄在 Server 裡,可能是 Database、記憶體或是檔案,可以以任何一種形式儲存。然後,當你去領飲料時,店員會輸入你的號碼,用你的號碼得知你是否點過餐、點了什麼東西。
一般的小網站,這樣的解決方案並沒有什麼問題。但是對今天這種超大流量的網站服務來說,因為他們有無數台對外的 Server,有如無數個服務窗口,讓顧客總是隨機進入其中一個窗口來兌換飲料,所以後端怎麼存放和共享這個 session 資料,又要兼顧效能和方便維護,就變成是很大的問題。
因此 Cookie-based Session 就被提出為一個解決方案,把資料暫存放在 Cookie 中,讓 Client 自己負責保存。簡單來說,就是把你點什麼飲料,通通直接寫在號碼牌上。Server 就可以直接看你的號碼牌上寫了什麼,而不必花大量時間去後面建立大規模的 Server 來處理 Session 。
不過,這邊要特別提到,因為 cookie 有 4K 資料大小的限制,很多網站服務會選擇 cookie-based 和後端儲存並行的方案。
或許有人會問到 Cookie 是否有可能被篡改?這類安全問題,通常會使用加密手段來解決。一般來說,Cookie-based session 的 cookie 會被加密,只有 Server 才知道如何解開,Client 並沒有能力可以存取,只是得到一個看不懂的包裹,所以不會有安全性的問題(當然還是有機會被破)。
由於仍然存在風險,這也是其中一個原因,為什麼有些網站仍然會採用 cookie-based 和後端儲存 Session 並行的解決方案,或是會避免把敏感資料放在 Cookie-based Session 上。
什麼是 Session 傳值?
時常會聽到『使用 Session 傳值』這類說法,其實就是利用 Session 機制儲存資料,讓不同頁面之間可以互相傳遞資料。其原理通常是使用 Query String 或 POST body 等方法,把資料往 Server 傳之後,在 Server 端將 Client 上傳的資料存在 Session 之中。之後的連線或開啟其它頁面時,因為你拿的號碼牌是同一個,所以在不同的頁面之下,仍然可以讀到前一次所儲存在 Session 的狀態。