Java Cookie和Session
1.會話技術簡介
http協議是無狀態的,因此對於服務端來說,當它接收到用戶端的http請求時,無法識別這個請求來源於哪個用戶端。無狀態的協議有優點也有缺點,但對於需要識別用戶端甚至是需要記住用戶端的業務來說,應當要讓http協議"有狀態"。
需要記住用戶端的業務種類非常多。例如登陸系統,在一個頁面登入後,新開啟一個該網站頁面,應當也保持登入狀態。再例如購物車系統,某使用者添加商品1後應當保證他還能繼續添加商品2,在結算時能夠讀取購物車中的所有商品。
如何讓服務端記住用戶端?目前使用最多的是cookie和session兩種會話技術。
- 1.Cookie:資料存放區在用戶端本地,減少伺服器端的儲存的壓力,安全性不好,用戶端可以清除cookie。
- 2.Session:將資料存放區到伺服器端,安全性相對好,會增加伺服器的壓力。
2.Cookie技術
Cookie技術是將使用者的資料存放區到用戶端的技術,它的作用是為了讓服務端根據每個用戶端持有的cookie來區分不同用戶端。
cookie由cookie name、具有唯一性的cookie value以及一些屬性(path、expires、domain等)構成,其中value是區分用戶端的唯一依據。
Cookie的原理為:服務端在接收到用戶端首次發送的請求後,服務端在響應首部中加入"set-cookie"欄位發送給用戶端;用戶端接收響應後,將cookie資訊儲存到記憶體中(如果設定了MaxAge屬性,則儲存到磁碟中);因為cookie資料在瀏覽器的記憶體中,因此無論是哪個頁面,用戶端再次向服務端發送請求時都能擷取該cookie資訊,並在請求首部中加入"cookie"欄位發送給服務端;服務端藉此就可以識別用戶端,並從cookie中找到該用戶端的資訊。
使用Cookie需要解決的兩個問題:
- (1).服務端怎樣將一個Cookie發送到用戶端。
- (2).服務端怎樣接受用戶端攜帶的Cookie。
2.1 伺服器端向用戶端發送Cookie
設定Cookie涉及的幾個常用方法為:
Cookie(String cookie_name,String cookie_value)
:構造一個Cookie對象。
setPath(uri)
:當訪問屬於該uri下的路徑(包括子路徑)時,該cookie都生效,例如setPath("/Cookie")
,當本機使用http://localhost/Cookie/servlet1
和http://localhost/Cookie/servlet2
訪問時,都擁有該Cookie。
setMaxAge(int second)
:設定該屬性時,cookie將持久化儲存到用戶端的磁碟中,儲存時間為second秒。如果cookie不具有該屬性,則cookie只會存放在記憶體中。
setDomain(String domain)
:設定Cookie生效的域範圍,例如cookie.setDomain(".foo.com");
,這將對foo.com域下的所有主機都生效(如www.foo.com),但不包括子域(www.abc.foo.com)。
設定好Cookie後,需要使用response的方法addCookie(Cookie cookie)
將cookie加入到響應首部中發送給用戶端。
例如,以下是名為CooikeDemo工程的一個servlet,該servlet的uri路徑為"/cookieservlet"。
import java.io.IOException;import javax.servlet.ServletException;import javax.servlet.http.Cookie;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class CookieServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Cookie cookie = new Cookie("username","zhangsan"); //構造cookie對象 cookie.setPath("/CookieDemo"); //設定cookie生效的uri範圍 cookie.setMaxAge(10*60); //設定cookie持久到磁碟的時間為10分鐘 response.addCookie(cookie); //在響應首部中加入set-cookie欄位並發送給用戶端 }protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response);}
該cookie將會在響應首部加入set-cookie欄位發送給用戶端:
當用戶端再次請求時,將在請求首部中加入cookie欄位。
需要注意的幾點:
- (1).Cookie中不能儲存中文。
- (2).如果不設定持久化時間,cookie會儲存在瀏覽器的記憶體中,瀏覽器關閉時cookie資訊銷毀,這是會話級的cookie。如果設定持久化時間,cookie資訊會被持久化到磁碟中,這是持久層級的cookie。持久化後的cookie不會隨瀏覽器關閉而失效,而是在有效時間內都有效。
- (3).
setPath()
設定的生效路徑為目錄時,則cookie對該目錄和子目錄下的資源都生效,如果生效路徑為檔案時,則只對該檔案有效。例如:cookie.setPath("/webapp"); //代表訪問webapp應用中的任何資源都攜帶cookiecookie.setPath("/webapp/cookieservlet"); //代表訪問webapp中的cookieservlet時才攜帶cookie資訊
- (4).如果想要刪除當前還有效cookie資訊,可以使用同名同路徑的持久化時間為0的cookie進行覆蓋。這樣一來,每次用戶端接收到響應後cookie就立即失效,也就無法攜帶cookie請求服務端。例如刪除上面樣本的cookie資訊
Cookie cookie = new Cookie("username","zhangsan");cookie.setPath("/CookieDemo");cookie.setMaxAge(0);response.addCookie(cookie);
2.2 伺服器端接受用戶端攜帶的Cookie
如前面的圖中所示,用戶端的cookie資訊是以要求標頭的方式發送到伺服器端的。因此服務端要擷取cookie資訊,需要使用request對象中的方法getCookies()
。這時唯一的擷取cookie的方法,它返回的是Cookie數組集合,因此需要遍曆該數組才能擷取指定名稱的cookie。
例如,擷取cookie name為"username"的cookie。
Cookie[] cookies = request.getCookies();if(cookies != null) { for (Cookie coo : cookies) { String cookie_name = coo.getName(); if (cookie_name.equals("username")) { String cookie_value = coo.getValue(); System.out.println(cookie_name+":"+cookie_value); } }}
3.Session技術
從開啟一個瀏覽器訪問某個網站,到關閉這個瀏覽器的整個過程(釋放瀏覽器記憶體),成為一次會話。除了Cookie技術可以讓服務端在一次會話過程中記住用戶端,Session技術也可以達到這樣的目的。
Session技術將資料存放區在伺服器端,它會為每個用戶端都建立一塊記憶體空間儲存用戶端資料,並為用戶端分配一個儲存在cookie中的JSESSIONID,用戶端需要每次都攜帶一個這個ID,伺服器通過這個ID可以找到屬於該用戶端的記憶體空間。由於這個標識ID是藉助Cookie儲存的唯一性標識JSESSIONID,因此Session是基於Cookie來實現的。
Session的原理:服務端接收到某用戶端首次發送的請求後,為此用戶端產生一個session,並分配一段屬於該session的緩衝區,同時將該session配對的標識號JSESSIONID作為cookie的name添加到響應首部中返回給用戶端;用戶端下次訪問時,請求首部中將攜帶該JSESSIONID,服務端將根據該JSESSIONID尋找與之配對的session,如果能找到對應的session,則直接操作該session資源,否則將重新為此JSESSIONID分配一個session和對應的緩衝區。
使用Session技術需要解決如下三個問題:
- (1).怎樣獲得屬於某用戶端的session對象(記憶體地區)?
- (2).怎樣向session中存取資料?
- (3).session對象的生命週期?
3.1 獲得Session對象
服務端通過用戶端發送cookie中的JSESSIONID區分用戶端,可以通過請求包中的這個資訊來擷取該用戶端相關的session資訊。
HttpSession session = request.getSession();
此方法有兩個作用:
- (1).從cookie中擷取JSESSIONID,並尋找是否存在該ID對應的session對象。如果存在,則擷取該session對象。
- (2).如果該用戶端沒有發送JSESSIONID或JSESSIONID和服務端記錄的ID值不匹配,則為該JSESSIONID重新分配一個session對象。
實際上就是根據JSESSIONID判斷該用戶端是否在伺服器上已經存在session了,有則用之,無則分配之。
3.2 向session中存取資料(session也是一個域對象)
session也是一個域對象,session域的作用範圍是整個session,可以對用戶端的多次請求生效。該範圍小於context域(即application域),大於request域(只在一次請求內有效)。
作為域對象,session對象也同樣具有如下三個方法:
session.setAttribute(String name,Object obj);session.getAttribute(String name);session.removeAttribute(String name);
此外,可以通過session對象的getId()
方法擷取到該session的JSESSIONID值。
3.3 Session對象的生命週期
- 建立:第一次執行request.getSession()時建立。
- 銷毀:
也就是說,用戶端在一次會話中任何資源都共用一個session對象。
問題:瀏覽器關閉,session就銷毀了嗎?
不對,session儲存在服務端,和用戶端沒多大關係,只要用戶端沒有操作session,等一段時間後,session自動銷毀。
但是,關閉瀏覽器後,cookie中的JSESSIONID就丟失了,也就無法再找到對應的session資料。可以在發送session給用戶端前將jsessionid當成cookie的屬性並配置cookie的持久化時間持久化到用戶端磁碟,這樣再次開啟瀏覽器時jsessionid就不會丟失。代碼大致如下:
HttpSession session = request.getSession();session.setAttribute("username","Tom");String id = session.getId(); //擷取JSESSIONID值Cookie cookie = new Cookie("JSESSIONID",id); //"JSESSIONID"為固定值cookie.setPath("/CookieDemo");cookie.setMaxAge(12*60*60); //JSESSIONID持久化儲存12小時response.addCookie(cookie);response.getWriter().write("JSESSIONID:"+id);System.out.println(session.getAttribute("username"));