Set-Cookie 響應頭寫入瀏覽器,瀏覽器後續請求自動攜帶 Cookie 請求頭。CookieUtils(生產級)public final class CookieUtils {
private CookieUtils() {}
/** 讀取指定名稱的 Cookie 值(自動 URL 解碼) */
public static String getValue(HttpServletRequest request, String name) {
Cookie cookie = getCookie(request, name);
if (cookie == null) return null;
try {
return URLDecoder.decode(cookie.getValue(), StandardCharsets.UTF_8.name());
} catch (Exception e) {
return cookie.getValue(); // 解碼失敗返回原值
}
}
/** 獲取 Cookie 對象 */
public static Cookie getCookie(HttpServletRequest request, String name) {
Cookie[] cookies = request.getCookies();
if (cookies == null) return null;
for (Cookie c : cookies) {
if (c.getName().equals(name)) return c;
}
return null;
}
/** 獲取所有 Cookie 轉 Map */
public static Map getAll(HttpServletRequest request) {
Map map = new LinkedHashMap<>();
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie c : cookies) {
map.put(c.getName(), c.getValue());
}
}
return map;
}
/** 寫入 Cookie(自動 URL 編碼、設置 Path=/、HttpOnly、SameSite=Lax) */
public static void add(HttpServletResponse response, String name, String value, int maxAgeSeconds) {
try {
value = URLEncoder.encode(value, StandardCharsets.UTF_8.name());
} catch (Exception ignored) {}
Cookie cookie = new Cookie(name, value);
cookie.setPath("/");
cookie.setHttpOnly(true); // 防 XSS
cookie.setMaxAge(maxAgeSeconds); // -1: 會話級; 0: 刪除; >0: 持久化秒數
cookie.setSecure(false); // 僅 HTTPS 時改 true
// SameSite 需 Servlet 4.0+ (Tomcat 9+, Jetty 10+)
try {
Method m = cookie.getClass().getMethod("setAttribute", String.class, String.class);
m.invoke(cookie, "SameSite", "Lax"); // CSRF 防護
} catch (Exception ignored) {}
response.addCookie(cookie);
}
/** 刪除 Cookie(必須同名、同 Path、同 Domain) */
public static void remove(HttpServletRequest request, HttpServletResponse response, String name) {
Cookie cookie = getCookie(request, name);
if (cookie != null) {
cookie.setMaxAge(0);
cookie.setPath("/");
response.addCookie(cookie);
}
}
/** 刪除所有 Cookie */
public static void clearAll(HttpServletRequest request, HttpServletResponse response) {
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie c : cookies) {
c.setMaxAge(0);
c.setPath("/");
response.addCookie(c);
}
}
}
}
@PostMapping("/login")
public Result login(@RequestParam String username,
@RequestParam String password,
@RequestParam(required = false) Boolean remember,
HttpServletResponse response) {
User user = userService.authenticate(username, password);
// 生成 Token 或 Session...
if (Boolean.TRUE.equals(remember)) {
CookieUtils.add(response, "remember_token", tokenService.generatePersistentToken(user), 30 * 24 * 3600);
}
return Result.ok();
}
@PostMapping("/logout")
public Result logout(HttpServletRequest request, HttpServletResponse response) {
session.invalidate();
CookieUtils.clearAll(request, response);
return Result.ok();
}
CookieUtils.add(response, "JSESSIONID", sessionId, -1); // 瀏覽器關閉即失效
UTF-8 編碼/解碼,確保中文、Emoji 正確存取。@CookieValue,框架自動解碼,無需手動處理。| 值 | 行為 | 適用場景 |
|---|---|---|
Strict | 完全禁止第三方攜帶(連結跳轉也不帶) | 高安全操作(修改密碼、轉賬) |
Lax | 允許鏈接跳轉、GET 表單攜帶,禁止 POST/iframe/圖片請求 | **大多數業務 Cookie 推薦默認** |
None | 無限制,**必須配合 Secure=true (HTTPS)** | 跨域嵌入、第三方登錄、CDN 資源 |
工具類默認 Lax,兼容 CSRF 防護與正常導航。需 None 時手動調用 cookie.setAttribute("SameSite", "None") 並確保 setSecure(true)。
Cookie / 響應頭 Set-Cookie 驗證傳輸內容。
掌握 CookieUtils 封裝的 **讀/寫/刪/清** 四大核心操作、URL 編碼規範、SameSite 防 CSRF 策略,即可應對 99% 的 Servlet 端 Cookie 業務場景。建議將工具類納入項目公共模塊,統一 Cookie 安全基線(HttpOnly=true, SameSite=Lax, Path=/)。