From Gossip@caterpillar
JSP/Servlet: session (1)
session隱含物件在轉換為Servlet之後,對應於 javax.servlet.http.HttpSession型態之物件, HttpSession負責管理客戶端在瀏覽網站期間的進程(session)資訊,也就是每個操作流程間的狀態維護,您無需知道進程追蹤的細節,就可以讓使用者在數個頁面之間瀏覽而又能保留相關的進程資訊,例如在幾個頁面之間挑選商品並放入購物車,在最後進行結帳工作。
之前在 Cookie 中介紹過進程追蹤的三種作法,其適用與不適用的場合各不相同。您可以將何時使用何種技術這個工作交給session(HttpSession)物件來負責,相關的實作細節並不太需要您來注意(當然您願意的話,建議您還是瞭解一下會更好)。
舉個例來說,session(HttpSession)會預設使用Cookie來追蹤進程,如果不行(例如使用者關閉Cookie的使用),則您可以試著使用URL rewriting,或許伺服器會保留一些資訊在伺服器的記憶體或資料庫甚至檔案中,而只傳送session id給客戶端,之後瀏覽器在每個請求中包括這個id,以讓伺服器取出對應的資訊,具體的作法依各家容器的實作而不同。
總而言之,您可以使用session(HttpSession)來協助您作進程追蹤,而不用擔心資訊在伺服端是如何保留的,而session id又是如何產生的。
下面簡單的示範session的使用方式,實作一個簡單的登入網頁,您可以大致瞭解session的使用方式,首先您必須先製作一個簡單的HTML表單,它將用來輸入使用者名稱與密碼:
form.htm
<html> <head><title>登入頁面</title></head> <body> 輸入密碼登入:<br> <form action="login.jsp" method="POST"> 名稱:<input type="text" name="user"> <br> 密碼:<input type="password" name="password"> <br> <input type="submit" value="登入"> </form> </body> </html>
接下來撰寫登入驗證頁面login.jsp:
login.jsp
<%@page contentType="text.html;charset=Big5"%> <% String user = request.getParameter("user"); String password = request.getParameter("password"); String memberURL = "http://localhost:8080/myjsp/member.jsp"; String loginFormURL = "http://localhost:8080/myjsp/form.html"; if(user == null || password == null) { response.setHeader("Refresh", "0; " + loginFormURL); } else if(user.equals("justin") && password.equals("1234")) { session.setAttribute("user", user); response.setHeader("Refresh", "3; " + memberURL); out.println(user + "歡迎登入!3秒後進入會員頁面!"); } else { response.setHeader("Refresh", "3; " + loginFormURL); out.println( "使用者或密碼錯誤,請重新登入(3秒後回到登入表單)"); } %>
上面這個頁面在驗證使用者名稱與密碼無誤之後,使用setAttribute()方法,將使用者名稱存入session (HttpSession)物件中,setAttribute()可以存入任何的Java物件,在這邊存入的屬性名稱為"user",存入的物件為 String物件,內容為登入者的名稱,也就是"justin",然後將客戶端重導至會員頁面,如果驗證的結果不正確,就將客戶端重導至登入表單;接下來撰寫會員頁面member.jsp:
member.jsp
<%@page contentType="text.html;charset=Big5"%> <% String loginFormURL = "http://localhost:8080/myjsp/form.html"; String user = (String) session.getAttribute("user"); if(user == null) { response.setHeader("Refresh", "3; " + loginFormURL); out.println( "喔喔!您還沒登入!不能偷看喔!3秒後進入登入頁面"); } else { out.println("嗨!" + user + "!"); out.println("會員好康在這邊。。。。"); // 立即登出 session.invalidate(); } %>
getAttribute()可以指定屬性名稱取得存在session(HttpSession)中的物件,取回的型態是 Object,為了能夠使用,您必須將之轉換為相對應的型態,在這邊則是String型態,如果之前login.jsp中驗證通過的話,在這個 member.jsp中就可以取得儲存在session中的屬性物件,如果取回的是null,表示之前沒有設定過user屬性,也就是在 login.jsp中沒有通過驗證,我們必須將之送回登入頁面以重新進行登入;invalidate()可以讓目前的session物件失效,我們在 member.jsp中執行這個方法,以實作登出的功能(其實也是為了測試的方便而讓使用者立即登出)。
上面這個例子中,完全沒有牽涉到進程追蹤的實作細節(cookie或是URL rewriting等),一切都是交由session物件來負責,而由於session可以儲存物件資訊,使得進程追蹤所關聯的資訊更為豐富,但卻不用增加您實作進程追蹤的困難,不過上面這個例子是在瀏覽器的Cookie功能有打開的情況下才能正常運作,如果Cookie沒有開啟,則我們需要作一些額外的動作,這在下一個主題中討論。
要注意的是,session並不是執行緒安全的,由於session會在客戶端對話期間存在,所以當有多個執行緒會存取session時,必須注意到執行緒安全問題。
From Gossip@caterpillar
JSP/Servlet: session (2)
session(HttpSession)物件會在使用者第一次存取Web伺服器時產生,伺服器會產生一個獨特的 session id來表示這個客戶端,瀏覽器在之後的每次請求中都包括這個session id(可能是使用Cookie或是URL rewriting,這個細節不用您來擔心),伺服器根據這個session id來對應該客戶端的session物件,您可以使用getId()方法來取得session id,例如:
<%
out.println("Session ID:" + session.getId());
%>
顯示的session id型式如下:
Session ID:2F892EDF2669858811B8D121119AE90B
session id預設是使用Cookie來儲存於客戶端,並在每一次瀏覽器發送請求時夾帶這個訊息給伺服器,伺服器根據session id來對應出HttpSession物件,假如Cookie沒有開啟,則瀏覽器將無法儲存session id,也就無法將session id的訊息傳送給伺服器,也就無法進行進程追蹤,即使資料物件確實儲存於HttpSession中,您也無法取出,下面這個程式在瀏覽器Cookie功能關閉的情況下,只會一直顯示session not found, reset!訊息:
sessionDemo.jsp
<%@page contentType="text/html;charset=Big5"%> <html> <head><title>session demo</title></head> <body> <H1> <% if(session.getAttribute("info") == null) { session.setAttribute("info", "session information"); out.println("session not found, reset!"); } else { out.println("session found: " + session.getAttribute("info")); } %> </H1> </body> </html>
如果Cookie功能關閉,則session id無法儲存,也就無法在下一次請求時一併送至伺服器,為了讓進程追蹤得以進行,您必須使用URL rewriting來傳送session id,所幸的是有一個簡單的方法可以幫您進行這個動作,使用response的encodeURL()可以自動將session id編進URL中,例如:
sessionDemo.jsp
<%@page contentType="text/html;charset=Big5"%> <html> <head><title>session demo</title></head> <body> <H1> <% if(session.getAttribute("info") == null) { session.setAttribute("info", "session information"); out.println("session not found, reset!"); } else { out.println("session found: " + session.getAttribute("info")); } out.println("<br><a href='" + response.encodeURL("sessionDemo.jsp") + "'>" + "進程追蹤" + "</a>"); %> </H1> </body> </html>
如果您的瀏覽器Cookie功能關閉,您必須使用response的encodeURL()自動將session id編進URL中,如果Cookie功能可以運作,則encodeURL()會原封不動的傳回指定的URL,否則會在指定的URL後加上sessiond id,例如上面的JSP網頁在Cookie功能關閉的情況下,會傳回以下的內容:
<html>
<head><title>session demo</title></head>
<body>
<H1>
session not found, reset!
<br><a href='sessiondemo.jsp;jsessionid=
7A2A0BFA32D0022D8BB80A5E690A9D10'>進程追蹤</a>
</H1>
</body>
</html>
簡單的說,按下經過URL rewriting的連結,瀏覽器就可以將session id傳送至伺服器,然而您的網址列上就會出現session id的訊息:
http://localhost:8080/myjsp/sessionDemo.jsp;jsessionid=7A2A0BFA32D0022D8BB80A5E690A9D10
這是一個有危險性的訊息,任何人只要在session的存活期限獲得這個訊息,就可以進行進程追蹤,所以基本上還是建議使用者開啟Cookie功能,以免 session id曝露在網址列上。
在上一個主題中的登入網頁如果在Cookie功能關閉的情況下也將無法運作,您必須這樣改寫login.jsp:
login.jsp
<%@page contentType="text.html;charset=Big5"%> <% String user = request.getParameter("user"); String password = request.getParameter("password"); String memberURL = "http://localhost:8080/myjsp/member.jsp"; String loginFormURL = "http://localhost:8080/myjsp/form.html"; if(user == null || password == null) { response.setHeader("Refresh", "0; " + loginFormURL); } else if(user.equals("justin") && password.equals("1234")) { session.setAttribute("user", user); memberURL = response.encodeURL(memberURL); response.setHeader("Refresh", "3; " + memberURL); out.println(user + "歡迎登入!3秒後進入會員頁面!"); } else { response.setHeader("Refresh", "3; " + loginFormURL); out.println( "使用者或密碼錯誤,請重新登入(3秒後回到登入表單)"); } %>
或者是您可以直接使用response的sendRedirect()方法,由於sendRedirect()要求完整的位址訊息,也就是包括http: //開始的位址訊息,您可以使用response的encodeRedirectURL()傳回這個位址,同樣的,如果 Cookie有開啟,則只是原封不動傳回原來指定的URL,您也可以改寫login.jsp程式如下:
login.jsp
<%@page contentType="text.html;charset=Big5"%> <% String user = request.getParameter("user"); String password = request.getParameter("password"); String memberURL = "http://localhost:8080/myjsp/member.jsp"; String loginFormURL = "http://localhost:8080/myjsp/form.html"; if(user == null || password == null) { response.setHeader("Refresh", "0; " + loginFormURL); } else if(user.equals("justin") && password.equals("1234")) { session.setAttribute("user", user); memberURL = response.encodeRedirectURL(memberURL); response.sendRedirect(memberURL); } else { response.setHeader("Refresh", "3; " + loginFormURL); out.println( "使用者或密碼錯誤,請重新登入(3秒後回到登入表單)"); } %>
session具有其存活期限,關閉瀏覽器、伺服器關閉可能讓session失效,當客戶端停止活動一段時間(Tomcat預設是30分鐘), session會自動失效,您可以使用getMaxInactiveInterval()取得session的等待期限,取得的值以秒為單位,或是以 setMaxInactiveInterval()設定等待期限,設定的值也是以秒為單位:
<%
out.println("default session life: " +
session.getMaxInactiveInterval());
session.setMaxInactiveInterval(600);
out.println("now session life: " +
session.getMaxInactiveInterval());
%>
您可以在web.xml中設定預設的session等待期限,使用<session-config>與<session- timeout>來進行設定,注意設定的單位為分鐘數,例如下面的設定將session等待期限預設為10分鐘:
web.xml
... <session-config> <session-timeout> 10 <!--分鐘--> </session-timeout> </session-config> ...
有時,看到一篇好的繁體文章想收集,可是許多亂碼。今天把網站使用的字符集改成utf-8了。