记得第一次用 Cookie 解决 “登录状态保持” 问题 —— 那时才明白,Cookie 就像用户访问网站时的 “临时身份证”:服务器给用户发一张写有身份信息的小纸条(Cookie),用户下次访问时带着纸条,服务器就能快速认出 “你是谁”,不用每次都重新登录。

在 Servlet 中,Cookie 的核心作用是 “在客户端存储少量状态数据”,弥补 HTTP 协议 “无状态” 的缺陷。就像医院给病人发就诊卡,卡上记录基本信息,病人复诊时不用再填一遍表单。它的工作流程分三步:

  • 服务器发放:Servlet 通过response.addCookie()生成 Cookie,随响应发送到客户端;
  • 客户端存储:浏览器将 Cookie 保存在本地(内存或硬盘);
  • 客户端携带:下次访问同一网站时,浏览器自动将 Cookie 随request.getCookies()发送给服务器。

早年做电商网站时,我曾因没理解 Cookie 的 “域名绑定” 特性,导致用用户登录后,访问二级域名时时又要重新登录 —— 就像病人拿着 A 医院的就诊卡去 B 医院,自然无法识别。理解 Cookie 的设计逻辑,是做好 Web 身份验证的基础。

一、从创建到使用的 Cookie 实战

1. 会话 Cookie 与持久 Cookie

会话 Cookie 像 “临时身份证”,关闭浏览器就失效;持久 Cookie 像 “长期居住证”,能在客户端保存指定时间:

// 电商登录Servlet:创建会话Cookie与持久Cookie

public class LoginServlet extends HttpServlet {

@Override

protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {

String username = req.getParameter("username");

String password = req.getParameter("password");

boolean rememberMe = "on".equals(req.getParameter("rememberMe"));

// 1. 验证用户(实际项目需加密校验)

User user = UserService.verify(username, password);

if (user == null) {

resp.getWriter().write("账号或密码错误");

return;

}

// 2. 创建会话Cookie:保存用户ID,关闭浏览器失效(默认)

Cookie userIdCookie = new Cookie("userId", user.getId().toString());

userIdCookie.setPath("/"); // 全站可用(所有路径都能访问该Cookie)

// 3. 创建持久Cookie:若勾选“记住我”,保存7天

Cookie rememberCookie = new Cookie("rememberMe", username);

rememberCookie.setPath("/");

if (rememberMe) {

rememberCookie.setMaxAge(7 * 24 * 60 * 60); // 有效期7天(秒为单位)

} else {

rememberCookie.setMaxAge(0); // 不记住:立即删除Cookie

}

// 4. 发送Cookie到客户端

resp.addCookie(userIdCookie);

resp.addCookie(rememberCookie);

resp.getWriter().write("登录成功,跳转首页...");

}

}

// 首页Servlet:读取Cookie识别用户

public class IndexServlet extends HttpServlet {

@Override

protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {

Cookie[] cookies = req.getCookies();

String userId = null;

String username = null;

// 遍历Cookie查找目标数据(注意:cookies可能为null)

if (cookies != null) {

for (Cookie cookie : cookies) {

if ("userId".equals(cookie.getName())) {

userId = cookie.getValue();

} else if ("rememberMe".equals(cookie.getName())) {

username = cookie.getValue();

}

}

}

// 根据Cookie判断登录状态

if (userId != null) {

resp.getWriter().write("欢迎回来," + username + "!用户ID:" + userId);

} else {

resp.getWriter().write("请先登录");

resp.sendRedirect("/login"); // 未登录跳转登录页

}

}

}

2. 安全属性配置(支付模块防劫持)

给 Cookie 添加安全属性,就像给 “身份证” 加安全锁,防止被篡改或窃取:

// 支付Servlet:配置安全Cookie

public class PayServlet extends HttpServlet {

@Override

protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {

// 1. 生成支付会话标识(防止CSRF攻击)

String payToken = UUID.randomUUID().toString();

// 2. 创建安全Cookie

Cookie payTokenCookie = new Cookie("payToken", payToken);

payTokenCookie.setPath("/pay"); // 仅支付相关路径可用,缩小作用域

payTokenCookie.setMaxAge(30 * 60); // 有效期30分钟

// 安全属性配置

payTokenCookie.setHttpOnly(true); // 禁止JS访问,防XSS攻击(关键!)

payTokenCookie.setSecure(true); // 仅HTTPS传输,防中间人窃取

payTokenCookie.setSameSite("Strict"); // 严格限制跨站携带,防CSRF攻击

resp.addCookie(payTokenCookie);

resp.getWriter().write("支付会话已创建,token:" + payToken);

}

}

三、Cookie 踩坑记

曾开发web系统时,遇到一个典型问题:用户在办公室电脑勾选 “记住我” 登录后,回家用家里电脑访问,却无法保持登录状态。排查后发现,Cookie 的setPath("/oa")设置成了 OA 系统的子路径,而家里电脑访问的是根域,导致 Cookie 无法被识别 —— 就像把 “办公楼门禁卡” 做成了 “某楼层门禁卡”,出了楼层就失效。

解决办法是将setPath("/"),确保整个域名下的所有路径都能访问该 Cookie。但新问题又出现了:财务模块的敏感 Cookie(如审批权限标识),在非财务路径也能被读取。最终通过 “路径隔离” 优化:普通 Cookie 设path="/",敏感 Cookie 设path="/oa/finance",既保证登录状态全站可用,又防止敏感数据泄露。

另一次是 一次风控事件, Cookie 劫持事件:黑客通过 XSS 攻击窃取用户的支付 Cookie,伪造支付请求。后来我们给所有支付相关 Cookie 添加HttpOnly=true和Secure=true,就像给 “银行卡密码” 加了两层锁 ——JS 无法读取,只能通过 HTTPS 传输,彻底杜绝了劫持风险。

四、不同类型 Cookie 的 “优劣对决”

Cookie 类型

核心特性

像什么场景

电商 / OA 适配性

痛点

会话 Cookie

内存存储,关闭浏览器失效

临时身份证,当天有效

普通登录、临时会话

关闭浏览器需重新登录

持久 Cookie

硬盘存储,有过期时间

长期居住证,有效期内可用

“记住我” 功能、自动登录

长期存储有安全风险

安全 Cookie(HttpOnly+Secure)

禁止 JS 访问,仅 HTTPS 传输

带密码锁的身份证,仅限安全通道使用

支付、财务审批等敏感模块

非 HTTPS 环境无法使用

跨域 Cookie

跨不同域名共享

跨国护照,多地区通用

多系统单点登录(SSO)

浏览器限制多,配置复杂

2020 年我们做过一次用户体验测试:会话 Cookie 的登录成功率约 85%(用户常关闭浏览器),持久 Cookie(7 天有效期)的登录成功率提升到 98%,但安全风险增加了 30%。这也说明:Cookie 的选择需在 “用户体验” 和 “安全” 之间找平衡 —— 普通功能用持久 Cookie,敏感功能用会话 Cookie + 安全属性。

五、踩过的 “Cookie 坑”

路径与域名陷阱

有个同事在 Servlet 中创建 Cookie 时,没设置setPath(),默认路径是当前请求路径(如/login),导致在/index路径无法读取 Cookie。这就像把 “图书馆借阅卡” 的使用范围设为 “借阅处”,到 “阅览室” 就无法使用。

解法:根据需求明确设置setPath(),全站可用设/,局部功能设具体路径(如/pay)。

安全属性遗漏

某 OA 系统的会话 Cookie 没加HttpOnly,被 XSS 攻击窃取用户身份,导致未授权用户访问审批页面。这就像把 “家门钥匙” 放在门口,任何人都能拿走。

解法:所有涉及身份、权限的 Cookie,必须添加HttpOnly=true;涉及支付、财务的 Cookie,额外添加Secure=true和SameSite=Strict。

跨域 Cookie 失效

电商平台想在两个系统之间共享登录 Cookie,直接创建 Cookie 后发现无法跨域读取。这就像用 “北京身份证” 在上海办理业务,系统不认可。

原理:浏览器默认禁止跨域携带 Cookie,需配合 CORS(跨域资源共享)配置。

解法:服务器端设置响应头Access-Control-Allow-Credentials=true,客户端 AJAX 请求设withCredentials=true,同时 Cookie 需设置domain=".company.com"(主域名一致)。

大小超限陷阱

有个同事在 Cookie 中存储大量用户信息(如完整个人资料),导致 Cookie 大小超过 4KB,被浏览器自动截断。这就像把 “10 页的简历” 塞进 “1 页的身份证”,多余内容会丢失。

解法:Cookie 仅存储少量关键数据(如用户 ID、令牌),大量数据存数据库或 Redis,通过 Cookie 中的关键数据查询。

六、最佳实践:安全高效的 Cookie 使用指南

设计层面

  • 会话 Cookie 与持久 Cookie 分工:普通登录用持久 Cookie(7~30 天),敏感操作(支付、改密码)用会话 Cookie;
  • 数据最小化:Cookie 仅存 ID、令牌等轻量数据(不超过 1KB),避免存储 JSON、HTML 等大内容;
  • 路径与域名精准配置:根据功能范围设置setPath()和setDomain(),缩小 Cookie 作用域,降低泄露风险。

安全层面

  • 敏感 Cookie 必加三大属性:HttpOnly=true(防 XSS)、Secure=true(防中间人)、SameSite=Strict/Lax(防 CSRF);
  • 定期更新 Cookie:如支付令牌每 30 分钟刷新一次,避免长期有效导致风险;
  • 加密存储:Cookie 中的敏感数据(如用户 ID)需加密(如 AES 加密),防止明文泄露。

性能层面

  • 减少 Cookie 数量:同一功能尽量合并 Cookie(如用一个 Cookie 存储多个键值对,用分隔符分隔),避免浏览器每次请求携带大量 Cookie;
  • 合理设置过期时间:短期会话设MaxAge=-1(会话结束失效),长期功能设MaxAge=30*24*60*60(30 天),避免 Cookie 长期占用客户端空间;
  • 静态资源免 Cookie:将图片、JS、CSS 等静态资源部署到无 Cookie 域名,减少请求携带的 Cookie 体积,提升加载速度。

七、云原生时代的 Cookie 演进

如今都是都用 JWT 令牌了,还需要 Cookie 吗?” 我笑着打开他们的登录模块 ——JWT 令牌依然是通过 Cookie 存储在客户端,只是 Cookie 的 “角色” 从 “存储数据” 变成了 “携带令牌的容器”。

云原生时代的 Cookie,有了新的应用场景:

  • 分布式追踪:在 Cookie 中存储 Trace ID,跨微服务追踪请求链路,就像给 “快递包裹” 贴跟踪码,全程监控流转过程;
  • A/B 测试:通过 Cookie 存储用户的测试分组(如 A 组用新版界面,B 组用旧版),确保用户在会话中体验一致的版本。

最后小结

之前处理Cookie 处理代码,那时还没有HttpOnly属性,只能手动加密数据防泄露;现在的代码里,HttpOnly、Secure、SameSite已成标配 —— 不是 Cookie 变复杂了,而是 Web 安全的要求更高了。

Cookie 就像 Web 世界的 “老伙计”,从早期的简单身份标识,到现在的安全凭证载体,始终在 “无状态” 的 HTTP 协议中,默默承担着 “保持状态” 的重任。它虽小(仅 4KB),却支撑着电商登录、OA 审批、支付验证等核心功能 —— 就像老木匠手中的 “小锤子”,虽不起眼,却能打造出复杂的家具。理解 Cookie 的设计逻辑、安全属性和使用技巧,不仅是写好 Servlet 的关键,更是理解 Web 技术 “用户体验与安全平衡” 的窗口。毕竟,再先进的身份验证方案,最终都需要一个 “载体” 传递给客户端 —— 而 Cookie,依然是最成熟、最通用的选择之一。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐