首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 程式設計入門

Servlet 會話管理實戰:Cookie 與 HttpSession 購物車完整範例

     編輯:Java 程式設計入門

HTTP 無狀態特性要求額外機制維持會話。Servlet 提供 Cookie (客戶端) 與 HttpSession (伺服器端) 兩大支柱。本文完整實作購物車範例,展示兩者協作細節、生命週期控制與安全考量。

Cookie:客戶端狀態載體

特性:Key-Value 配對、附屬 Domain/Path、可設定 MaxAge (秒)、Secure (僅 HTTPS)、HttpOnly (防 XSS)。

核心 API

// 建立 Cookie
Cookie cookie = new Cookie("userId", "12345");
cookie.setMaxAge(60 * 60 * 24 * 7); // 7 天
cookie.setPath("/app");             // 僅 /app 路徑可見
cookie.setSecure(true);             // 僅 HTTPS 傳輸
cookie.setHttpOnly(true);           // JavaScript 不可存取
resp.addCookie(cookie);             // 寫入回應 Header

// 讀取 Cookie
Cookie[] cookies = req.getCookies();
if (cookies != null) {
    for (Cookie c : cookies) {
        if ("userId".equals(c.getName())) {
            String userId = c.getValue();
        }
    }
}

ShowCookiesServlet:檢視所有 Cookie

@WebServlet("/show-cookies")
public class ShowCookiesServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        resp.setContentType("text/html;charset=UTF-8");
        PrintWriter out = resp.getWriter();
        Cookie[] cookies = req.getCookies();
        
        out.println("

請求中的 Cookie

"); if (cookies == null || cookies.length == 0) { out.println("

無 Cookie

"); } else { out.println(""); for (Cookie c : cookies) { out.printf("", c.getName(), c.getValue(), c.getComment(), c.getMaxAge(), c.getDomain(), c.getPath(), c.getSecure(), c.isHttpOnly()); } out.println("
名稱值註解MaxAgeDomainPathSecureHttpOnly
%s%s%s%d%s%s%b%b
"); } } }

HttpSession:伺服器端會話容器

核心差異:資料儲存在伺服器記憶體/資料庫/Redis,客戶端僅持有 JSESSIONID (Cookie 或 URL 重寫)。安全性高、容量大、支援任意物件 (需實作 Serializable 以支援叢集/持久化)。

獲取與生命週期

HttpSession session = req.getSession(true);  // 取得或建立 (預設)
HttpSession session = req.getSession(false); // 僅取得現有,無則回傳 null

// 設定閒置逾時 (秒),web.xml 全域設定:30
session.setMaxInactiveInterval(1800); // 30 分鐘

// 銷毀 Session (登出)
session.invalidate();

購物車完整實作

商品頁面:shop.html

<!DOCTYPE html>
<html><head><meta charset="UTF-8"><title>百貨商場</title></head><body>
<h1 align="center">百貨商場</h1><hr>
<form action="shop-cart" method="POST">
    <p><input type="checkbox" name="item" value="0"> 糖果 ($50)</p>
    <p><input type="checkbox" name="item" value="1"> 收音機 ($1200)</p>
    <p><input type="checkbox" name="item" value="2"> 練習簿 ($30)</p>
    <hr>
    <input type="submit" value="加入購物袋">
</form>
</body></html>

購物車 Servlet:ShopCartServlet.java

package com.example;

import jakarta.servlet.*;
import jakarta.servlet.http.*;
import jakarta.servlet.annotation.*;
import java.io.*;
import java.util.*;

@WebServlet("/shop-cart")
public class ShopCartServlet extends HttpServlet {
    private static final String[] ITEMS = {"糖果", "收音機", "練習簿"};
    private static final int[] PRICES = {50, 1200, 30};

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        
        HttpSession session = req.getSession(true);
        // 從 Session 取得購物車 (List 存商品索引)
        @SuppressWarnings("unchecked")
        List cart = (List) session.getAttribute("cart");
        if (cart == null) {
            cart = new ArrayList<>();
            session.setAttribute("cart", cart);
        }
        
        // 處理新增商品
        String[] selected = req.getParameterValues("item");
        if (selected != null) {
            for (String idxStr : selected) {
                try {
                    int idx = Integer.parseInt(idxStr);
                    if (idx >= 0 && idx < ITEMS.length) {
                        cart.add(idx);
                    }
                } catch (NumberFormatException ignored) {}
            }
        }
        
        // 導向購物車檢視頁 (避免重複提交)
        resp.sendRedirect(req.getContextPath() + "/view-cart");
    }
}

檢視購物車:ViewCartServlet.java

@WebServlet("/view-cart")
public class ViewCartServlet extends HttpServlet {
    private static final String[] ITEMS = {"糖果", "收音機", "練習簿"};
    private static final int[] PRICES = {50, 1200, 30};

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        
        HttpSession session = req.getSession(false);
        List cart = (session != null) ? (List) session.getAttribute("cart") : null;
        
        resp.setContentType("text/html;charset=UTF-8");
        PrintWriter out = resp.getWriter();
        
        out.println("購物袋");
        out.println("

你的購物袋


"); if (cart == null || cart.isEmpty()) { out.println("

購物袋是空的 繼續購物

"); } else { out.println(""); int total = 0; for (int idx : cart) { out.printf("", ITEMS[idx], PRICES[idx]); total += PRICES[idx]; } out.printf("", total); out.println("
商品單價
%s$%,d
總計$%,d
"); out.println("

繼續購物 | 結帳 | "); out.println("清空購物車

"); } out.println(""); } }

清空購物車:ClearCartServlet.java

@WebServlet("/clear-cart")
public class ClearCartServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        HttpSession session = req.getSession(false);
        if (session != null) {
            session.removeAttribute("cart");
        }
        resp.sendRedirect(req.getContextPath() + "/view-cart");
    }
}

Cookie vs Session:選型決策表

維度CookieHttpSession
儲存位置客戶端瀏覽器伺服器記憶體/外部存儲
容量限制單 Cookie 4KB,每域 ~20-50 個受限於伺服器記憶體/快取容量
安全性可被竄改、攔截、XSS 盜取資料不暴露客戶端,需防 Session Fixation/Hijacking
生命週期控制MaxAge 精確控制閒置逾時 + 手動 invalidate()
跨瀏覽器/裝置自動攜帶需共享 Session 儲存 (Redis/DB) 實現叢集
適用場景偏好設定、記住我、追蹤 ID、CSRF Token登入狀態、購物車、權限、多步驟表單、敏感資料

安全強化清單

  1. Session ID 安全:Secure (HTTPS)、HttpOnly、定期輪換 (登入後 session.invalidate() → req.getSession(true))。
  2. 防 CSRF:表單隱藏欄位 + Session 存儲 Token,驗證雙重提交。
  3. 敏感資料不入 Cookie:密碼、Token、個資絕不寫入 Cookie。
  4. 叢集環境:Session 外部化 (Spring Session + Redis、Sticky Session)。

掌握 Cookie 與 Session 協作模式,是構建有狀態 Web 應用的基石。實務上多採「Session 存業務資料、Cookie 存識別 Token/偏好」的混合策略,兼顧安全、效能與使用者體驗。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved