会话管理:Session与Cookie
1. 導言
HTTP 是一種無狀態協議,每次客戶端訪問Web頁面時,客戶端打開一個單獨的瀏覽器窗口連接到Web服務器,由于服務器不會自動保存之前客戶端請求的相關信息,所以無法識別一個HTTP請求是否為第一次訪問。這意味著需要有相應的技術來維持Web客戶端和服務器之間的會話,這就是會話跟蹤。
2. Cookie與Session簡單架構圖
每一個客戶端向服務器請求信息,服務器為每個客戶端建立一個對話session,每個對話有一個唯一的sessionID,服務器將sessionID打包到cookie中,服務器將cookie發送給客戶端。客戶端保存cookie,等到下一次客戶端請求服務器時,發送保存的cookie,服務器通過cookie里面的sessionID就能判別是哪一個客戶端進行再次請求。
3.Cookies
3.1 Cookies簡介
Cookie 是存儲在客戶端計算機上的文本文件,并保留了各種跟蹤信息。識別返回用戶包括三個步驟:
(1)服務器腳本向瀏覽器發送一組 Cookie;
(2)瀏覽器將這些信息存儲在本地計算機上,以備將來使用;
(3)當下一次瀏覽器向Web服務器發送任何請求時,瀏覽器會把這些Cookie信息發送到服務器,服務器將使用這些信息來識別用戶。
3.2 Servlet中操作Cookie
Cookie源代碼:
3.3 Cookie API
Cookie源代碼:
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) //package javax.servlet.http;import java.io.Serializable; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.Locale;public class Cookie implements Cloneable, Serializable {private static final CookieNameValidator validation;private static final long serialVersionUID = 1L;private final String name;private String value;private int version = 0;private String comment;private String domain;private int maxAge = -1;private String path;private boolean secure;private boolean httpOnly;public Cookie(String name, String value) {validation.validate(name);this.name = name;this.value = value;}public void setComment(String purpose) {this.comment = purpose;}public String getComment() {return this.comment;}public void setDomain(String pattern) {this.domain = pattern.toLowerCase(Locale.ENGLISH);}public String getDomain() {return this.domain;}public void setMaxAge(int expiry) {this.maxAge = expiry;}public int getMaxAge() {return this.maxAge;}public void setPath(String uri) {this.path = uri;}public String getPath() {return this.path;}public void setSecure(boolean flag) {this.secure = flag;}public boolean getSecure() {return this.secure;}public String getName() {return this.name;}public void setValue(String newValue) {this.value = newValue;}public String getValue() {return this.value;}public int getVersion() {return this.version;}public void setVersion(int v) {this.version = v;}public Object clone() {try {return super.clone();} catch (CloneNotSupportedException var2) {throw new RuntimeException(var2);}}public void setHttpOnly(boolean httpOnly) {this.httpOnly = httpOnly;}public boolean isHttpOnly() {return this.httpOnly;}static {boolean strictServletCompliance;String propStrictNaming;String propFwdSlashIsSeparator;if (System.getSecurityManager() == null) {strictServletCompliance = Boolean.getBoolean("org.apache.catalina.STRICT_SERVLET_COMPLIANCE");propStrictNaming = System.getProperty("org.apache.tomcat.util.http.ServerCookie.STRICT_NAMING");propFwdSlashIsSeparator = System.getProperty("org.apache.tomcat.util.http.ServerCookie.FWD_SLASH_IS_SEPARATOR");} else {strictServletCompliance = (Boolean)AccessController.doPrivileged(new PrivilegedAction<Boolean>() {public Boolean run() {return Boolean.valueOf(System.getProperty("org.apache.catalina.STRICT_SERVLET_COMPLIANCE"));}});propStrictNaming = (String)AccessController.doPrivileged(new PrivilegedAction<String>() {public String run() {return System.getProperty("org.apache.tomcat.util.http.ServerCookie.STRICT_NAMING");}});propFwdSlashIsSeparator = (String)AccessController.doPrivileged(new PrivilegedAction<String>() {public String run() {return System.getProperty("org.apache.tomcat.util.http.ServerCookie.FWD_SLASH_IS_SEPARATOR");}});}boolean strictNaming;if (propStrictNaming == null) {strictNaming = strictServletCompliance;} else {strictNaming = Boolean.parseBoolean(propStrictNaming);}boolean allowSlash;if (propFwdSlashIsSeparator == null) {allowSlash = !strictServletCompliance;} else {allowSlash = !Boolean.parseBoolean(propFwdSlashIsSeparator);}if (strictNaming) {validation = new RFC2109Validator(allowSlash);} else {validation = new RFC6265Validator();}} }3.4 簡單案例
setCookie.html
<!DOCTYPE html> <html> <head><meta charset="utf-8"><title>菜鳥教程(runoob.com)</title> </head> <body> <form action="/setCookie" method="GET">站點名 :<input type="text" name="name"><br />站點 URL:<input type="text" name="url" /><br><input type="submit" value="提交" /> </form> </body> </html> package webstudy;import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; import java.net.URLEncoder;/*** Servlet implementation class HelloServlet*/ @WebServlet("/SetCookie") public class SetCookie extends HttpServlet {private static final long serialVersionUID = 1L;/*** @see HttpServlet#HttpServlet()*/public SetCookie() {super();// TODO Auto-generated constructor stub}/*** @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)*/@Overridepublic void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{// 為名字和姓氏創建 CookieCookie name = new Cookie("name",URLEncoder.encode(request.getParameter("name"), "UTF-8")); // 中文轉碼Cookie url = new Cookie("url",request.getParameter("url"));// 為兩個 Cookie 設置過期日期為 24 小時后name.setMaxAge(60*60*24);url.setMaxAge(60*60*24);// 在響應頭中添加兩個 Cookieresponse.addCookie(name);response.addCookie(url);// 設置響應內容類型response.setContentType("text/html;charset=UTF-8");PrintWriter out = response.getWriter();String title = "設置 Cookie 實例";String docType = "<!DOCTYPE html>\n";out.println(docType +"<html>\n" +"<head><title>" + title + "</title></head>\n" +"<body bgcolor=\"#f0f0f0\">\n" +"<h1 align=\"center\">" + title + "</h1>\n" +"<ul>\n" +" <li><b>站點名:</b>:"+ request.getParameter("name") + "\n</li>" +" <li><b>站點 URL:</b>:"+ request.getParameter("url") + "\n</li>" +"</ul>\n" +"</body></html>");}/*** @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)*/@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// TODO Auto-generated method stubdoGet(request, response);}} package webstudy;import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; import java.net.URLDecoder; import java.util.Arrays;/*** Servlet implementation class ReadCookies*/ @WebServlet("/ReadCookies") public class ReadCookies extends HttpServlet {private static final long serialVersionUID = 1L;/*** @see HttpServlet#HttpServlet()*/public ReadCookies() {super();// TODO Auto-generated constructor stub}/*** @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)*/@Overridepublic void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{Cookie cookie = null;Cookie[] cookies = null;// 獲取與該域相關的 Cookie 的數組cookies = request.getCookies();System.out.println(Arrays.toString(cookies));// 設置響應內容類型response.setContentType("text/html;charset=UTF-8");PrintWriter out = response.getWriter();String title = "Delete Cookie Example";String docType = "<!DOCTYPE html>\n";out.println(docType +"<html>\n" + "<head><title>" + title + "</title></head>\n" + "<body bgcolor=\"#f0f0f0\">\n" );if( cookies != null ){out.println("<h2>Cookie 名稱和值</h2>");for (int i = 0; i < cookies.length; i++){cookie = cookies[i];//比較字符串的值是否相等,-1 0 1if((cookie.getName( )).compareTo("name") == 0 ){cookie.setMaxAge(0);//設置過期response.addCookie(cookie);//添加Cookieout.print("已刪除的 cookie:" + cookie.getName( ) + "<br/>");}out.print("名稱:" + cookie.getName( ) + ",");out.print("值:" + URLDecoder.decode(cookie.getValue(), "utf-8") +" <br/>");}}else{out.println( "<h2 class=\"tutheader\">No Cookie founds</h2>");}out.println("</body>");out.println("</html>");}/*** @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)*/@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// TODO Auto-generated method stubdoGet(request, response);}}4. HttpSession 對象
4.1 Session 簡介
為了讓 Web 應用程序能夠記住用戶送出的不同請求,Servlet 規范內定義一個 HttpSession 接口,允許 Servlet 容器針對每個用戶建立一個 HTTP 會話(即 HttpSession 對象),每個 HTTP 會話將被賦予惟一的“會話編號”( Session ID )。
HttpSession 對象會在用戶第一次訪問服務器時由容器創建(只有訪問 JSP、Servlet 等動態資源時才會創建,訪問 HTML 等靜態資源并不會創建),當用戶調用其失效方法(invalidate() 方法)或超過其最大不活動(超時,timeout )時間時會失效。
Servlet 容器為每個 HttpSession 生成唯一的標識,并將該標識發送給瀏覽器,或創建一個名為 JSESSIONID 的 cookie ,或者在 URL 后附加一個名為 jsessionid 的參數。在后續的請求中,瀏覽器會將標識提交給服務端,這樣服務器就可以識別該請求是由哪個用戶發起的。
4.2 HttpSession API
HttpSession 源代碼:
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) //package javax.servlet.http;import java.util.Enumeration; import javax.servlet.ServletContext;public interface HttpSession {long getCreationTime();String getId();long getLastAccessedTime();ServletContext getServletContext();void setMaxInactiveInterval(int var1);int getMaxInactiveInterval();/** @deprecated */@DeprecatedHttpSessionContext getSessionContext();Object getAttribute(String var1);/** @deprecated */@DeprecatedObject getValue(String var1);Enumeration<String> getAttributeNames();/** @deprecated */@DeprecatedString[] getValueNames();void setAttribute(String var1, Object var2);/** @deprecated */@Deprecatedvoid putValue(String var1, Object var2);void removeAttribute(String var1);/** @deprecated */@Deprecatedvoid removeValue(String var1);void invalidate();boolean isNew(); }4.3 簡單案例
bookList.jsp頁面顯示圖書購買列表,將表單數據提交給ProcessOneServlet.java處理,ProcessOneServlet.java將欲購買的圖書數據放置在HttpSession中,并請求重定向到address.jsp頁面,在address.jsp頁面輸入客戶的用戶名和郵寄地址,并提交給ProcessTwoServlet.java處理,ProcessTwoServlet.java將客戶信息和郵寄地址數據放置在HttpSession中,并將請求重定向到ConfirmServlet.java,ConfirmServlet將購物信息從HttpSession中取出,進行顯示,實現加入購物車的功能。
bookList.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>選擇購買書籍</title> </head> <body><h2>選擇購買書籍</h2><form action="/ProcessOne" method="post"><table border="1" width="68%"><tr><th align="center">書名</td><th align="center">購買</td></tr><tr><td>Android Application Develoment Practice Tutorial</td><td align="center"><input type="checkbox" name="buy"value="Android Application Develoment Practice Tutorial"></td></tr><tr><td>Software Project Management</td><td align="center"><input type="checkbox" name="buy"value="Software Project Management"></td></tr><tr><td>Agile Software Development</td><td align="center"><input type="checkbox" name="buy"value="Agile Software Development"></td></tr><tr><td>Information Technology Project Management</td><td align="center"><input type="checkbox" name="buy"value="Information Technology Project Management"></td></tr></table><p><input type="submit" value="下一步"></p></form> </body> </html> package book.ch4;import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.IOException; import java.io.PrintWriter;@WebServlet(name = "ProcessOneServlet", urlPatterns = { "/ProcessOne" }) public class ProcessOneServlet extends HttpServlet {private static final long serialVersionUID = 1L;public ProcessOneServlet() {super();}@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {response.setContentType("text/html;charset=UTF-8");PrintWriter out = response.getWriter();HttpSession session = request.getSession(true);String[] selectedBooks = request.getParameterValues("buy"); session.setAttribute("choosed", selectedBooks);response.sendRedirect(request.getContextPath() + "/page/ch4/address.jsp");}}address.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>郵寄地址</title> </head> <body><h2>輸入郵寄地址</h2><form action="/ProcessTwo" method="post"><table border="1" width="68%"><tr><td>姓名:</td><td><input type="text" name="customer" size="30"></td></tr><tr><td>郵寄地址:</td><td><input type="text" name="address" size="66"></td></tr></table><p><input type="submit" value="下一步"></p></form> </body> </html> package book.ch4;import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.IOException; import java.io.PrintWriter;@WebServlet(name = "ProcessTwoServlet", urlPatterns = { "/ProcessTwo" }) public class ProcessTwoServlet extends HttpServlet {private static final long serialVersionUID = 1L;public ProcessTwoServlet() {super();}@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {response.setContentType("text/html;charset=UTF-8");PrintWriter out = response.getWriter();HttpSession session = request.getSession(true);//String customer = request.getParameter("customer");String customer = new String(request.getParameter("customer").getBytes("iso-8859-1"), "utf-8");//String address = request.getParameter("address");String address = new String(request.getParameter("address").getBytes("iso-8859-1"), "utf-8");session.setAttribute("A_customer", customer);session.setAttribute("A_address", address);response.sendRedirect("Confirm");}} package book.ch4;import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.IOException; import java.io.PrintWriter;@WebServlet(name = "/ConfirmServlet", urlPatterns = { "/Confirm" }) public class ConfirmServlet extends HttpServlet {private static final long serialVersionUID = 1L;public ConfirmServlet() {super();}@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {response.setContentType("text/html;charset=UTF-8");PrintWriter out = response.getWriter();HttpSession session = request.getSession(true);String[] books = (String[]) session.getAttribute("choosed");String customer = (String) session.getAttribute("A_customer");String address = (String) session.getAttribute("A_address");boolean conditon = (books != null) && (customer != null) && (address != null) && (!customer.equals("")) && (!address.equals(""));if (conditon) {System.out.println(conditon);System.out.println(!address.equals(""));System.out.println("customer=" + customer);System.out.println("address=" + address);out.println("<html><body>");out.println("<h2>訂單確認</h2>");out.println("<table border=\"1\" width=\"68%\">");out.println("<tr>");out.println("<td>顧客姓名:</td>");out.println("<td>" + customer + "</td>");out.println("</tr>");out.println("<tr>");out.println("<td>地址:</td>");out.println("<td>" + address + "</td>");out.println("</tr>");out.println("<tr>");out.println("<td>訂購書籍:</td>");out.println("<td>");for (int i = 0; i < books.length; i++) {out.println(books[i] + "<br>");}out.println("</td>");out.println("</tr>");out.println("</table>");out.println("<p><input type=\"submit\" value=\"提交訂單\"></p>");out.println("</html></body>");} else {out.println("<html><body>");out.println("<h2>無法從HTTP會話內取出所需信息!</h2>");out.println("</html></body>");}}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {}}5. URL 重寫
在某些情況下,用戶為了安全性可能會關閉 Web 瀏覽器的 Cookie 功能。這時候如果從用戶端瀏覽器送出 HTTP 請求,并不會包含 Session ID,因此 Servlet 容器無法利用同一個 HTTP 會話來保存不同的 HTTP 請求內容。我們可以用URL重寫解決這種情況。
5.1 什么是URL重寫
在URL重寫中,我們將標記或標識符添加到下一個Servlet或下一個資源的URL。我們可以使用以下格式發送參數名稱/值對:
url?name1=value1&name2=value2名稱和值使用等號分隔,參數名稱/值對與另一個參數使用&符號分隔。當用戶單擊超鏈接時,參數名稱/值對將被傳遞到服務器。在Servlet中,我們可以使用getParameter()方法獲取參數值。
5.2 URL重寫的優勢
5.3 URL重寫的缺點
5.4 URL重寫的案例
在下面的示例中,我們使用URL鏈接維護用戶的狀態。為了實現此目的,我們將用戶名附加在查詢字符串(queryString)中,并從另一頁面的查詢字符串(queryString)中獲取值。
URL 重寫通過 HttpServletResponse的 encodeURL()方法和 encodeRedirectURL()方法實現,其中 encodeRedirectURL() 方法主要對使用 sendRedirect () 方法的URL進行重寫。
5.4.1 編寫頁面
UrlRewrite.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>URL重寫案例</title> </head> <body> <form action="/servlet1">用戶名:<input type="text" name="userName"/><br/><input type="submit" value="提交"/> </form> </body> </html>5.4.2 編寫FirstServlet
FirstServlet:
import java.io.*; import javax.servlet.*; import javax.servlet.http.*;public class FirstServlet extends HttpServlet {private static final long serialVersionUID = 1L;protected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {try{response.setContentType("text/html;charset=utf-8");PrintWriter out = response.getWriter();String n=request.getParameter("userName");out.print("歡迎 "+n);//把用戶名追加到查詢字符串后面out.print("<a href='"+request.getContextPath()+"/servlet2?uname="+n+"'>訪問</a>");out.close();}catch(Exception e){System.out.println(e);}} }5.4.3 編寫SecondServlet
SecondServlet:
import javax.servlet.ServletException; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter;public class SecondServlet extends HttpServlet {private static final long serialVersionUID = 1L;protected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {try{response.setContentType("text/html;charset=utf-8");PrintWriter out = response.getWriter();//從查詢字符串獲取用戶名String n=request.getParameter("uname");out.print("你好 "+n);out.close();}catch(Exception e){System.out.println(e);}} }5.4.4 配置web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"version="4.0"><servlet><servlet-name>FirstServlet</servlet-name><servlet-class>book.ch4.FirstServlet</servlet-class> </servlet><servlet-mapping> <servlet-name>FirstServlet</servlet-name> <url-pattern>/servlet1</url-pattern> </servlet-mapping><servlet> <servlet-name>SecondServlet</servlet-name> <servlet-class>book.ch4.SecondServlet</servlet-class> </servlet><servlet-mapping> <servlet-name>SecondServlet</servlet-name> <url-pattern>/servlet2</url-pattern> </servlet-mapping></web-app>
5.5 隱藏表單域
一個 Web 服務器可以發送一個隱藏的 HTML 表單字段,以及一個唯一的 SESSION 會話 ID,如下所示:
<input type="hidden" name="session_id" value="123456789">當表單被提交時,指定的名稱和值會被自動包含在表單數據中。每次當用戶端瀏覽器發送回請求時,session_id 值可以用于保持不同的用戶端瀏覽器的跟蹤。
6. 小結
- Cookie 分為會話 Cookie 和持久 Cookie。
會話 Cookie是指在不設定它的生命周期 expires 時的狀態。
瀏覽器的開啟到關閉就是一次會話,當關閉瀏覽器時,會話Cookie 就會跟隨瀏覽器而銷毀。
當關閉一個頁面時,不影響會話 Cookie 的銷毀。
持久 Cookie則是設定了它的生命周期 expires,關閉瀏覽器之后,它不會銷毀,直到設定的過期時間。
對于持久 Cookie,可以在同一個瀏覽器中傳遞數據,比如,你在打開一個淘寶頁面登陸后,你在點開一個商品頁面,依然是登錄狀態,即便你關閉了瀏覽器,再次開啟瀏覽器,依然會是登錄狀態。這就是因為 Cookie 自動將數據傳送到服務器端,在反饋回來的結果。
-
Session 是通過 Cookie 技術實現的,依賴于名為 JSESSIONID 的 Cookie,它將信息保存在服務器端。
Session 中能夠存儲復雜的 Java 對象。
Session 機制是一種服務器端的機制,服務器使用一種類似于散列表的結構(也可能就是使用散列表)來保存信息。 -
由于 Cookie 可以被人為的禁止,必須有其他機制以便在 Cookie 被禁止時仍然能夠把 Session ID 傳遞回服務器。
于是我們可以采用URL 重寫技術,就是把Session ID 直接附加在 URL 路徑的后面。URL 重寫是一種很好的維持 HTTP 會話的方式,它在瀏覽器不支持 Cookie 時能夠很好地工作,但是它的缺點是會動態生成每個 URL 來為頁面分配一個 HTTP 會話 Session ID。為了在整個交互過程中始終保持狀態,就必須在每個客戶端可能請求的路徑后面都包含這個 Session ID。
-
隱藏表單域技術就是在表單域添加一個隱藏字段,以便在表單提交時能夠把 Session ID 傳遞回服務器。
利用 Form 表單的隱藏表單域,可以在完全脫離瀏覽器對Cookie 的使用限制以及在用戶無法從頁面顯示看到隱藏標識的情況下,將標識隨請求一起傳送給服務器處理,從而實現會話的跟蹤。
總結
以上是生活随笔為你收集整理的会话管理:Session与Cookie的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Servlet入门篇(GenericSe
- 下一篇: 工厂与抽象工厂