Cookie 和 Session機制具體解釋

    原文地址:http://blog.csdn.net/fangaoxin/article/details/6952954
    會話(Session)跟蹤是Web程序中常用的技術,用來跟蹤用戶的整個會話。

常用的會話跟蹤技術是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的內容。緩存


    注意:

  • Cookie功能需要瀏覽器的支持。

  • 假設瀏覽器不支持Cookie(如大部分手機中的瀏覽器)或者把Cookie禁用了。Cookie功能就會失效。

  • 不一樣的瀏覽器採用不一樣的方式保存Cookie。
  • IE瀏覽器會在「C:\Documents and Settings\你的username\Cookies」目錄下以文本文件形式保存,一個文本文件保存一個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屬性。
    注意:domain參數必須以點(".")開始。另外,name一樣但domain不一樣的兩個Cookie是兩個不一樣的Cookie。假設想要兩個域名全然不一樣的站點共同擁有Cookie,可以生成兩個Cookie。domain屬性分別爲兩個域名。輸出到client。
1.1.10 Cookie的路徑
    domain屬性決定執行訪問Cookie的域名。而path屬性決定贊成訪問Cookie的路徑(ContextPath)。好比,假設僅僅贊成/sessionWeb/下的程序使用Cookie,可以這麼寫:
Cookie cookie = new Cookie("time","20080808");     // 新建Cookie
cookie.setPath("/sessionWeb/");                          // 設置路徑
response.addCookie(cookie);                           // 輸出到client
    設置爲「/」時贊成所有路徑使用Cookie。path屬性需要使用符號「/」結尾。name一樣但domain一樣的兩個Cookie也是兩個不一樣的Cookie。
    注意:頁面僅僅能獲取它屬於的Path的Cookie。好比/session/test/a.jsp不能獲取到路徑爲/session/abc/的Cookie。使用時必定要注意。
1.1.11 Cookie的安全屬性
    HTTP協議不只是無狀態的,而且是不安全的。使用HTTP協議的數據不通過不論什麼加密就直接在網絡上傳播,有被截獲的可能。使用HTTP協議傳輸很是機密的內容是一種隱患。假設不但願Cookie在HTTP等非安全協議中傳輸。可以設置Cookie的secure屬性爲true。瀏覽器僅僅會在HTTPS和SSL等安全協議中傳輸此類Cookie。

如下的代碼設置secure屬性爲true:

Cookie cookie = new Cookie("time", "20080808"); // 新建Cookie
cookie.setSecure(true);                           // 設置安全屬性
response.addCookie(cookie);                        // 輸出到client
    提示:secure屬性並不能對Cookie內容加密。於是不能保證絕對的安全性。假設需要高安全性。需要在程序中對Cookie內容加密、解密,以防泄密。
1.1.12 JavaScript操做Cookie
    Cookie是保存在瀏覽器端的,所以瀏覽器具備操做Cookie的先決條件。瀏覽器可以使用腳本程序如JavaScript或者VBScript等操做Cookie。這裏以JavaScript爲例介紹常用的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>
<% } %>

1.2 Session機制
    除了使用Cookie,Web應用程序中還經常使用Session來記錄client狀態。

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中獲取信息,並將用戶資料顯示出來。
    welcome.jsp:
<%@ 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方便。
    當多個client運行程序時,server會保存多個client的Session。獲取Session的時候也不需要聲明獲取誰的Session。Session機制決定了當前客戶僅僅會獲取到本身的Session,而不會獲取到別人的Session。各客戶的Session也彼此獨立,互不可見。
    提示:Session的使用比Cookie方便,但是過多的Session存儲在server內存中。會對server形成壓力。
1.2.3 Session的生命週期
    Session保存在server端。

爲了得到更高的存取速度,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失效
    Tomcat中Session的默認超時時間爲20分鐘。

經過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,改動內容例如如下:
    代碼1.12 context.xml
<!-- The contents of this file will be loaded for eachweb application -->
<Context cookies="false">
    <!-- ... 中間代碼略 -->
</Context>
    部署後TOMCAT便不會本身主動生成名JSESSIONID的Cookie。Session也不會以Cookie爲識別標誌,而只以重寫後的URL地址爲識別標誌了。
    注意:該配置僅僅是禁止Session使用Cookie做爲識別標誌,並不能阻止其它的Cookie讀寫。

也就是說server不會本身主動維護名爲JSESSIONID的Cookie了。但是程序中仍然可以讀寫其它的Cookie。

相關文章
相關標籤/搜索