
本文系统讲解 http 无状态协议下如何通过 cookie 实现客户端会话跟踪,涵盖 cookie 创建、发送、读取、属性设置及典型应用场景,强调以手机号等自然标识替代 session id 的设计思路,并提供可运行的 java servlet 示例代码。
本文系统讲解 http 无状态协议下如何通过 cookie 实现客户端会话跟踪,涵盖 cookie 创建、发送、读取、属性设置及典型应用场景,强调以手机号等自然标识替代 session id 的设计思路,并提供可运行的 java servlet 示例代码。
在 Web 开发中,“会话”(Session)并非物理连接,而是逻辑概念:从用户打开浏览器首次访问服务器起,到关闭浏览器为止,其间发生的多次请求-响应交互构成一次会话。由于 HTTP 协议本身是无状态的——每次请求对服务器而言都是全新的、彼此独立的——服务器无法天然识别“这两次请求是否来自同一用户”。因此,必须借助会话跟踪技术,在多次请求间维持用户上下文,例如登录态、购物车内容或个性化偏好。
目前主流的会话跟踪方案分为两类:客户端存储型(Cookie) 和 服务端存储型(Session)。其中,Cookie 是最基础、最轻量且无需服务端额外资源的实现方式:它由服务器生成并写入客户端浏览器(或短信终端等 HTTP 客户端),后续请求自动携带该数据回传,从而让服务器得以识别用户身份或恢复会话状态。
✅ Cookie 的核心工作流程
-
服务器创建并下发 Cookie
使用 javax.servlet.http.Cookie 构造器创建键值对,并通过 HttpServletResponse.addCookie() 发送至客户端:@WebServlet("/login") public class LoginServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException { String phone = req.getParameter("phone"); // 如 +8613800138000 Cookie userCookie = new Cookie("user_phone", phone); userCookie.setPath("/"); // 全站有效 userCookie.setMaxAge(7 * 24 * 3600); // 持久化保存 7 天(秒) userCookie.setHttpOnly(true); // 禁止 JS 访问,提升安全性 userCookie.setSecure(true); // 仅 HTTPS 传输(生产环境必需) resp.addCookie(userCookie); resp.getWriter().print("Login OK"); } } 客户端自动回传 Cookie
浏览器在后续对同域路径的请求中,自动将匹配 path 和 domain 的 Cookie 放入 Cookie 请求头(如 Cookie: user_phone=%2B8613800138000),无需前端额外编码。-
服务器读取并解析 Cookie
通过 HttpServletRequest.getCookies() 获取所有 Cookie 数组,遍历匹配名称提取值:@WebServlet("/profile") public class ProfileServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { Cookie[] cookies = req.getCookies(); String phone = null; if (cookies != null) { for (Cookie c : cookies) { if ("user_phone".equals(c.getName())) { phone = c.getValue(); // 解码后为原始手机号 break; } } } if (phone == null) { resp.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Not logged in"); return; } // 基于手机号查询用户资料(如从数据库或缓存) String profile = getUserProfileByPhone(phone); resp.getWriter().print("Welcome, " + profile); } }
⚠️ 关键注意事项与最佳实践
- 不依赖 HttpSession 或 Cookie 存储敏感信息:Cookie 明文存储于客户端,易被窃取或篡改。切勿存放密码、Token 或支付凭证;推荐仅存非敏感标识(如脱敏手机号、只读用户ID),敏感状态应由服务端 Session/Redis 管理。
- 明确区分“会话标识”与“业务标识”:示例中使用 user_phone 而非默认 JSESSIONID,正是为了以自然业务键(手机号)替代抽象会话键——这更符合短信场景(如 Twilio 回调中 From 参数即天然唯一标识),避免引入 HttpSession 及其依赖的 Cookie 机制,提升架构清晰度与可测试性。
- 合理设置生命周期:setMaxAge(0) 表示删除 Cookie;setMaxAge(-1)(默认)表示会话级 Cookie(关闭浏览器即失效);正整数值表示秒级持久化时间。生产环境务必显式设置,避免意外长期驻留。
- 路径与域控制作用范围:setPath("/admin") 仅使该 Cookie 在 /admin/* 下发送;setDomain(".example.com") 支持子域名共享(如 api.example.com 和 www.example.com)。
- 中文与特殊字符需编码:Cookie 值不支持 UTF-8 直接存储,须使用 URLEncoder.encode(value, "UTF-8") 编码,读取时用 URLDecoder.decode(cookie.getValue(), "UTF-8") 解码。
? 总结:为什么用手机号,而不用 Cookie 或 Session?
正如问题所洞察——在短信通信(如 Twilio 场景)中,From 字段(发送方手机号)本身就是强唯一、不可伪造、天然稳定的会话标识符。此时强行引入 HttpSession 并依赖 Cookie 维护会话 ID,不仅增加复杂度(需处理 Cookie 过期、跨域、安全属性),更违背了“以业务实体为中心”的设计原则。正确做法是:直接提取 request.getParameter("From") 作为会话主键,结合 Redis 或数据库持久化用户上下文,彻底解耦会话管理与传输层机制。Cookie 在此场景中,仅作为可选的辅助手段(如 Web 端同步登录态),而非会话跟踪的必要依赖。
掌握 Cookie 的本质——它是 HTTP 协议层的标准化键值传递机制,而非某种“必须配合 Session 使用”的附属品——才能真正写出清晰、健壮、面向业务的会话管理代码。
立即学习“Java免费学习笔记(深入)”;


















