會話技術之Session詳解

上一文中,我們詳細的討論了會話、會話技術,細緻的分析了Cookie的作用和使用方法。我們知道,Cookie是用於在瀏覽器中保存會話信息的,並且可以在多次Http請求之間實現數據的共享。但是如果每次請求攜帶的Cookie信息過多,則會明顯的增大服務器端程序的處理難度和降低傳輸效率。

​鑑於上面提出的問題,有請我們今日的主角—Session。

​注:關於會話、會話技術的描述請參考上篇博客

資源分配圖

1.Session的概念

​Session同樣也是會話技術的一種,和存儲於客戶端的Cookie不同,Session是存儲於服務器端的。我們可以先簡單的把Session理解爲個人的儲物櫃,每個人有一個唯一對應的儲物櫃,用戶可以根據手裏的鑰匙來操作儲物櫃。

資源分配圖

​就在上面的例子中,比如說是一家健身房,當我們進入健身房後,前臺小妹會給我們一把儲物櫃的鑰匙(Cookie),我們拿着鑰匙找到對應的櫃子(Session),換裝,健身…,換裝,歸還鑰匙,此次健身結束。

​在上面這次健身經歷中,從我們進入健身房到離開健身房就是一次完整的會話,前臺小妹給我們的鑰匙就是一個Cookie,鑰匙上號碼牌對應的櫃子就是Session,每位健身者都只擁有一個儲物櫃,可以將衣服、揹包、手機等物品存放其中,中間健身過程中比如買了瓶水、炸雞等物品,一樣可以放入儲物櫃中或拿在身邊,這些內容即是會話過程中產生的數據,可以存儲於客戶端或服務器。

​想象一下,如果沒有儲物櫃的存在(Session),我們則需將所有衣服、鞋子等等帶在身邊,每換一個項目就得將所有的東西一起攜帶過去,這也是我們說的如果把過多的信息存儲在客戶端的弊端。

2.如何獲取Session

​在上面的例子中,我們講到,一把鑰匙對應一個儲物櫃。在Web應用中,一個客戶端在服務器上也有一個對應這的Session,在當前會話結束前,這個Session被此客戶端獨享,當會話結束後,此Session會被銷燬,內存會被回收。這個過程類似於健身房中儲物櫃的使用,使用結束歸還鑰匙,一個儲物櫃在不同的時間可以供多個健身者使用。

​Session的獲取主要通過HttpServletRequest來完成的,我們來看下request中提供的兩個方法:

//如果create爲true,如果與request對應的Session不存在,則返回一個新創建的Session;
//如果create爲false,如果與request對應的Session不存在,則返回null;
//如果與request對應的Session存在,則直接返回該Session,create的值true或false,效果相同
public HttpSession getSession(boolean create);
    
//等價於getSession(true)
public HttpSession getSession();

​上面兩個方法有提到,與request對應的Session,這句話應該如何理解呢?我們知道,當瀏覽器發起請求時,請求參數,Cookie等信息都被封裝在HttpServletRequest中,即request中包含客戶端的Cookie信息(鑰匙),當通過request去獲取Session時,就會從request中得到鑰匙信息,來判斷當前服務器中是否有與之對應的Session存在。

​下面我們通過一個小示例來看下,request是怎麼通過Cookie來找到與此次請求相關的Session的。我們新建一個SessionServlet,urlPattern默認,其中的doGet方法如下:

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws 						ServletException, IOException {

  //獲取Session
  HttpSession session = request.getSession();
  //輸出SessionId
  System.out.println("服務器端request的對應的SessionId:" + session.getId());
}

​瀏覽器中輸入URL:http://localhost:8080/FirstProject/SessionServlet,訪問此Servelt後,我們打開Chrome的控制態F12->Application->Storage->Cookies->http://localhost:8080(根據你輸入的url來定),截圖如下:

資源分配圖

​後端的運行截圖如下,我們可以看到,服務器端Session的sessionId屬性和瀏覽器端保存的Cookie(name爲JSESSIONID)的值是相同的。這裏可不是一個巧合,而是一個非常精妙的設計,我們在上面一直提到一個開儲物櫃的鑰匙是一個Cookie,更精確的來講,是一個name=JSESSIONID的Cookie,其中存儲的值我們就可以理解成號碼牌,這個號碼牌唯一對應一個Session(儲物櫃,其編號爲sessionId)。

資源分配圖

​這裏還有一點讓人比較疑惑的地方,我在服務端並沒有創建一個name=JSESSIONID的Cookie呀,也沒有添加任何Cookie到response中,怎麼在Chrome中的是有的這個JSESSIONID這個Cookie?這裏主要是因爲,如果通過request獲取Session時,如果新建了一個Session,則會自動創建一個Cookie,並將其添加到response中。

​下圖爲第一訪問http://localhost:8080/FirstProject/SessionServlet時,此次Http請求對應的響應頭消息,當我們刷新頁面後,可以看到,響應頭中沒有了Cookie信息。

資源分配圖
資源分配圖

3.HttpSession API

​和Cookie相同,Tomcat在Servlet Api中提供了一個HttpSession接口,其中提供一些方法,讓我們可以方便的操作Session,比如存儲會話數據,設置一些屬性等。

​下面我們來看下HttpSession提供的方法:

資源分配圖

​其中的setAttribute、getAttribute、removeAtrribute方法是用來向Session這個儲物櫃中存取數據的,Session的也是一個域對象。

​我們還可以通過setMaxInactiveInterval方法,來設置Session的空閒超時間隔,默認的空閒超時間隔爲30分鐘。這裏簡單的解釋下空閒超時間隔,比如客戶端A在9點登入系統,創建了對應的Cookie和Session,此時對應Session的失效時間爲9點30;如果用戶在9點25時,在頁面上向服務器發起了請求,那麼對應Session的過期時間則變爲9點55;如果在接下來的半小時,也就是9點55之前,客戶端沒有向服務器發起任何請求,則此Session會自動被銷燬,解綁所有的綁定在此Session上的屬性。如果想修改當前應用中所有Session的默認超時間隔,則可以在web.xml中增加如下配置:

<session-config>
  <session-timeout>30</session-timeout>
</session-config>

​在web.xml中配置的最大空閒超時間隔的單位是分鐘,默認配置爲30分鐘。當session-timeout的值爲0或者負數時,表示會話永不超時。不過不建議這麼設置,因爲Session中空閒超時時間存在的目的,就是可以讓服務器能自動回收那些近期不在活躍的客戶端對應的Session,這樣才能保證內存資源不會因爲積累大量的不活躍Session而耗盡。當然,當用戶點擊退出,主動結束會話時,我們同樣可以使用invalidate方法來立即回收此Session。

4.Cookie與Session的區別與聯繫

​這部分屬於面試時經常問到的問題,因此這裏也簡單的進行一下總結。

區別:Cookie是存儲於客戶端(瀏覽器)的,可以通過Http響應頭和JavaScript存儲到瀏覽器端;Session是存儲在服務器端的,可以通過request來獲取Session;一個客戶端中一個domain下的Cookie的數量是有限的,儘量數量不超過50個;服務器端總的Session數量不限,只要內存空間允許即可,但一個客戶端唯一對應一個Session;Cookie中的值只能是字符串,Session中綁定的屬性爲Object,可以存儲任意的數據類型;Cookie的安全性低於Session。

聯繫:客戶端存儲的Cookie中,存在一個名爲JSESSIONID的Cookie,用於唯一對應服務端的一個Session(當Session沒有失效,並且沒有被invalidate時);當獲取Session時,如果是新建一個Session,會自動向客戶端發送JSESSIONID這個Cookie,如果客戶端已經存在,則更新此Cookie。

​上面技術兩者之間的區別與聯繫了,其實Cookie和Session都是會話技術的一種,其出現的目的就是爲了方便會話信息的追蹤,但是這兩者之間有可通過JSESSIONID來關聯到一起,解決"一人一櫃"的獨立性需求,可以說是很精髓了。

​瀏覽器中的Cookie,如果在下次請求時自動將domain、path都符合Cookie自動裝入request Header中,可在服務端通過request獲取傳來的Cookie,同樣可以通過request獲取與客戶端唯一對一個的Session;而Session的功能就像是一個儲物櫃,客戶端可以通過JSESSIONID這把鑰匙找到session,我們可以將數據存儲在此櫃子中。

​我們在上面的例子中,Session的功能完全沒有被體現出來,因爲只做一個櫃子的話,與申請一塊內存直接存儲信息相比,Session似乎沒有什麼飛躍性的優化,如果單獨的講Session,這裏是這樣的,功能很雞肋,但是,如果是在JSP中,Session就是一個bug性的功能了,因爲Session是JSP的內置對象,這麼來理解吧,Session這個儲物櫃可以被你攜帶到JSP頁面中,你可以在JSP頁面中直接從Session中獲取數據。想想吧,這樣子直接省去了JS對數據的解析等問題,多麼美好啊。

5.總結

​Session技術的出現,極大的促進了Web的發展,讓JSP開發變得更簡單快捷,再加上EL表達式對Session的加持,Session變得更加強勁。

​不過,現在的趨勢是前後端分離,JSP的使用率目前非常低咯,估計再過個十年就要退出歷史舞臺了。和JSP榮譽與共的Session,也難逃此命運。現在更多的是用token技術,比如JWT(Json Web Token),讓存儲於客戶端的信息更安全,而Session的儲物櫃的功能則可使用文件存儲、數據庫(關係型數據庫,MySQL、Oracle等)、Redis等等來進行替代。

​江山代有才人出,各領風騷數百年。吾輩共勉!

參考閱讀:

  1. Servlet基礎之HttpServletRequest詳解
  2. Servlet基礎之HttpServletResponse詳解
  3. 會話技術之Cookie詳解

​又到了分隔線以下,本文到此就結束了,本文內容全部都是由博主自己進行整理並結合自身的理解進行總結,如果有什麼錯誤,還請批評指正。

​Java web這一專欄會是一個系列博客,喜歡的話可以持續關注,如果本文對你有所幫助,還請還請點贊、評論加關注。

​有任何疑問,可以評論區留言。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章