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

Java Servlet 讀寫 Cookie 完整指南:建立、讀取、安全屬性與最佳實踐

     編輯:更多程式設計知識

Cookie 是 HTTP 無狀態協議中維持客戶端狀態的核心機制。Java Servlet API 提供 javax.servlet.http.Cookie 類別進行操作。本文涵蓋**寫入、讀取、屬性設定、安全強化、常見坑**與**工具類封裝**。

核心 API 快速回顧

方法說明
new Cookie(name, value)建立 Cookie,名值皆為 String
setMaxAge(int seconds)存活秒數:>0 持久化;=0 刪除;<0 會話級(瀏覽器關閉失效)
setPath(String)生效路徑,預設為當前 Servlet 路徑,**建議設為 "/"**
setDomain(String)生效網域,支援子網域共享(如 .example.com)
setSecure(boolean)僅 HTTPS 傳輸
setHttpOnly(boolean)防止 JavaScript 讀取(XSS 防護),**強烈建議開啟**
response.addCookie(cookie)寫入響應標頭 Set-Cookie
request.getCookies()讀取請求標頭 Cookie,回傳 Cookie[] 或 null

寫入 Cookie:完整範例


/**
 * 寫入加密的使用者識別 Cookie
 */
public void writeUserCookies(HttpServletResponse response, String schoolId, String userId, String encodedUsername) {
    // 1. 使用者名稱 Cookie(一年有效期)
    Cookie usernameCookie = new Cookie("username_" + schoolId, encodedUsername);
    usernameCookie.setMaxAge(60 * 60 * 24 * 365); // 1 年
    usernameCookie.setPath("/");                 // 全站有效
    usernameCookie.setHttpOnly(true);            // 防 XSS
    usernameCookie.setSecure(true);              // 僅 HTTPS(生產環境必開)
    // usernameCookie.setDomain(".example.com");  // 跨子網域共享時啟用
    response.addCookie(usernameCookie);

    // 2. 使用者 ID Cookie
    Cookie userIdCookie = new Cookie("userId_" + schoolId, userId);
    userIdCookie.setMaxAge(60 * 60 * 24 * 365);
    userIdCookie.setPath("/");
    userIdCookie.setHttpOnly(true);
    userIdCookie.setSecure(true);
    response.addCookie(userIdCookie);
}

讀取 Cookie:安全遍歷模式


/**
 * 讀取指定前綴的 Cookie 值
 * @param request HttpServletRequest
 * @param cookieNamePrefix Cookie 名稱前綴(如 "userId_")
 * @param schoolId 業務識別碼
 * @return Cookie 值或 null
 */
public String readCookieValue(HttpServletRequest request, String cookieNamePrefix, String schoolId) {
    Cookie[] cookies = request.getCookies();
    if (cookies == null) return null;

    String targetName = cookieNamePrefix + schoolId;
    for (Cookie cookie : cookies) {
        if (targetName.equals(cookie.getName())) {
            return cookie.getValue();
        }
    }
    return null;
}

// 使用範例
String userId = readCookieValue(request, "userId_", schoolId);

Cookie 工具類封裝(生產級)


package com.utils.web;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.Optional;

public final class CookieUtils {

    private CookieUtils() {}

    /**
     * 新增/更新 Cookie(預設安全屬性)
     */
    public static void addCookie(HttpServletResponse response, String name, String value, int maxAgeSeconds) {
        Cookie cookie = new Cookie(name, value);
        cookie.setPath("/");
        cookie.setMaxAge(maxAgeSeconds);
        cookie.setHttpOnly(true);
        cookie.setSecure(true); // 僅 HTTPS,開發環境可透過配置關閉
        response.addCookie(cookie);
    }

    /**
     * 刪除 Cookie(設 maxAge=0)
     */
    public static void removeCookie(HttpServletResponse response, String name) {
        Cookie cookie = new Cookie(name, "");
        cookie.setPath("/");
        cookie.setMaxAge(0);
        cookie.setHttpOnly(true);
        cookie.setSecure(true);
        response.addCookie(cookie);
    }

    /**
     * 讀取 Cookie 值(Optional 避免 NPE)
     */
    public static Optional getCookieValue(HttpServletRequest request, String name) {
        Cookie[] cookies = request.getCookies();
        if (cookies == null) return Optional.empty();

        return Arrays.stream(cookies)
                .filter(c -> name.equals(c.getName()))
                .map(Cookie::getValue)
                .findFirst();
    }

    /**
     * 讀取所有 Cookie 轉 Map
     */
    public static java.util.Map getAllCookies(HttpServletRequest request) {
        Cookie[] cookies = request.getCookies();
        if (cookies == null) return java.util.Collections.emptyMap();

        return Arrays.stream(cookies)
                .collect(java.util.stream.Collectors.toMap(
                        Cookie::getName,
                        Cookie::getValue,
                        (v1, v2) -> v1 // 重複名稱取第一個
                ));
    }
}

安全屬性深度解析

屬性作用建議設定風險
HttpOnly禁止 JS 存取 document.cookie必開 trueXSS 攻擊竊取 Session ID
Secure僅 HTTPS 傳輸生產必開 true,開發可配置關閉中間人攻擊竊取 Cookie
SameSite控制跨站請求攜帶 CookieStrict 或 Lax(Servlet API 需手動加標頭)CSRF 攻擊
Path限制生效路徑/(全站)或具體應用路徑路徑洩漏導致 Cookie 被非預期頁面送出
Domain限制生效網域不設(僅當前網域)或 .example.com(共享子網域)網域過寬導致 Cookie 被惡意子網域竊取

SameSite 手動設定範例(Servlet 3.0+ 無原生 API)


// 手動構建 Set-Cookie 標頭加入 SameSite
String cookieHeader = String.format("%s=%s; Path=/; HttpOnly; Secure; SameSite=Lax; Max-Age=%d", 
        name, value, maxAge);
response.addHeader("Set-Cookie", cookieHeader);

常見坑與避坑指南

  1. request.getCookies() 回傳 null:客戶端未攜帶任何 Cookie 時回傳 null,非空陣列。**必須判空**。
  2. Cookie 名稱區分大小寫:Cookie.getName() 比對時**嚴格區分大小寫**。
  3. 同名 Cookie 覆蓋規則:瀏覽器以 Name + Domain + Path 為鍵,後寫入者覆蓋先寫入者。
  4. 中文值必須編碼:Cookie 值不支援中文,需 URLEncoder.encode(value, "UTF-8") 寫入,讀取時 URLDecoder.decode(value, "UTF-8")。
  5. 大小限制:單個 Cookie ≤ 4KB,單網域總數 ≤ 20~50 個(瀏覽器不同),**勿存大量資料**。
  6. 子網域共享:設 setDomain(".example.com") 可讓 a.example.com、b.example.com 共享,**需確保信任所有子網域**。

總結

Java Servlet 操作 Cookie 雖 API 簡單,但**安全屬性配置(HttpOnly、Secure、SameSite)直接關係應用安全性**。建議:

  • 統一使用工具類封裝,強制預設安全屬性。
  • 敏感資料(Session ID、Token)務必開啟 HttpOnly + Secure。
  • 大量/結構化資料改用 Server-side Session、JWT 或 LocalStorage(前端)。
  • 定期審查 Cookie 範圍與有效期,最小權限原則。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved