Session 是 Web 開發中實現有狀態會話的核心機制。本文系統梳理 Session 的創建、銷毀、與 Cookie 的關係、禁用 Cookie 時的 URL 重寫方案,並提供完整 Servlet 實戰代碼,幫你徹底搞懂 Session 生命週期與最佳實踐。
| 特性 | Cookie | Session |
|---|---|---|
| 儲存位置 | 客戶端瀏覽器 | 伺服器端記憶體/資料庫/Redis |
| 安全性 | 低(可被竄改、攔截) | 高(敏感數據不落地客戶端) |
| 容量限制 | 4KB / 域名約 20 條 | 理論無上限(受伺服器記憶體限制) |
| 生命週期 | 可設定過期時間 | 預設 30 分鐘無操作失效,可配置 |
| 標識傳遞 | 自動攜帶 | 依賴 Cookie (JSESSIONID) 或 URL 重寫 |
本質:Session 由伺服器創建,透過 JSESSIONID (Cookie 或 URL 參數) 關聯具體用戶會話數據。
HttpSession session = request.getSession(); // 等同 getSession(true)
// 1. 若請求攜帶有效 JSESSIONID → 返回對應 Session
// 2. 若無/無效 → 創建新 Session,生成新 JSESSIONID,寫入回應 Cookie
HttpSession session = request.getSession(false);
// 僅獲取現有 Session,不存在返回 null(常用於檢查登入狀態)
常見錯誤:在 Filter/Interceptor 中濫用 getSession() 導致匿名用戶產生大量垃圾 Session。原則:僅在確認需要會話狀態時(如登入成功、加入購物車)才創建。
request.getSession() 或 getSession(true)<%@ page session="true" %> 預設開啟)web.xml 配置:<session-timeout>15</session-timeout>(分鐘)session.invalidate()(登出時必須調用)session.setMaxInactiveInterval(1800); // 30 分鐘,單位:秒
// 0 或負數 = 永不超時(極度不推薦)
當瀏覽器禁用 Cookie,JSESSIONID 無法透過 Cookie 傳遞。Servlet API 提供兩個智能方法自動處理:
response.encodeURL(url):用於 超連結、表單 action、Ajax 請求。自動在路徑後附加 ;jsessionid=XXX。response.encodeRedirectURL(url):用於 sendRedirect 重定向。// IndexServlet:商品列表,超連結經過 encodeURL
@WebServlet("/index")
public class IndexServlet extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
resp.setContentType("text/html;charset=UTF-8");
PrintWriter out = resp.getWriter();
req.getSession(); // 確保 Session 創建
out.write("本網站有以下書:<br/>");
for (Book book : DB.getAll().values()) {
String url = req.getContextPath() + "/buy?id=" + book.getId();
url = resp.encodeURL(url); // 關鍵:自動附加 jsessionid
out.println(book.getName() + " <a href='" + url + "'>購買</a><br/>");
}
}
}
// BuyServlet:加入購物車,重定向經過 encodeRedirectURL
@WebServlet("/buy")
public class BuyServlet extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String id = req.getParameter("id");
Book book = DB.getAll().get(id);
HttpSession session = req.getSession();
List<Book> cart = (List<Book>) session.getAttribute("cart");
if (cart == null) { cart = new ArrayList<>(); session.setAttribute("cart", cart); }
cart.add(book);
String url = resp.encodeRedirectURL(req.getContextPath() + "/cart");
resp.sendRedirect(url);
}
}
// ListCartServlet:顯示購物車
@WebServlet("/cart")
public class ListCartServlet extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
List<Book> cart = (List<Book>) req.getSession().getAttribute("cart");
// 顯示邏輯...
}
}
這兩個方法內建檢測:若請求攜帶 Cookie 且含有效 JSESSIONID,則不進行 URL 重寫,保持 URL 乾淨。只有檢測到 Cookie 被禁用/無效時才啟用重寫。因此請無腦在所有輸出 URL 處調用,零成本兼容兩種場景。
Sticky Session (Nginx ip_hash) / Session Replication (Tomcat DeltaManager) / Redis 集中式存儲 (推薦)。session.invalidate() 再 request.getSession(true) 生成新 ID。Session 是伺服器端會話狀態的容器,JSESSIONID 是鑰匙。掌握 getSession() 雙參數語義、生命週期控制、URL 重寫自動兼容、集群化方案選型,是 Java Web 開發者的必修課。下次遇到「為什麼登入後刷新就登出?」先檢查:是否禁用 Cookie?是否調用 encodeURL?Session 超時配置多少?集群是否同步?