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應用中的任何資源都攜帶cookie cookie.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()時創建。當客戶端訪問帶有getSession()方法的servlet時會執行該方法並創建session,訪問jsp頁面也會,因為jsp默認設置session=true會創建session。但訪問靜態資源(html/pic)等不會,因為輸出這些靜態數據的默認servlet不執行getSession()。
- 銷毀:
- 1.服務器(非正常)關閉時。
- 2.session過期/失效(默認30分鍾,這個默認時間可以在web.xml中修改)。
需要注意的是失效時間的起算點,即從何時開始計算30分鍾?從不操作服務器端的資源開始計時(即從最近一次讀取session數據開始)。<session-config> <session-timeout>30</session-timeout> </session-config>
- 3.手動銷毀session:
session.invalidate();
。
也就是說,客戶端在一次會話中任何資源都共用一個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"));
注:若您覺得這篇文章還不錯請點擊右下角推薦,您的支持能激發作者更大的寫作熱情,非常感謝!