<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    weidagang2046的專欄

    物格而后知致
    隨筆 - 8, 文章 - 409, 評論 - 101, 引用 - 0
    數據加載中……

    深入理解和改進JSP/Servlet會話管理機制

    ?在Web服務器端編程中,會話狀態管理是一個經常必須考慮的重要問題。本文分析JSP/Servlet的會話管理機制及其所面臨的問題,然后提出了一種改進的會話管理方法。?

    一、Servlet的會話管理機制?

    根據設計,HTTP是一種無狀態的協議。它意味著Web應用并不了解有關同一用戶以前請求的信息。維持會話狀態信息的方法之一是使用Servlet或者 JSP容器提供的會話跟蹤功能。Servlet?API規范定義了一個簡單的HttpSession接口,通過它我們可以方便地實現會話跟蹤。?

    HttpSession 接口提供了存儲和返回標準會話屬性的方法。標準會話屬性如會話標識符、應用數據等,都以“名字-值”對的形式保存。簡而言之,HttpSession接口提供了一種把對象保存到內存、在同一用戶的后繼請求中提取這些對象的標準辦法。在會話中保存數據的方法是setAttribute(String?s, ?Object?o),從會話提取原來所保存對象的方法是getAttribute(String?s)。?

    在HTTP協議中,當用戶不再活動時不存在顯式的終止信號。由于這個原因,我們不知道用戶是否還要再次返回,如果不采取某種方法解決這個問題,內存中會積累起大量的HttpSession對象。?

    為此,Servlet采用“超時限制”的辦法來判斷用戶是否還在訪問:如果某個用戶在一定的時間之內沒有發出后繼請求,則該用戶的會話被作廢,他的 HttpSession對象被釋放。會話的默認超時間隔由Servlet容器定義。這個值可以通過getMaxInactiveInterval方法獲得,通過setMaxInactiveInterval方法修改,這些方法中的超時時間以秒計。如果會話的超時時間值設置成-1,則會話永不超時。 Servlet可以通過getLastAccessedTime方法獲得當前請求之前的最后一次訪問時間。?

    要獲得HttpSession對象,我們可以調用HttpServletRequest對象的getSession方法。為了正確地維持會話狀態,我們必須在發送任何應答內容之前調用getSession方法。?
    用戶會話既可以用手工方法作廢,也可以自動作廢。作廢會話意味著從內存中刪除HttpSession對象以及它的數據。例如,如果一定時間之內(默認30分鐘)用戶不再發送請求,Java?Web?Server自動地作廢他的會話。?

    Servlet/JSP會話跟蹤機制有著一定的局限,比如:?

    ·?會話對象保存在內存之中,占用了可觀的資源。?

    ·?會話跟蹤依賴于Cookie。由于各種原因,特別是安全上的原因,一些用戶關閉了Cookie。?

    ·?會話跟蹤要用到服務器創建的會話標識符。在多個Web服務器以及多個JVM的環境中,Web服務器不能識別其他服務器創建的會話標識符,會話跟蹤機制無法發揮作用。?
    要深入理解會話跟蹤機制,首先我們必須理解在Servlet/JSP容器中會話如何運作。?


    二、會話標識符?

    每當新用戶請求一個使用了HttpSession對象的JSP頁面,JSP容器除了發回應答頁面之外,它還要向瀏覽器發送一個特殊的數字。這個特殊的數字稱為“會話標識符”,它是一個唯一的用戶標識符。此后,HttpSession對象就駐留在內存之中,等待同一用戶返回時再次調用它的方法。?

    在客戶端,瀏覽器保存會話標識符,并在每一個后繼請求中把這個會話標識符發送給服務器。會話標識符告訴JSP容器當前請求不是用戶發出的第一個請求,服務器以前已經為該用戶創建了HttpSession對象。此時,JSP容器不再為用戶創建新的HttpSession對象,而是尋找具有相同會話標識符的 HttpSession對象,然后建立該HttpSession對象和當前請求的關聯。?

    會話標識符以Cookie的形式在服務器和瀏覽器之間傳送。如果瀏覽器不支持Cookie又如何呢?此時,對服務器的后繼請求將不會帶有會話標識符。結果,JSP容器認為該請求來自一個新用戶,它會再創建一個HttpSession對象,而以前創建的HttpSession對象仍舊駐留在內存中,但該用戶以前的會話信息卻丟失了。?

    另外,Servlet/JSP容器只認可它自己創建的會話標識符。如果同一Web應用在“Web農場”(Web?farm)的多臺服務器上運行,則必須存在這樣一種機制:保證來自同一用戶的請求總是被定向到處理該用戶第一次請求的服務器。?

    三、偽會話管理機制?

    如前所述,基于Cookie的會話管理技術面臨著種種問題。下面我們要設計一種新的會話管理機制來解決這些問題。這種會話管理機制稱為“偽會話”(Pseudo?Session)機制,它具有如下特點:?

    ·?對象和數據不是保存在內存中,而是以文本文件形式保存。每一個文本文件與一個特定的用戶關聯,文件的名字就是會話的標識符。因此,文件名字必須是唯一的。?
    ·?文本文件保存在一個專用的目錄中,所有Web服務器都可以訪問這個目錄。因此,偽會話可以用于Web農場。?
    ·?會話標識符不作為Cookie發送,而是直接編碼到URL里面。因此,采用偽會話技術要求修改所有的超級鏈接,包括HTML表單的ACTION屬性。?
    此外,實現偽會話管理機制時我們還要考慮到以下幾點:?
    ·?它應該與應用無關,其他想要實現同樣功能的開發者應該能夠方便地重用它。?
    ·?考慮到安全原因,應該有一種為會話標識符生成隨機數字的辦法。?
    ·?為了作廢過期的會話,應該設定一個超時值。同一個用戶,如果他超過一定的時間之后再次返回,他將獲得一個新的會話標識符。此舉能夠防止未經授權的用戶冒用其他人的會話。?
    ·?應該有一種收集過期會話并刪除相應文本文件的機制。?
    ·?如果用戶使用已經過期的會話標識符再次訪問服務器,即使這個會話標識符的文本文件還沒有刪除,系統也不應該允許用戶使用原來的會話。?
    ·?同時,應該存在一種更新會話文本文件最后改動時間的機制,使得用戶在會話過期時限之前返回時會話總是保持最新且合法的狀態數據。?


    四、實現偽會話管理機制?

    下面所介紹的工程稱為PseudoSession,它是偽會話機制一個很簡單的實現。考慮到移植性,我們以JavaBean的形式實現它。PseudoSessionBean的完整代碼可以從本文后面下載。?

    PseudoSessionBean擁有如下域(Field):?
    public?String?path;public?long?timeOut;?
    path是保存所有會話文本文件的目錄。如果Web服務器的數量在一個以上,這個目錄必須允許所有服務器訪問。然而,為了防止用戶直接訪問這些文本文件,這個路徑應該不允許用戶直接訪問。解決這個問題的一種方法是使用Web網站根之外的目錄。?

    timeOut 是用戶的最后一個請求到會話過期作廢之間的時間。在PseudoSessionBean的代碼清單中,timeOut設置成了以毫秒表示的20分鐘,這是一個比較合理的超時時間值。對于任何用戶,如果他在這個超時時間之后才繼續發出請求,他將得到一個新的會話標識符。?
    PseudoSessionBean有4個方法:getSessionID,setValue,getValue,deleteAllInvalidSessions。?

    4.1?getSessionID方法?
    getSessionID方法的聲明如下:?
    public?String?getSessionID(HttpServletRequest?request)?
    這個方法應該在每一個JSP頁面的開頭調用。它完成如下任務:?
    ·?如果用戶是第一次訪問,則為該用戶設定一個新的會話標識符。?
    ·?檢查URL所帶會話標識符的合法性。如果會話標識符已經過期,則getSessionID方法返回一個新的會話標識符。?
    下面我們來看看getSessionID方法的工作過程。?
    String?sessionId?=?request.getParameter("sessionId");?
    validSessionIdFound是一個標記,用于指示會話標識符是否合法。validSessionIdFound的初始值是false。?
    boolean?validSessionIdFound?=?false;?
    long類型的now變量包含請求出現時的服務器時間。該變量用于確定用戶會話的合法性。?
    long?now?=?System.currentTimeMillis();?
    如果找到了會話標識符,則getSessionID方法檢查它的合法性。檢查過程如下:?
    ·?一個合法的會話標識符必須有對應的同名文本文件。?
    ·?文件的最后修改時間加上timeOut應該大于當前時間。?
    ·?如果存在與會話對應的文本文件,但文件已經過期,則原來的文件被刪除。?
    ·?把合法會話標識符所對應文本文件的最后修改日期改為now。?
    這些任務主要借助File對象完成,創建File對象的參數就是會話文本文件的路徑:?
    if? (sessionId!=null)?{File?f?=?new?File(path?+?sessionId);if?(f.exists())?{?if? (f.lastModified()?+?timeOut?>?now)?{?//?會話合法//?使用setLastModified時,如果文件已經被其他程序鎖定,//?程序不會產生任何異常,但文件數據不會改變f.setLastModified(now); validSessionIdFound?=?true;?}?else?{?//?會話已經過期?//?刪除文件f.delete();?}} ?//?end?if?(f.exists)?}?//?end?if?(sessionId!=null)?
    如果不存在合法的會話標識符,則getSessionID方法生成一個會話標識符以及相應的文本文件:?
    if? (!validSessionIdFound)?{?sessionId?=?Long.toString(now);?//?創建文件?File?f? =?new?File(path?+?sessionId);?try?{f.createNewFile();?}?catch?(IOException?ioe)?{}}?//?end?of?if?!validSessionIdFound?
    程序保證文件名字隨機性的方法非常簡單:把當前的系統時間直接轉換成會話標識符。對于那些涉及敏感數據的應用,我們應該考慮運用更安全的隨機數生成器來生成會話標識符。?
    綜上所述,getSessionID并不總是返回新的合法會話標識符:它返回的標識符可能與傳遞給它的標識符相同,也可能是新創建的會話標識符。?
    為了保證JSP頁面擁有合法的會話標識符以便調用setValue、getValue方法,每個JSP頁面都必須在開頭位置調用getSesstionID方法。?


    4.2?setValue方法?
    setValue 方法保存value字符串以及與它關聯的字符串名字。這種“名字-值”對很容易使人想起Dictionary對象。setValue方法要求在第一個參數中提供合法的會話標識符,它假定在自己被調用之前getSessionID方法已經執行,經過檢驗的合法會話標識符必然存在,因此它不再對傳入的會話標識符進行合法性檢驗。?
    setValue方法按如下規則保存名字-值對:?
    ·?如果與value值關聯的name以前還沒有保存過,則新的名字-值對加入到文本文件的末尾。?
    ·?如果value字符串關聯的name值以前已經保存過,則原來保存的值被新的value值替換。?
    setValue方法按照如下格式保存名字-值對,注意“名字”是大小寫敏感的:?
    name-1?value-1name-2?value-2name-3?value-3...name-n?value-n?
    setValue方法的聲明如下:?
    public?void?setValue(String?sessionId,?String?name,?String?value)?
    setValue 方法首先尋找與當前會話對應的文本文件。如果不能找到文本文件,則setValue方法不做任何事情直接返回。如果找到了會話文本文件,setValue 方法讀取文本文件的各個行,然后比較讀入的行與name:如果讀入的文本行開頭與name一樣,則說明該名字已經保存,setValue方法將替換該行后面的值;如果name不能與讀入的文本行匹配,則這行文本被直接復制到一個臨時文件。?
    這部分功能的實現代碼如下:?
    try?{?FileReader?fr?=?new?FileReader(path?+?sessionId);?BufferedReader?br?=?new?BufferedReader(fr);?FileWriter?fw?=?new?FileWriter(path?+?sessionId?+?".tmp");?BufferedWriter?bw?=?new?BufferedWriter(fw);?String?s;?while?((s?=?br.readLine())?!=?null)if?(!s.startsWith(name?+?"?"))?{?bw.write(s);?bw.newLine();}?bw.write(name?+?"?"?+?value);?bw.newLine();?bw.close();?br.close();?fw.close();?bw.close();?.?.?.}catch?(FileNotFoundException?e)?{}catch?(IOException?e)?{?System.out.println(e.toString());}?
    原來文本文件中的所有行復制到臨時文件之后,setValue方法刪除原來的文本文件,然后把臨時文件改成會話文本文件的名字:?
    File?f?=?new?File(path?+?sessionId?+?".tmp");File?dest?=?new?File(path?+?sessionId);dest.delete();f.renameTo(dest);?


    4.3?getValue方法?
    getValue 方法用于提取原來保存在偽會話中的數據。正如setValue方法,getValue方法也要求傳入一個合法的會話標識符,而且getValue方法不再對傳入的會話標識符進行合法性檢查。getValue方法的第二個參數是待提取數據的name,返回值是與指定name關聯的value。?
    getValue方法的聲明如下:?
    public?String?getValue(String?sessionId,?String?name)?
    getValue方法的基本執行過程如下:首先找到會話文本文件,然后按行讀入直至找到與name匹配的文本行;找到匹配的文本行之后,getValue方法返回該行保存的value;如果不能找到,getValue方法返回null。?


    4.4?deleteAllInvalidSessions方法?
    deleteAllInvalidSessions 方法刪除那些與已經過期的會話關聯的文本文件。由于調用getSessionID方法時過期的會話文本文件會被刪除, deleteAllInvalidSessions方法并不是關鍵的方法。什么時候調用這個方法由應用自己決定。例如,我們可以編寫一個專用的后臺程序,由這個程序每天一次清除所有過期的文本文件。最簡單的辦法是在JSP文件末尾調用deleteAllInvalidSessions方法,但如果網站比較繁忙,重復地調用deleteAllInvalidSessions方法將降低整個網站的響應能力。一種明智的做法是:編寫一個在訪問量較少的時候自動進行清理的后臺程序。?
    deleteAllInvalidSessions方法的聲明如下:?
    public?void?deleteAllInvalidSessions()?
    它首先把所有會話文本文件的名字讀入files字符串數組:?
    File?dir?=?new?File(path);?String[]?files?=?dir.list();?
    deleteAllInvalidSessions方法比較文本文件的最后修改時間(加上超時時間)和系統當前時間,確定會話是否過期。long類型的變量now用于保存系統的當前時間。?
    long?now?=?System.currentTimeMillis();?
    接下來,deleteAllInvalidSessions方法通過循環訪問files數組,依次檢查每個文件的lastModified屬性。所有與過期會話關聯的文件都將被刪除:?
    for? (int?i=0;?i<files.length;?i++)?{?File?f?=?new?File(path?+?files[i]);?if? (f.lastModified()?+?timeOut?>?now)?f.delete();?//?刪除過期的會話文本文件}?


    五、應用實例?


    編譯好PseudoSessionBean這個JavaBean之后,我們就可以利用偽會話管理機制來管理Web應用的會話狀態信息了。由于不必再使用服務器的會話管理機制,我們可以在page指令中把session屬性設置為false關閉默認的JSP/Servlet會話管理功能。?
    <%@?page?session="false"?%>?

    然后,我們用JSP的<jsp:useBean>標記告訴JSP容器程序要使用PseudoSessionBean:?
    <jsp:useBean?id="PseudoSessionId"?scope="application"?class="pseudosession.PseudoSessionBean"?/>?

    在上面這個<jsp:useBean>標記中,class屬性值是“包.類名字”形式。當然,對于不同的包名字,class屬性的值應該作相應的修改。注意Bean的scope屬性是“application”,這是因為我們要在應用的所有頁面中使用這個Bean。在這個應用中,把Bean的 scope屬性設置為“application”具有最好的效率,因為我們只需創建Bean對象一次就可以了。另外,正如前面所提到的, getSessionID方法必須在所有其他代碼之前調用。?
    <%?String?sessionId?=?PseudoSessionId.getSessionID(request);%>?
    為了說明PseudoSessionBean的應用,下面我們來看兩個JSP頁面,它們是index.jsp和secondPage.jsp。index.jsp頁面在偽會話變量中保存用戶的名字,而secondPage.jsp則提取這個用戶名字。?

    index.jsp頁面的代碼如下:?
    <%@?page?session="false"?contentType="text/html;charset=gb2312"?%>
    <jsp:useBean?id="PseudoSessionId"?scope="application"?class="pseudosession.PseudoSessionBean"?/>
    <%?String?sessionId?=?PseudoSessionId.getSessionID(request);%>
    <html>
    <head>
    <title>偽會話</title>
    </head>
    <body>
    <h1>偽會話管理機制</h1>
    <%?String?userName?=?"bulbul";?PseudoSessionId.setValue(sessionId,?"userName",?userName);%>
    <a?href=secondPage.jsp?sessionId=<%=sessionId%>>點擊此處</a>
    <form?method="post"?action=anotherPage.jsp?sessionId=<%=sessionId%>>
    輸入數據:<input?type="text"?name="sample">
    <input?type="submit"?name="Submit"?value="Submit">
    </form>
    </body>
    </html>
    <%?PseudoSessionId.deleteAllInvalidSessions();%>?

    注意,包括<form>標記的action屬性在內,所有的超級鏈接都已經改寫,現在都包含了會話標識符。另外也請注意頁面的最后調用了deleteAllInvalidSessions方法。?
    secondPage.jsp頁面只簡單地返回以前保存的用戶名字。?
    <%@?contentType="text/html;charset=gb2312"?page?session="false"?%>
    <jsp:useBean?id="PseudoSessionId"?scope="application"?class="pseudosession.PseudoSessionBean"?/>
    <%?String?sessionId?=?PseudoSessionId.getSessionID(request);%>
    <html>
    <head>
    <title>第2個頁面</title>
    </head>
    <body>
    <%?String?userName?=?PseudoSessionId.getValue(sessionId,?"userName");?out.println("用戶名字是?"?+?userName);%>
    </body>
    </html>

    from: http://www.tech521.com/autoGenFile/techData/17/1708.htm

    posted on 2006-10-31 20:11 weidagang2046 閱讀(237) 評論(0)  編輯  收藏 所屬分類: Java

    主站蜘蛛池模板: 边摸边吃奶边做爽免费视频网站 | 77777亚洲午夜久久多喷| 亚洲三级在线视频| 亚洲国产精品成人AV在线| 草久免费在线观看网站| a级成人毛片免费视频高清| 在线美女免费观看网站h| 免费人成视频在线| 亚洲毛片不卡av在线播放一区| 国产精品亚洲精品日韩已方| 亚洲avav天堂av在线不卡| 激情亚洲一区国产精品| 免费人成再在线观看网站| 很黄很污的网站免费| 猫咪社区免费资源在线观看| 亚洲国产精品激情在线观看| 亚洲va国产va天堂va久久| 456亚洲人成在线播放网站| 深夜福利在线视频免费| 99re在线这里只有精品免费| 日韩在线a视频免费播放| 亚洲中文字幕久久精品无码APP | 国产精品免费观看| 免费萌白酱国产一区二区| 久久久久亚洲av无码尤物| 亚洲日本一线产区和二线产区对比| sss日本免费完整版在线观看| 最近高清中文字幕免费| 无码欧精品亚洲日韩一区夜夜嗨 | 亚洲一区二区女搞男| 亚洲人成网站日本片| 一级毛片不卡免费看老司机| 99久热只有精品视频免费看| 国产一级淫片视频免费看| 久久亚洲精精品中文字幕| 污视频网站免费观看| av无码国产在线看免费网站| 久久久久亚洲AV无码专区网站| 亚洲影视自拍揄拍愉拍| 中文无码成人免费视频在线观看 | 国产成人午夜精品免费视频|