天天看點

javaweb展現文章内容_javaweb基礎之Session 的工作原理(詳解)

大家都知道,HTTP 協定本身是無狀态的,Session 的出現解決了這個問題,也被大多數 Web 端采用。 但它背後的實作原理你是否有興趣了解呢,以及在它基礎上的思維發散,和你聊聊。

無狀态的 HTTP

大家都知道,我們目前使用的網際網路應用層協定基本上都是基于 HTTP 和 HTTPS 的,它們的本身是無狀态的, 隻負責請求和響應。 我告訴伺服器我需要什麼,伺服器傳回給我相應的資源。 如果沒有額外處理的話, 伺服器是不知道你是誰,更無法根據你是誰給你展現和你相關的内容了。

HTTP 協定一開始被設計成這樣還是有一些曆史原因的,當時的網際網路多用于學術交流,隻用于文章資訊的展現之類的事情,遠沒有現在這麼豐富多彩。是以在當時的背景下 HTTP 協定被設計成這樣其實也是很符合它的場景的。

但随着網際網路應用越來越廣泛,應用的形式也變得越來越多,我們的 Web 應用不隻限于提供簡單的資訊展現了,還需要使用者能夠登入,可以在論壇發文章,在購物網站買東西等等。 這就需要 HTTP 協定能夠記錄使用者的狀态。也就是我們現在熟悉的 Session 由來。

Session 如何實作

大多數 Web 架構都提供了操作 Session 的 API。 如果你有 Web 端開發的經驗,那麼你對這樣的代碼應該不會陌生:

session["name"] = "xxx";

我們給 session 對象設定一個值,比如使用者登入成功後,給他設定一個使用者名,這樣我們這個使用者的狀态就儲存下來了。 并且後續的請求中,這個狀态都可以讀取到。這一切就這麼自動的發生了,并且習以為常。那麼這一行代碼背後發生了什麼呢? 就是下面幾個步驟。

生成 Session ID

關于 Session 的工作原理, 其實還是值得我們了解一下的。 我們調用上面這行代碼後,我們使用的 Web 架構首先會給目前請求建立一個 Session ID。 這個 Session ID 是通過一系列算法生成的一個唯一字元串。 這也是一個 Web 架構(PHP, Ruby on Rails, ExpressJS 等)提供的基礎能力,每個架構生成 Sesssion ID 的具體實作算法可能有所差别,但整體流程都是一樣的。

建立服務端 Session 存儲結構

這個新生成的 Session ID 用于辨別這次發起請求的使用者,并存儲到伺服器的某個區域中(預設情況下會在記憶體中)。 這個 Session ID 同樣也是本地存儲的一個 key,比如我們上面代碼中設定了 name 屬性,就相當于在這個 key 中設定了對應的屬性。 這樣說起來可能有點抽象,舉個例子 Session 在服務端的存儲結構大概相當于這樣:

{ "session-id-1": { "name" : "xxx" }, "session-id-2": { "name" : "xxx" } //... } }

服務端會儲存這樣一個字典, 将每個使用者的 Session ID 和對應的屬性都記錄下來。

把 Session ID 傳回給使用者浏覽器

還繼續我們剛才的流程,生成完 Session ID 并建立好本地存儲結構後, 服務端會在傳回給使用者的 HTTP 響應消息中帶上這個 Session ID:

javaweb展現文章内容_javaweb基礎之Session 的工作原理(詳解)

如上圖, 通過 Response Header 的 set-cookie 帶上,這樣截圖使用的是基于 Express 的 NodeJS 服務端架構, 這裡面的 connect.sid=xxx 就是服務端給這個使用者生成的 Session ID。

這樣,使用者的浏覽器得到這個 Cookie 後,再下一次請求同一個網站的時候就會在請求中帶上這個 Cookie。

如果你對 Cookie 的細節不熟悉的話,不用多想,你可以了解成這樣 — 你用的所有浏覽器都會有這個邏輯,收到 set-cookie 響應頭後,就會把裡面的内容儲存下來,下一次再通路同樣的站點時候,就會把之前儲存的 cookie 再重新發送回去。當然,關于 Cookie 的細節還有很多知識,不過了解我的這個簡單解釋就足夠了,感興趣的話可以再深入研究。

用戶端發送 Session ID

就會像我們剛說的,浏覽器下一次再請求這個網站時,會把之前儲存的 Session ID 再重新發給服務端。 這時候服務端就會用這個 Session ID 和它之前建立的存儲結構中進行比對,如果這個 Session ID 是之前合法建立的,那麼就可以從服務端存儲中得到使用者之前的狀态了,比如登入使用者名之類的。 比如:

{ //... "session-id-1": { "name" : "xxx" } //... }

假設上面是我們服務端建立的本地 Session 資料存儲,如果 Session ID 正确比對,就能找到對應這個使用者的 name 屬性了。

整體步驟

上面給大家介紹的就是 Session 整體的工作過程。 大概分為這幾個步驟:

浏覽器第一次請求網站, 服務端生成 Session ID。

把生成的 Session ID 儲存到服務端存儲中。

把生成的 Session ID 傳回給浏覽器,通過 set-cookie。

浏覽器收到 Session ID, 在下一次發送請求時就會帶上這個 Session ID。

服務端收到浏覽器發來的 Session ID,從 Session 存儲中找到使用者狀态資料,會話建立。

此後的請求都會交換這個 Session ID,進行有狀态的會話。

擴充知識

看完這一套流程後,是不是對我們開始的那一行代碼背後發生的事情了解的更通透了呢。還有幾個值得讨論的地方也和大家聊聊。

1. Session ID

首先就是 Session ID,如果你了解了前面的介紹後,就會得到一個知識,在整個會話過程中,最重要的就是 Session ID。一個相對成熟的 Web 應用,往往會同時處理成百上千,甚至更大量的使用者同時線上。 這就對 Session ID 的建立有一個非常重要的要求,那就是在保證生成性能的同時,不能重複!

可以想象,如果你的 Web 架構在生成 Session ID 的時候重複了,會發生什麼事情 — 使用者會誤登入進别人的賬号, 這個後果還是非常嚴重的。 好在現在成熟的 Web 架構都考慮到了這個問題,你知識架構的使用者,是以你不必過于擔心。但了解背後的這個原理以及思維方式還是有助于你寫出更安全的程式的。

2. Session 資料存儲

另外一個要聊聊的就是 Session 資料的存儲。 通常情況下,如果你不明确的設定, 大多數 Web 架構會把 Session 資料存放到記憶體中。如果你的 Web 應用使用者量不大的話,這也不成問題。 但如果你的使用者數比較大的話,就可能發生一個事情 — 記憶體不夠用了。

這很正常,記憶體容量是非常寶貴的,假設每個使用者的 Session 資料是 100K, 1萬個使用者就會大概占用 1G 的存儲空間,如果你的 Session 資料清理機制也恰巧比較慢的話,記憶體非常容易被占滿。

這就需要你在設計比較大并發量的站點時,要考慮 Session 的存儲方式,比如把它們儲存到硬碟檔案系統中,或者資料庫中。 是以你在開發一個 Web 應用的時候,如果你的使用者量很大,你需要有這個意識。

另外 Session 放到記憶體中還有一個弊端,如果你的 Web 伺服器發生重新開機,那麼所有的 Session 狀态都會被情況,會在一定程度上影響使用者體驗。

3. 傳輸安全

最後再聊聊傳輸安全,有一種叫做 Session ID 劫持的,假如 Session ID 是基于 HTTP 協定傳輸的,因為是明文傳輸,那麼它就可能被中間的路由器劫持。 攻擊者得到 Session ID 後,把它帶到自己的請求中,就能夠進入你的賬戶。

是以一些 Web 架構還提供了 Session 的一些安全保護,比如間隔時間内動态重新整理 Session ID,加上 Token 等。但這些也無法完全保證不被中間人看到。 其實從這個角度也間接展現了為什麼 HTTPS 這麼重要。

總結

這次跟大家聊了一下 HTTP Session 的原理和整個工作過程。 透過對它的了解,不僅是對細節的掌握,更重要的是這些知識能夠幫助我們理清對技術整體的思維方式。 包括我們最後說的 Session ID 生成機制。為什麼把 Session 放到記憶體中會有問題,這樣才會了解架構為什麼要提供硬碟和資料庫之類的其他 Session 存儲方式。無論你使用什麼架構,什麼語言,這些原理性的東西都是不變的。了解的多了,你也就越來越不用焦慮學哪種技術有前途這個問題了。

上一篇: vue 相關知識
下一篇: doracms 安裝