通俗來說就是客戶和服務器的一次私密談話,客戶發送請求以後服務器能夠識別請求是來自同一個客戶,他們是1對1的關系。
了解會話以後我們就要去考慮如何去實現這些問題下面一一進行解析
竟然服務器能別識別不同的用戶,但是他是如何識別的呢,這裡就說到了SessionId,它是Session的唯一識別,保存在cookies中存放於本地硬盤裡面,每次客戶請求的時候會把SessionId一起傳給服務器,那麼服務器就能根據SessionId來識別Session。那麼下面我們用代碼來演示Session的運行方式
第一步:我們先寫一個LoginServlet類
1 public class LoginServlet extends HttpServlet { 2 private static final long serialVersionUID = 1L; 3 4 /** 5 * @see HttpServlet#HttpServlet() 6 */ 7 public LoginServlet() { 8 super(); 9 } 10 11 /** 12 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response) 13 */ 14 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 15 response.setContentType("text/html;charset=gb2312"); 16 HttpSession session=request.getSession(); 17 String userName=(String)session.getAttribute("username"); 18 19 PrintWriter out =response.getWriter(); 20 out.println("<html>"); 21 out.println("<meta http-equiv=\"pragma\" content=\"no-cache\">"); 22 out.println("<head><title>登錄頁面</title></head>"); 23 out.println("<body>"); 24 25 printSessionInfo(out, session); 26 out.println("<p>"); 27 out.println("<form action=loginchk method=post>"); 28 out.println("<table>"); 29 out.println("<tr>"); 30 out.println("<td>請輸入用戶名:</td>"); 31 if(userName==null) 32 { 33 out.println("<td><input type=text name=username></td>"); 34 } 35 else { 36 out.println("<td><input type=text name=username value="+userName+"></td>"); 37 } 38 out.println("</tr>"); 39 40 out.println("<tr>"); 41 out.println("<td>請輸入密碼:</td>"); 42 out.println("<td><input type=password name=password></td>"); 43 out.println("</tr>"); 44 45 out.println("<tr>"); 46 out.println("<td><input type=reset value=重填></td>"); 47 out.println("<td><input type=submit value=登錄></td>"); 48 out.println("</tr>"); 49 50 out.println("</table>"); 51 out.println("</form>"); 52 out.println("</body>"); 53 out.println("</html>"); 54 } 55 56 /** 57 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response) 58 */ 59 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 60 doGet(request, response); 61 } 62 /** 63 * 打印與session相關的信息 64 * @param out 65 * @param session 66 */ 67 public void printSessionInfo(PrintWriter out,HttpSession session) 68 { 69 out.println("<table>"); 70 out.println("<tr>"); 71 out.println("<td>會話的狀態:</td>"); 72 if(session.isNew()) 73 { 74 out.println("<td>新的會話</td>"); 75 } 76 else { 77 out.println("<td>舊的會話</td>"); 78 } 79 out.println("</tr>"); 80 81 out.println("<tr>"); 82 out.println("<td>會話ID:</td>"); 83 out.println("<td>"+session.getId()+"</td>"); 84 out.println("</tr>"); 85 86 out.println("<tr>"); 87 out.println("<td>創建時間:</td>"); 88 out.println("<td>"+new Date(session.getCreationTime())+"</td>"); 89 out.println("</tr>"); 90 91 out.println("<tr>"); 92 out.println("<td>上次訪問時間:</td>"); 93 out.println("<td>"+new Date(session.getLastAccessedTime())+"</td>"); 94 out.println("</tr>"); 95 96 out.println("<tr>"); 97 out.println("<td>最大不活動時間間隔:</td>"); 98 out.println("<td>"+session.getMaxInactiveInterval()+"</td>"); 99 out.println("</tr>"); 100 out.println("</table>"); 101 } LoginServlet
在代碼中我們首先獲取Session,(如果沒有的話Tomcat容器會自動創建一個Session)然後打印出Session的相關信息。然後在渲染出來登錄界面
第二步:我們在寫一個LoginChkServlet類
1 public class LoginChkServlet extends HttpServlet { 2 private static final long serialVersionUID = 1L; 3 4 /** 5 * @see HttpServlet#HttpServlet() 6 */ 7 public LoginChkServlet() { 8 super(); 9 } 10 11 /** 12 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response) 13 */ 14 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 15 request.setCharacterEncoding("gb2312"); 16 String userName=request.getParameter("username"); 17 String passWord=request.getParameter("password"); 18 19 if(userName==null||userName.equals("")||passWord==null||passWord.equals("")) 20 { 21 response.sendRedirect("login"); 22 return; 23 } 24 else 25 { 26 HttpSession session=request.getSession(); 27 session.setAttribute("username", userName); 28 response.sendRedirect("welcome"); 29 } 30 } 31 32 /** 33 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response) 34 */ 35 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 36 // TODO Auto-generated method stub 37 doGet(request, response); 38 } LoginChkServlet這個Servlet類主要是驗證登錄的用戶信息,如果成功就把用戶名寫入Session中
第三步:寫一個登錄成功以後指向的WelcomeServlet類
1 public class WelcomeServlet extends HttpServlet { 2 private static final long serialVersionUID = 1L; 3 4 /** 5 * @see HttpServlet#HttpServlet() 6 */ 7 public WelcomeServlet() { 8 super(); 9 // TODO Auto-generated constructor stub 10 } 11 12 /** 13 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response) 14 */ 15 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 16 HttpSession session=request.getSession(); 17 String userName=(String)session.getAttribute("username"); 18 19 if(userName==null) 20 { 21 response.sendRedirect("login"); 22 } 23 else 24 { 25 response.setContentType("text/html;charset=gb2312"); 26 PrintWriter out=response.getWriter(); 27 out.println("<html><head><title>歡迎頁面</title></head><body>"); 28 LoginServlet loginServlet=new LoginServlet(); 29 loginServlet.printSessionInfo(out, session); 30 31 out.println("<p>"); 32 out.println("歡迎你,"+userName+"<p>"); 33 out.println("<a href=login>重新登錄</a>"); 34 out.println("<a href=logout>注銷</a>"); 35 out.println("</body></html>"); 36 out.close(); 37 } 38 } 39 40 /** 41 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response) 42 */ 43 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 44 // TODO Auto-generated method stub 45 doGet(request, response); 46 } WelcomeServlet這個類是登錄成功以後要顯示的界面
第四步:在寫一個退出類LogOutServlet類
1 public class LogOutServlet extends HttpServlet { 2 private static final long serialVersionUID = 1L; 3 4 /** 5 * @see HttpServlet#HttpServlet() 6 */ 7 public LogOutServlet() { 8 super(); 9 // TODO Auto-generated constructor stub 10 } 11 12 /** 13 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response) 14 */ 15 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 16 response.setContentType("text/html;charset=gb2312"); 17 HttpSession session=request.getSession(); 18 session.invalidate(); 19 20 PrintWriter out=response.getWriter(); 21 out.println("<html><head><title>退出登錄</title></head><body>"); 22 out.println("已退出登錄<br>"); 23 out.println("<a href=login>重新登錄</a>"); 24 out.println("</body></html>"); 25 out.close(); 26 } 27 /** 28 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response) 29 */ 30 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 31 // TODO Auto-generated method stub 32 doGet(request, response); 33 } LogOutServlet這個類是清除Session返回登錄頁面。
然後我們在配置一下web.xml
1 <web-app> 2 <display-name>Archetype Created Web Application</display-name> 3 <servlet> 4 <servlet-name>LoginServlet</servlet-name> 5 <display-name>LoginServlet</display-name> 6 <description></description> 7 <servlet-class>com.lp.servlet.LoginServlet</servlet-class> 8 </servlet> 9 <servlet> 10 <servlet-name>LoginChkServlet</servlet-name> 11 <display-name>LoginChkServlet</display-name> 12 <description></description> 13 <servlet-class>com.lp.servlet.LoginChkServlet</servlet-class> 14 </servlet> 15 <servlet> 16 <servlet-name>WelcomeServlet</servlet-name> 17 <display-name>WelcomeServlet</display-name> 18 <description></description> 19 <servlet-class>com.lp.servlet.WelcomeServlet</servlet-class> 20 </servlet> 21 <servlet> 22 <servlet-name>LogOut</servlet-name> 23 <display-name>LogOut</display-name> 24 <description></description> 25 <servlet-class>com.lp.servlet.LogOutServlet</servlet-class> 26 </servlet> 27 <servlet-mapping> 28 <servlet-name>LoginServlet</servlet-name> 29 <url-pattern>/login</url-pattern> 30 </servlet-mapping> 31 <servlet-mapping> 32 <servlet-name>LoginChkServlet</servlet-name> 33 <url-pattern>/loginchk</url-pattern> 34 </servlet-mapping> 35 <servlet-mapping> 36 <servlet-name>WelcomeServlet</servlet-name> 37 <url-pattern>/welcome</url-pattern> 38 </servlet-mapping> 39 <servlet-mapping> 40 <servlet-name>LogOut</servlet-name> 41 <url-pattern>/logout</url-pattern> 42 </servlet-mapping> 43 <session-config> 44 <session-timeout>10</session-timeout> 45 </session-config> 46 </web-app> web.xml其中<session-config>是設置Session的過期時間。ok現在整個項目寫好了,我們運行一下看看結果
注解:由於是第一次訪問所以服務器會首次創建會話所以這是一個新的會話,最大不活動時間指的就是Session失效的時間,由於我們在配置文件定義的是10分鐘,所以默認就是60*10=600s(輸出的單位是秒)
如果我用F5刷新我們看下結果
我們可以看出已經是舊的會話了,而且他們的SessionId是相同的。這是說明這2次是同一個會話,然後我們輸入用戶名和密碼進行登錄
我們通過SessionId可以看出這依然是同一個會話,然後我們在點擊重新登錄按鈕輸入用戶名和密碼如下圖
我們發現SessionId依然相同,這說明了一個問題就是同一個浏覽器並不支持2個用戶同時登錄,第二個登錄的用戶會把第一個用戶信息進行覆蓋(這也是Session覆蓋問題)
但是有人會疑問如果用另一種浏覽器登錄會不會還是同一個會話呢,那麼我們在IE浏覽器輸入http://localhost:8080/session-test/login如下圖
是一個新的會話,這就說明不同浏覽器會有不同會話的。那麼有人就說如果我們禁用Cookies了是不是就不可以登錄了呢我們來看看結果。
登錄不上了,這也恰恰說明SessionId需要Cookies,剛剛禁止Cookies以後我自己都登錄不了博客園,(⊙﹏⊙)。那麼怎麼辦呢 就有另一種方法Url重寫機制。
指的是如果客戶端不支持Cookies的時候,可以使用URL重寫機制來跟蹤會話。URL重寫機制就是在URL中附加客戶的標識SessionId,Servlet容器解釋URL的時候取出SessionId,在Servlet容器規范中這個參數的名字必須是jsessionid,完整的例子如http://localhost:8080/session-test/login;jsessionid=1。這樣一來服務器將SessionId作為Url一部分發送給客戶端,客戶端在請求URL的時候再把SessionId傳回來這樣就會達到會話跟蹤。
我們把上面代碼中類似於
1:response.sendRedirect("login")修改為response.sendRedirect(response.encodeRedirectURL("login"));
2:action=loginchk修改為action="+response.encodeURL("loginchk")
然後再次運行就會發現可以登錄了如下圖
登錄成功以後我們從浏覽器來看一下jsessionid,會發現和上面的sessionId一模一樣
3.1:當關閉浏覽器以後,Session如果沒有達到失效的時間是沒有消除的,登錄以後依然會發現是一個舊的Session。
3.2:用戶會話跟蹤的Cookies名字必須是jsessionid,通常報錯在浏覽器內存中,在浏覽器內存中的cookies是不能被不同浏覽器進程所共享。
3.3:如果想用另一種方式了解Session,我的另一個博客用.net寫的也講解了Session http://www.cnblogs.com/LipeiNet/p/4755103.html