HttpServletRequest的Cookie的设置
发布时间:2025-09-05 00:02       
使用 HttpServletRequest 读取 Cookie、以及正确设置/删除 Cookie(Servlet 原生做法)
先把关键点说清楚 ✅:
- 读取:用
HttpServletRequest#getCookies()
(只读,可能为null
)。 - 设置/删除:用
HttpServletResponse#addCookie(Cookie)
(服务端下发给浏览器)。HttpServletRequest
本身不能设置 Cookie。
一、设置 Cookie(安全属性齐全)🍪
// 伪代码:在 doPost/doGet 中
String sid = URLEncoder.encode(token, StandardCharsets.UTF_8); // 避免特殊字符
Cookie c = new Cookie("sid", sid);
// 基础属性
c.setPath("/"); // 推荐设为根,避免子路径不可见
c.setMaxAge(7 * 24 * 3600); // 7 天;会话 Cookie 用 -1
// 可选:跨子域共享(如 a.example.com 与 b.example.com)
c.setDomain("example.com"); // 不设则为 host-only,仅当前主机可见
// 安全属性
c.setHttpOnly(true); // 禁止 JS 读取,防 XSS
c.setSecure(request.isSecure()); // 仅在 HTTPS 下发送
response.addCookie(c);
解释:
URLEncoder
让值只含安全字符(也可用 Base64)。HttpOnly
/Secure
是生产必备 🔐。- 默认 Path 若不设置,会按规范取当前请求路径的父路径,容易出现别的路径拿不到 Cookie 的现象,显式设置为
/
最稳。
关于 SameSite(强烈建议设置)
Servlet 原生 Cookie
尚未内置 SameSite 属性(不同容器支持进度不一)。通用做法:手动构造 Set-Cookie 头(只构造一次,不再调用 addCookie
以免重复)。
String value = URLEncoder.encode(token, StandardCharsets.UTF_8);
String header = "sid=" + value
+ "; Max-Age=" + (7 * 24 * 3600)
+ "; Path=/"
+ "; HttpOnly"
+ "; Secure" // 若 SameSite=None,必须同时 Secure
+ "; SameSite=None"; // 备选:Lax / Strict
response.addHeader("Set-Cookie", header);
选择建议:
SameSite=Lax
:大多数站点登录态够用,能减少 CSRF。SameSite=None; Secure
:跨站场景(如嵌入 iframe、跨站回调)必需。Strict
:最安全,但跨站返回时不带 Cookie。
二、读取 Cookie(判空与解码)🔎
public static Optional<String> readCookie(HttpServletRequest request, String name) {
Cookie[] arr = request.getCookies();
if (arr == null) return Optional.empty(); // 可能为 null
for (Cookie ck : arr) {
if (name.equals(ck.getName())) {
String val = URLDecoder.decode(ck.getValue(), StandardCharsets.UTF_8);
return Optional.ofNullable(val);
}
}
return Optional.empty();
}
解释:
getCookies()
可能返回null
,必须判空。- 读取时与写入保持同一编码方式(
URLDecoder
对应URLEncoder
)。 - 如果设置了
Domain
/Path
,浏览器只会在符合作用域的请求里回传,其他请求上自然读不到。
三、更新与删除(务必匹配作用域)🧹
更新:与设置相同,同名 Cookie 再发一次即可覆盖(浏览器以最后一个为准)。
删除:必须 匹配原来的 Path
与 Domain
,再把 Max-Age
设为 0
。
Cookie del = new Cookie("sid", "");
del.setPath("/"); // 与原 Cookie 一致
// 若曾设置过 Domain,也要一致:del.setDomain("example.com");
del.setMaxAge(0); // 立即过期
del.setHttpOnly(true);
del.setSecure(true);
response.addCookie(del);
解释:
- 作用域不同等同于“另一个”Cookie,浏览器不会删对。
- 删除也建议带上
HttpOnly/Secure
,有些客户端对属性敏感,保持一致更稳妥。
四、常见坑与对策(速查表)📚
问题/现象 | 根因 | 对策 |
---|---|---|
本地能读,子路径/子域读不到 | Path/Domain 作用域不一致 | Path 统一设为 / ;需要跨子域就设 Domain=example.com |
跨站场景不带 Cookie | SameSite 默认限制 | 用 SameSite=None; Secure |
JS 能读到敏感 Cookie | 未设 HttpOnly | 登录态/敏感信息一律 HttpOnly |
HTTP 下 Cookie 不传 | 设置了 Secure | 开发环境用 HTTPS 或临时去掉 Secure(生产必须启用) |
值出现乱码或分号等非法字符 | 未编码 | 使用 URLEncoder 或 Base64 |
删除失败 | Path/Domain 不匹配 | 用与创建时完全一致的 Path/Domain 删除 |
过多/过大 | 浏览器有数量/体积限制(单个约 4KB 量级) | 精简内容,改存服务器会话,Cookie 放 key/指针 |
五、最小可用模板(登录态示例)🚀
// 写入
void writeSid(HttpServletRequest req, HttpServletResponse resp, String token) {
String v = URLEncoder.encode(token, StandardCharsets.UTF_8);
// 这里示例:SameSite=Lax,更安全的默认
String header = "sid=" + v + "; Max-Age=" + (7*24*3600) + "; Path=/; HttpOnly; Secure; SameSite=Lax";
resp.addHeader("Set-Cookie", header);
}
// 读取
Optional<String> sid(HttpServletRequest req) {
return readCookie(req, "sid");
}
// 删除
void removeSid(HttpServletResponse resp) {
Cookie del = new Cookie("sid", "");
del.setPath("/");
del.setMaxAge(0);
del.setHttpOnly(true);
del.setSecure(true);
resp.addCookie(del);
}