常用的會話跟蹤技術是Cookie與Session。html
Cookie經過在client記錄信息肯定用戶身份,Session經過在server端記錄信息肯定用戶身份。
本章將系統地講述Cookie與Session機制。並比較說明何時不能用Cookie。何時不能用Session。
1.1 Cookie機制
在程序中,會話跟蹤是很是重要的事情。理論上,一個用戶的所有請求操做都應該屬於同一個會話,而還有一個用戶的所有請求操做則應該屬於還有一個會話,兩者不能混淆。好比,用戶A在超市購買的不論什麼商品都應該放在A的購物車內,不管是用戶A什麼時間購買的。這都是屬於同一個會話的,不能放入用戶B或用戶C的購物車內,這不屬於同一個會話。java
而Web應用程序是使用HTTP協議數據傳輸的。web
HTTP協議是無狀態的協議。一旦數據交換完成。client與server端的鏈接就會關閉,再次交換數據需要創建新的鏈接。這就意味着server沒法從鏈接上跟蹤會話。即用戶A購買了一件商品放入購物車內,當再次購買商品時server已經沒法推斷該購買行爲是屬於用戶A的會話仍是用戶B的會話了。算法
要跟蹤該會話,必須引入一種機制。數據庫
Cookie就是這種一種機制。它可以彌補HTTP協議無狀態的不足。在Session出現以前,基本上所有的站點都採用Cookie來跟蹤會話。
1.1.1 什麼是Cookie
Cookie意爲「甜餅」,是由W3C組織提出,最先由Netscape社區發展的一種機制。眼下Cookie已經成爲標準。所有的主流瀏覽器如IE、Netscape、Firefox、Opera等都支持Cookie。
由於HTTP是一種無狀態的協議,server單從網絡鏈接上無從知道客戶身份。怎麼辦呢?就給client們頒發一個通行證吧,每人一個,無論誰訪問都必須攜帶本身通行證。編程
這樣server就能從通行證上確認客戶身份了。跨域
這就是Cookie的工做原理。
數組
Cookie其實是一小段的文本信息。client請求server,假設server需要記錄該用戶狀態,就使用response向client瀏覽器頒發一個Cookie。client瀏覽器會把Cookie保存起來。瀏覽器
當瀏覽器再請求該站點時,瀏覽器把請求的網址連同該Cookie一同提交給server。server檢查該Cookie。以此來辨認用戶狀態。server還可以依據需要改動Cookie的內容。緩存
注意:
1.1.2 記錄用戶訪問次數
Java中把Cookie封裝成了javax.servlet.http.Cookie類。
每個Cookie都是該Cookie類的對象。server經過操做Cookie類對象對clientCookie進行操做。
經過request.getCookie()獲取client提交的所有Cookie(以Cookie[]數組形式返回),經過response.addCookie(Cookiecookie)向client設置Cookie。
Cookie對象使用key-value屬性對的形式保存用戶狀態,一個Cookie對象保存一個屬性對,一個request或者response同一時候使用多個Cookie。因爲Cookie類位於包javax.servlet.http.*如下,因此JSP中不需要import該類。
1.1.3 Cookie的不可跨域名性
很是多站點都會使用Cookie。好比,Google會向client頒發Cookie,Baidu也會向client頒發Cookie。
那瀏覽器訪問Google會不會也攜帶上Baidu頒發的Cookie呢?或者Google能不能改動Baidu頒發的Cookie呢?
答案是否認的。Cookie具備不可跨域名性。
依據Cookie規範。瀏覽器訪問Google僅僅會攜帶Google的Cookie。而不會攜帶Baidu的Cookie。
Google也僅僅能操做Google的Cookie。而不能操做Baidu的Cookie。
Cookie在client是由瀏覽器來管理的。瀏覽器能夠保證Google僅僅會操做Google的Cookie而不會操做Baidu的Cookie,從而保證用戶的隱私安全。瀏覽器推斷一個站點可否操做還有一個站點Cookie的根據是域名。Google與Baidu的域名不同。所以Google不能操做Baidu的Cookie。
需要注意的是,儘管站點images.google.com與站點www.google.com同屬於Google。但是域名不同,兩者相同不能互相操做彼此的Cookie。
注意:用戶登陸站點www.google.com以後會發現訪問images.google.com時登陸信息仍然有效,而普通的Cookie是作不到的。這是因爲Google作了特殊處理。
本章後面也會對Cookie作相似的處理。
1.1.4 Unicode編碼:保存中文
中文與英文字符不一樣,中文屬於Unicode字符,在內存中佔4個字符。而英文屬於ASCII字符。內存中僅僅佔2個字節。Cookie中使用Unicode字符時需要對Unicode字符進行編碼,不然會亂碼。
提示:Cookie中保存中文僅僅能編碼。通常使用UTF-8編碼就能夠。
不推薦使用GBK等中文編碼,因爲瀏覽器不必定支持。而且JavaScript也不支持GBK編碼。
1.1.5 BASE64編碼:保存二進制圖片
Cookie不只可以使用ASCII字符與Unicode字符,還可以使用二進制數據。好比在Cookie中使用數字證書。提供安全度。使用二進制數據時也需要進行編碼。
注意:由於瀏覽器每次請求server都會攜帶Cookie,所以Cookie內容不宜過多。不然影響速度。
Cookie的內容應該少而精。
1.1.6 設置Cookie的所有屬性
除了name與value以外。Cookie還具備其它幾個常用的屬性。
每個屬性相應一個getter方法與一個setter方法。
Cookie類的所有屬性如表1.1所看到的。
表1.1 Cookie常用屬性
屬 性 名
|
描 述
|
String name
|
該Cookie的名稱。 Cookie一旦建立,名稱便不可更改 |
Object value
|
該Cookie的值。假設值爲Unicode字符,需要爲字符編碼。 假設值爲二進制數據。則需要使用BASE64編碼 |
int maxAge
|
該Cookie失效的時間,單位秒。 假設爲正數,則該Cookie在maxAge秒以後失效。假設爲負數,該Cookie爲暫時Cookie,關閉瀏覽器即失效,瀏覽器也不會以不論什麼形式保存該Cookie。假設爲0,表示刪除該Cookie。默以爲–1 |
boolean secure
|
該Cookie是否僅被使用安全協議傳輸。安全協議。安全協議有HTTPS,SSL等,在網絡上數據傳輸以前先將數據加密。默以爲false
|
String path
|
該Cookie的使用路徑。假設設置爲「/sessionWeb/」,則僅僅有contextPath爲「/sessionWeb」的程序可以訪問該Cookie。假設設置爲「/」,則本域名下contextPath都可以訪問該Cookie。注意最後一個字符必須爲「/」
|
String domain
|
可以訪問該Cookie的域名。假設設置爲「.google.com」,則所有以「google.com」結尾的域名都可以訪問該Cookie。注意第一個字符必須爲「.」
|
String comment
|
該Cookie的用處說明。瀏覽器顯示Cookie信息的時候顯示該說明
|
int version
|
該Cookie使用的版本。 0表示遵循Netscape的Cookie規範,1表示遵循W3C的RFC 2109規範 |
1.1.7 Cookie的有效期
Cookie的maxAge決定着Cookie的有效期,單位爲秒(Second)。Cookie中經過getMaxAge()方法與setMaxAge(int maxAge)方法來讀寫maxAge屬性。
假設maxAge屬性爲正數,則表示該Cookie會在maxAge秒以後本身主動失效。瀏覽器會將maxAge爲正數的Cookie持久化,即寫到相應的Cookie文件裏。無論客戶關閉了瀏覽器仍是電腦。僅僅要還在maxAge秒以前,登陸站點時該Cookie仍然有效。如下代碼中的Cookie信息將永遠有效。
Cookie cookie = new Cookie("username","helloweenvsfei"); // 新建Cookie cookie.setMaxAge(Integer.MAX_VALUE); // 設置生命週期爲MAX_VALUE response.addCookie(cookie); // 輸出到client假設maxAge爲負數。則表示該Cookie僅在本瀏覽器窗體以及本窗體打開的子窗體內有效,關閉窗體後該Cookie即失效。
maxAge爲負數的Cookie,爲暫時性Cookie,不會被持久化,不會被寫到Cookie文件裏。Cookie信息保存在瀏覽器內存中。所以關閉瀏覽器該Cookie就消失了。Cookie默認的maxAge值爲–1。
假設maxAge爲0,則表示刪除該Cookie。Cookie機制沒有提供刪除Cookie的方法,所以經過設置該Cookie即時失效實現刪除Cookie的效果。
失效的Cookie會被瀏覽器從Cookie文件或者內存中刪除,好比:
Cookie cookie = new Cookie("username","helloweenvsfei"); // 新建Cookie cookie.setMaxAge(0); // 設置生命週期爲0,不能爲負數 response.addCookie(cookie); // 必須運行這一句
response對象提供的Cookie操做方法僅僅有一個加入操做add(Cookie cookie)。
要想改動Cookie僅僅能使用一個同名的Cookie來覆蓋原來的Cookie,達到改動的目的。
刪除時僅僅需要把maxAge改動爲0就能夠。
注意:從client讀取Cookie時,包含maxAge在內的其它屬性都是不可讀的,也不會被提交。瀏覽器提交Cookie時僅僅會提交name與value屬性。
maxAge屬性僅僅被瀏覽器用來推斷Cookie是否過時。
1.1.8 Cookie的改動、刪除
Cookie並不提供改動、刪除操做。
假設要改動某個Cookie,僅僅需要新建一個同名的Cookie。加入到response中覆蓋原來的Cookie。
假設要刪除某個Cookie,僅僅需要新建一個同名的Cookie,並將maxAge設置爲0。並加入到response中覆蓋原來的Cookie。注意是0而不是負數。負數表明其它的意義。
讀者可以經過上例的程序進行驗證,設置不一樣的屬性。
注意:改動、刪除Cookie時,新建的Cookie除value、maxAge以外的所有屬性,好比name、path、domain等。都要與原Cookie全然同樣。不然,瀏覽器將視爲兩個不一樣的Cookie不予覆蓋,致使改動、刪除失敗。
1.1.9 Cookie的域名
Cookie是不可跨域名的。域名www.google.com頒發的Cookie不會被提交到域名www.baidu.com去。這是由Cookie的隱私安全機制決定的。隱私安全機制能夠禁止站點非法獲取其它站點的Cookie。
正常狀況下,同一個一級域名下的兩個二級域名如www.helloweenvsfei.com和images.helloweenvsfei.com也不能交互使用Cookie。因爲兩者的域名並不嚴格一樣。假設想所有helloweenvsfei.com名下的二級域名都可以使用該Cookie,需要設置Cookie的domain參數,好比:
Cookie cookie = new Cookie("time","20080808"); // 新建Cookie cookie.setDomain(".helloweenvsfei.com"); // 設置域名 cookie.setPath("/"); // 設置路徑 cookie.setMaxAge(Integer.MAX_VALUE); // 設置有效期 response.addCookie(cookie); // 輸出到client讀者可以改動本機C:\WINDOWS\system32\drivers\etc下的hosts文件來配置多個暫時域名,而後使用setCookie.jsp程序來設置跨域名Cookie驗證domain屬性。
Cookie cookie = new Cookie("time","20080808"); // 新建Cookie cookie.setPath("/sessionWeb/"); // 設置路徑 response.addCookie(cookie); // 輸出到client設置爲「/」時贊成所有路徑使用Cookie。path屬性需要使用符號「/」結尾。name一樣但domain一樣的兩個Cookie也是兩個不一樣的Cookie。
如下的代碼設置secure屬性爲true:
Cookie cookie = new Cookie("time", "20080808"); // 新建Cookie cookie.setSecure(true); // 設置安全屬性 response.addCookie(cookie); // 輸出到client提示:secure屬性並不能對Cookie內容加密。於是不能保證絕對的安全性。假設需要高安全性。需要在程序中對Cookie內容加密、解密,以防泄密。
好比如下的代碼會輸出本頁面所有的Cookie。
document.write(document.cookie);
由於JavaScript能夠隨意地讀寫Cookie,有些好事者便想使用JavaScript程序去窺探用戶在其它站點的Cookie。
只是這是徒勞的,W3C組織早就意識到JavaScript對Cookie的讀寫所帶來的安全隱患並加以防備了,W3C標準的瀏覽器會阻止JavaScript讀寫不論什麼不屬於本身站點的Cookie。
換句話說。A站點的JavaScript程序讀寫B站點的Cookie不會有不論什麼結果。
1.1.13 案例:永久登陸
假設用戶是在本身家的電腦上上網,登陸時就可以記住他的登陸信息,下次訪問時不需要再次登陸,直接訪問就能夠。實現方法是把登陸信息如帳號、password等保存在Cookie中,並控制Cookie的有效期,下次訪問時再驗證Cookie中的登陸信息就能夠。
保存登陸信息有多種方案。
最直接的是把username與password都保持到Cookie中,下次訪問時檢查Cookie中的username與password。與數據庫比較。這是一種比較危急的選擇,通常不把password等重要信息保存到Cookie中。
另外一種方案是把password加密後保存到Cookie中,下次訪問時解密並與數據庫比較。這樣的方案稍微安全一些。
假設不但願保存password。還可以把登陸的時間戳保存到Cookie與數據庫中,到時僅僅驗證username與登陸時間戳就可以了。
這幾種方案驗證帳號時都要查詢數據庫。
本例將採用還有一種方案,僅僅在登陸時查詢一次數據庫,之後訪問驗證登陸信息時再也不查詢數據庫。
實現方式是把帳號依照必定的規則加密後,連同帳號一塊保存到Cookie中。下次訪問時僅僅需要推斷帳號的加密規則是否正確就能夠。本例把帳號保存到名爲account的Cookie中。把帳號連同密鑰用MD1算法加密後保存到名爲ssid的Cookie中。驗證時驗證Cookie中的帳號與密鑰加密後是否與Cookie中的ssid相等。
相關代碼例如如下:loginCookie.jsp:
<%@ page language="java"pageEncoding="UTF-8" isErrorPage="false" %> <%! private static final String KEY ="hiddenzzh2@163.com"; public final static String calcMD1(String ss) { // MD1 加密算法 //此處省略N字 } %> <% request.setCharacterEncoding("UTF-8"); // 設置request編碼 response.setCharacterEncoding("UTF-8"); // 設置response編碼 String action = request.getParameter("action"); // 獲取action參數 if ("login".equals(action)) { // 假設爲login動做 String account = request.getParameter("account"); // 獲取account參數 String password = request.getParameter("password"); // 獲取password參數 int timeout = new Integer(request.getParameter("timeout")); // 獲取timeout參數 String ssid = calcMD1(account + KEY); // 把帳號、密鑰使用MD1加密後保存 Cookie accountCookie = new Cookie("account", account); // 新建Cookie accountCookie.setMaxAge(timeout); // 設置有效期 Cookie ssidCookie = new Cookie("ssid", ssid); // 新建Cookie ssidCookie.setMaxAge(timeout); // 設置有效期 response.addCookie(accountCookie); // 輸出到client response.addCookie(ssidCookie); // 輸出到client // 又一次請求本頁面,參數中帶有時間戳。禁止瀏覽器緩存頁面內容 response.sendRedirect(request.getRequestURI() + "?" + System.currentTimeMillis()); return; } else if ("logout".equals(action)) { // 假設爲logout動做 Cookie accountCookie = new Cookie("account", ""); // 新建Cookie,內容爲空 accountCookie.setMaxAge(0); // 設置有效期爲0。刪除 Cookie ssidCookie = new Cookie("ssid", ""); // 新建Cookie。內容爲空 ssidCookie.setMaxAge(0); // 設置有效期爲0,刪除 response.addCookie(accountCookie); // 輸出到client response.addCookie(ssidCookie); // 輸出到client // 又一次請求本頁面,參數中帶有時間戳,禁止瀏覽器緩存頁面內容 response.sendRedirect(request.getRequestURI() + "?" + System.currentTimeMillis()); return; } boolean login = false; // 是否登陸 String account = null; // 帳號 String ssid = null; // SSID標識 if (request.getCookies() != null) { // 假設Cookie不爲空 for (Cookie cookie : request.getCookies()) { // 遍歷Cookie if (cookie.getName().equals("account")) // 假設Cookie名爲account account = cookie.getValue(); // 保存account內容 if (cookie.getName().equals("ssid")) // 假設爲SSID ssid = cookie.getValue(); // 保存SSID內容 } } if (account != null && ssid != null) { // 假設account、SSID都不爲空 login = ssid.equals(calcMD1(account + KEY)); // 假設加密規則正確, 則視爲已經登陸 } %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01Transitional//EN"> <legend><%= login ? "歡迎您回來" : "請先登陸"%></legend> <% if(login){%> 歡迎您, ${ cookie.account.value }. <a href="${ pageContext.request.requestURI }?action=logout"> 註銷</a> <% } else {%> <formaction="${ pageContext.request.requestURI }?action=login" method="post"> <table> <tr><td>帳號: </td> <td><input type="text"name="account" style="width:200px; "></td> </tr> <tr><td>密碼: </td> <td><input type="password" name="password"></td> </tr> <tr> <td>有效期: </td> <td><input type="radio" name="timeout" value="-1" checked> 關閉瀏覽器即失效 <br/> <input type="radio" name="timeout" value="<%= 30 *24 * 60 * 60 %>"> 30天內有效 <br/> <input type="radio" name="timeout" value="<%= Integer.MAX_VALUE %>"> 永久有效 <br/> </td> </tr> <tr><td></td> <td><input type="submit"value=" 登 錄 " class="button"></td> </tr> </table> </form> <% } %>
Session是server端使用的一種記錄client狀態的機制,使用上比Cookie簡單一些,對應的也添加了server的存儲壓力。
1.2.1 什麼是Session
Session是還有一種記錄客戶狀態的機制,不一樣的是Cookie保存在client瀏覽器中,而Session保存在server上。client瀏覽器訪問server的時候,server把client信息以某種形式記錄在server上。這就是Session。
client瀏覽器再次訪問時僅僅需要從該Session中查找該客戶的狀態就可以了。
假設說Cookie機制是經過檢查客戶身上的「通行證」來肯定客戶身份的話,那麼Session機制就是經過檢查server上的「客戶明細表」來確認客戶身份。
Session至關於程序在server上創建的一份客戶檔案,客戶來訪的時候僅僅需要查詢客戶檔案表就可以了。
1.2.2 實現用戶登陸
Session相應的類爲javax.servlet.http.HttpSession類。
每個來訪者相應一個Session對象,所有該客戶的狀態信息都保存在這個Session對象裏。
Session對象是在client第一次請求server的時候建立的。
Session也是一種key-value的屬性對。經過getAttribute(Stringkey)和setAttribute(String key,Objectvalue)方法讀寫客戶狀態信息。
Servlet裏經過request.getSession()方法獲取該客戶的Session。好比:
HttpSession session = request.getSession(); // 獲取Session對象 session.setAttribute("loginTime", new Date()); // 設置Session中的屬性 out.println("登陸時間爲:" +(Date)session.getAttribute("loginTime")); // 獲取Session屬性request還可以使用getSession(boolean create)來獲取Session。
差異是假設該客戶的Session不存在,request.getSession()方法會返回null,而getSession(true)會先建立Session再將Session返回。
Servlet中必須使用request來編程式獲取HttpSession對象。而JSP中內置了Session隱藏對象,可以直接使用。
假設使用聲明瞭<%@page session="false" %>,則Session隱藏對象不可用。如下的樣例使用Session記錄客戶帳號信息。
源碼例如如下:session.jsp:
<%@ page language="java" pageEncoding="UTF-8"%> <jsp:directive.page import="com.zzh.sessionWeb.bean.Person"/> <jsp:directive.page import="java.text.SimpleDateFormat"/> <jsp:directive.page import="java.text.DateFormat"/> <jsp:directive.page import="java.util.Date"/> <%! DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); // 日期格式化器 %> <% response.setCharacterEncoding("UTF-8"); // 設置request編碼 Person[] persons = { // 基礎數據,保存三我的的信息 new Person("Liu Jinghua","password1", 34, dateFormat.parse ("1982-01-01")), new Person("Hello Kitty","hellokitty", 23, dateFormat.parse ("1984-02-21")), new Person("Garfield", "garfield_pass",23, dateFormat.parse ("1994-09-12")), }; String message = ""; // 要顯示的消息 if(request.getMethod().equals("POST")) { // 假設是POST登陸 for(Person person :persons) { // 遍歷基礎數據,驗證帳號、password // 假設username正確且password正確 if(person.getName().equalsIgnoreCase(request.getParameter("username"))&&person.getPassword().equals(request.getParameter("password"))) { // 登陸成功。設置將用戶的信息以及登陸時間保存到Session session.setAttribute("person", person); // 保存登陸的Person session.setAttribute("loginTime", new Date()); // 保存登陸的時間 response.sendRedirect(request.getContextPath() + "/welcome.jsp"); return; } } message = "usernamepassword不匹配,登陸失敗。"; // 登陸失敗 } %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01Transitional//EN"> <html> // ... HTML代碼爲一個FORM表單,代碼略 </html>登陸界面驗證用戶登陸信息,假設登陸正確。就把用戶信息以及登陸時間保存進Session,而後轉到歡迎頁面welcome.jsp。welcome.jsp中從Session中獲取信息,並將用戶資料顯示出來。
<%@ page language="java" pageEncoding="UTF-8"%> <jsp:directive.pageimport="com.helloweenvsfei.sessionWeb.bean.Person"/> <jsp:directive.page import="java.text.SimpleDateFormat"/> <jsp:directive.page import="java.text.DateFormat"/> <jsp:directive.page import="java.util.Date"/> <%! DateFormat dateFormat = newSimpleDateFormat("yyyy-MM-dd"); // 日期格式化器 %> <% Person person =(Person)session.getAttribute("person"); // 獲取登陸的person Date loginTime =(Date)session.getAttribute("loginTime"); // 獲取登陸時間 %> // ... 部分HTML代碼略 <table> <tr><td>您的姓名:</td> <td><%= person.getName()%></td> </tr> <tr><td>登陸時間:</td> <td><%= loginTime%></td> </tr> <tr><td>您的年齡:</td> <td><%= person.getAge()%></td> </tr> <tr><td>您的生日:</td> <td><%=dateFormat.format(person.getBirthday()) %></td> </tr> </table>注意程序中Session中直接保存了Person類對象與Date類對象,使用起來要比Cookie方便。
爲了得到更高的存取速度,server通常把Session放在內存裏。
每個用戶都會有一個獨立的Session。假設Session內容過於複雜,當大量客戶訪問server時可能會致使內存溢出。所以,Session裏的信息應該儘可能精簡。
Session在用戶第一次訪問server的時候本身主動建立。
需要注意僅僅有訪問JSP、Servlet等程序時纔會建立Session,僅僅訪問HTML、IMAGE等靜態資源並不會建立Session。假設還沒有生成Session。也可以使用request.getSession(true)強制生成Session。
Session生成後,僅僅要用戶繼續訪問。server就會更新Session的最後訪問時間,並維護該Session。用戶每訪問server一次。無論是否讀寫Session。server都以爲該用戶的Session「活躍(active)」了一次。
1.2.4 Session的有效期
由於會有愈來愈多的用戶訪問server,所以Session也會愈來愈多。爲防止內存溢出,server會把長時間內沒有活躍的Session從內存刪除。
這個時間就是Session的超時時間。假設超過了超時時間沒訪問過server,Session就本身主動失效了。
Session的超時時間爲maxInactiveInterval屬性,可以經過相應的getMaxInactiveInterval()獲取,經過setMaxInactiveInterval(longinterval)改動。
Session的超時時間也可以在web.xml中改動。另外。經過調用Session的invalidate()方法可以使Session失效。
1.2.5 Session的常常用法
Session中包含各類方法,使用起來要比Cookie方便得多。Session的常常用法如表1.2所看到的。
表1.2 HttpSession的常常用法
方法名
|
描寫敘述
|
void setAttribute(String attribute, Object value)
|
設置Session屬性。value參數可以爲不論什麼Java Object。一般爲Java Bean。 value信息不宜過大 |
String getAttribute(String attribute)
|
返回Session屬性
|
Enumeration getAttributeNames()
|
返回Session中存在的屬性名
|
void removeAttribute(String attribute)
|
移除Session屬性
|
String getId()
|
返回Session的ID。 該ID由server本身主動建立,不會反覆 |
long getCreationTime()
|
返回Session的建立日期。 返回類型爲long,常被轉化爲Date類型,好比:Date createTime = new Date(session.get CreationTime()) |
long getLastAccessedTime()
|
返回Session的最後活躍時間。返回類型爲long
|
int getMaxInactiveInterval()
|
返回Session的超時時間。單位爲秒。 超過該時間沒有訪問,server以爲該Session失效 |
void setMaxInactiveInterval(int second)
|
設置Session的超時時間。單位爲秒
|
boolean isNew()
|
返回該Session是不是新建立的
|
void invalidate()
|
使該Session失效
|
經過setMaxInactiveInterval(int seconds)改動超時時間。可以改動web.xml改變Session的默認超時時間。好比改動爲60分鐘:
<session-config> <session-timeout>60</session-timeout> <!-- 單位:分鐘 --> </session-config>注意:<session-timeout>參數的單位爲分鐘,而setMaxInactiveInterval(int s)單位爲秒。
1.2.6 Session對瀏覽器的要求
儘管Session保存在server,對client是透明的,它的正常執行仍然需要client瀏覽器的支持。
這是因爲Session需要使用Cookie做爲識別標誌。HTTP協議是無狀態的。Session不能根據HTTP鏈接來推斷是否爲同一客戶,所以server向client瀏覽器發送一個名爲JSESSIONID的Cookie,它的值爲該Session的id(也就是HttpSession.getId()的返回值)。Session根據該Cookie來識別是否爲同一用戶。
該Cookie爲server本身主動生成的,它的maxAge屬性通常爲–1。表示僅當前瀏覽器內有效,並且各瀏覽器窗體間不共享。關閉瀏覽器就會失效。
所以同一機器的兩個瀏覽器窗體訪問server時,會生成兩個不一樣的Session。但是由瀏覽器窗體內的連接、腳本等打開的新窗體(也就是說不是雙擊桌面瀏覽器圖標等打開的窗體)除外。這類子窗體會共享父窗體的Cookie,所以會共享一個Session。
注意:新開的瀏覽器窗體會生成新的Session,但子窗體除外。子窗體會共用父窗體的Session。
好比。在連接上右擊,在彈出的快捷菜單中選擇「在新窗體中打開」時。子窗體便可以訪問父窗體的Session。
假設client瀏覽器將Cookie功能禁用,或者不支持Cookie怎麼辦?好比,絕大多數的手機瀏覽器都不支持Cookie。
Java Web提供了還有一種解決方式:URL地址重寫。
1.2.7 URL地址重寫
URL地址重寫是對client不支持Cookie的解決方式。URL地址重寫的原理是將該用戶Session的id信息重寫到URL地址中。server能夠解析重寫後的URL獲取Session的id。這樣即便client不支持Cookie。也可使用Session來記錄用戶狀態。
HttpServletResponse類提供了encodeURL(Stringurl)實現URL地址重寫。好比:
<td> <a href="<%=response.encodeURL("index.jsp?c=1&wd=Java") %>"> Homepage</a> </td>該方法會本身主動推斷client是否支持Cookie。假設client支持Cookie,會將URL原封不動地輸出來。假設client不支持Cookie,則會將用戶Session的id重寫到URL中。重寫後的輸出多是這種:
<td> <a href="index.jsp;jsessionid=0CCD096E7F8D97B0BE608AFDC3E1931E?c=1&wd=Java">Homepage</a> </td>即在文件名稱的後面。在URL參數的前面加入了字符串「;jsessionid=XXX」。當中XXX爲Session的id。分析一下可以知道。增添的jsessionid字符串既不會影響請求的文件名稱,也不會影響提交的地址欄參數。
用戶單擊這個連接的時候會把Session的id經過URL提交到server上。server經過解析URL地址得到Session的id。
假設是頁面重定向(Redirection),URL地址重寫可以這樣寫:
<% if(「administrator」.equals(userName)) { response.sendRedirect(response.encodeRedirectURL(「administrator.jsp」)); return; } %>效果跟response.encodeURL(String url)是同樣的:假設client支持Cookie,生成原URL地址,假設不支持Cookie。傳回重寫後的帶有jsessionid字符串的地址。
對於WAP程序。由於大部分的手機瀏覽器都不支持Cookie。WAP程序都會採用URL地址重寫來跟蹤用戶會話。比方用友集團的移動商街等。
注意:TOMCAT推斷client瀏覽器是否支持Cookie的根據是請求中是否含有Cookie。
雖然client可能會支持Cookie,但是因爲第一次請求時不會攜帶不論什麼Cookie(因爲並沒有不論什麼Cookie可以攜帶),URL地址重寫後的地址中仍然會帶有jsessionid。當第二次訪問時server已經在瀏覽器中寫入Cookie了,所以URL地址重寫後的地址中就不會帶有jsessionid了。
1.2.8 Session中禁止使用Cookie
既然WAP上大部分的客戶瀏覽器都不支持Cookie。索性禁止Session使用Cookie,統一使用URL地址重寫會更好一些。Java Web規範支持經過配置的方式禁用Cookie。
如下舉例說一下如何經過配置禁止使用Cookie。
打開項目sessionWeb的WebRoot目錄下的META-INF目錄(跟WEB-INF目錄同級,假設沒有則建立),打開context.xml(假設沒有則建立)。編輯內容例如如下:
代碼1.11 /META-INF/context.xml
<?xml version='1.0' encoding='UTF-8'?> <Context path="/sessionWeb"cookies="false"> </Context>或者改動Tomcat全局的conf/context.xml,改動內容例如如下:
<!-- The contents of this file will be loaded for eachweb application --> <Context cookies="false"> <!-- ... 中間代碼略 --> </Context>部署後TOMCAT便不會本身主動生成名JSESSIONID的Cookie。Session也不會以Cookie爲識別標誌,而只以重寫後的URL地址爲識別標誌了。
也就是說server不會本身主動維護名爲JSESSIONID的Cookie了。但是程序中仍然可以讀寫其它的Cookie。