Cookie 与 Session:服务器怎么记住你是谁
HTTP 是"无状态"的——服务器处理完一个请求,就不记得你是谁了。你登录了一下,刷新又让你登录?用户会骂娘。Cookie 和 Session 就是解决"跨请求记住用户"的两件套。
论一图理解 Cookie + Session 配合
第 1 次访问:服务器在内存里给你建一个 Session(存你的登录信息),并给你发一张写着 SessionId 的小纸条(Cookie),让浏览器存着。
第 2 次起:浏览器每次请求都自动把小纸条带上,服务器一看 SessionId:"哦,是你,登录信息还在。"——你就不用重新登录了。
区别记法:Cookie 存在用户浏览器(小,不安全),Session 存在服务器(大,占内存)。Cookie 是"门票",Session 是"后台档案"。
登录成功写 Session,过滤器里校验
// 登录 Servlet 里,账号密码对了之后:
HttpSession session = req.getSession(); // 没有就新建一个
session.setAttribute("loginUser", user); // 把用户对象塞进 Session
session.setMaxInactiveInterval(30 * 60); // 30 分钟不操作自动失效
// Cookie 手动写一个(比如"记住我 7 天"):
Cookie c = new Cookie("token", tokenValue);
c.setMaxAge(7 * 24 * 60 * 60); // 7 天
c.setHttpOnly(true); // JS 读不到,防 XSS 偷 Cookie
c.setSecure(true); // 只在 HTTPS 下传输
c.setAttribute("SameSite", "Strict"); // 跨站请求不带 Cookie,防 CSRF
resp.addCookie(c);
Cookie 常用属性:安全三件套一个都不能少
| 属性 | 作用 & 为什么 |
|---|---|
| MaxAge | 活多久(秒)。负数=会话级(关浏览器就没),0=立刻删除。 |
| HttpOnly | JavaScript 读不到这个 Cookie。没有它,XSS 一打,你的登录 Cookie 就被偷了。 |
| Secure | 只在 HTTPS 传输。HTTP 下发它,等于半路裸奔。 |
| SameSite | Strict/Lax/None。控制跨站请求带不带 Cookie,主要防 CSRF。 |
| Path / Domain | 限定 Cookie 在哪些路径 / 域名下生效,别到处乱发。 |
Session 什么时候没:三种死法
| 情况 | 说明 |
|---|---|
| 超时自动失效 | 超过 setMaxInactiveInterval 没活动,容器自动回收。 |
| 主动登出 | 代码里 session.invalidate(),立刻清空。 |
| 服务器重启 | 默认存在内存里,重启就没——这也是要分布式 Session 的原因之一。 |
用户禁用 Cookie 怎么办:URL 重写(了解即可)
// 万一用户把浏览器 Cookie 关了,SessionId 传不过去。
// 老办法:把 SessionId 拼在 URL 后面,容器自动处理:
String link = resp.encodeURL("/board");
// 输出可能变成 /board;jsessionid=ABC123(现在基本没人用,了解即可)
Session 存在哪台 Tomcat 的内存里,就只有那台认识你。一旦部署成"多台 Tomcat 集群",用户请求被负载均衡分到另一台,那边没他的 Session——又要登录了。解决方案:① Session 粘滞(负载均衡器固定把同一个用户打到同一台,治标不治本);② Session 集中存储(把 Session 放 Redis,所有机器共享,生产主流);③ 干脆不用 Session,用 JWT Token(无状态)。这也是现代前后端分离架构抛弃 Session 的原因。
完整案例:登录写 Cookie + Session + 分布式 Session 思路
// 登录成功后:写 Session + 写"记住我"Cookie
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws IOException, ServletException {
String user = req.getParameter("username");
String pwd = req.getParameter("password");
if ("admin".equals(user) && "123456".equals(pwd)) {
// ① 写 Session:服务器内存存登录态
HttpSession session = req.getSession();
session.setAttribute("loginUser", user);
session.setMaxInactiveInterval(30 * 60); // 30 分钟过期
// ② 防 Session 固定攻击:登录后换新 SessionId
req.changeSessionId();
// ③ "记住我 7 天":写一个安全的 Cookie
Cookie remember = new Cookie("remember", java.util.UUID.randomUUID().toString());
remember.setMaxAge(7 * 24 * 60 * 60);
remember.setHttpOnly(true); // JS 读不到,防 XSS
remember.setSecure(true); // 只 HTTPS 传
remember.setAttribute("SameSite", "Strict");
resp.addCookie(remember);
resp.sendRedirect("/home");
} else {
req.setAttribute("error", "用户名或密码错");
req.getRequestDispatcher("/login.jsp").forward(req, resp);
}
}
}
// 登出:销毁 Session
@WebServlet("/logout")
public class LogoutServlet extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
HttpSession session = req.getSession(false); // 没有就不新建
if (session != null) session.invalidate(); // 立刻清空
resp.sendRedirect("/login.jsp");
}
}
// 分布式 Session 思路(多台 Tomcat 集群):
// 方案 A:Session 粘滞(Nginx 把同一用户固定打到同一台 Tomcat)
// 方案 B:Session 集中存 Redis(Spring Session 实现,所有机器共享)
// 方案 C:不用 Session,用 JWT Token(无状态,服务器不存)
浏览器禁用 Cookie 怎么办:URL 重写兜底
Session 靠 Cookie 存 JSESSIONID,但万一用户浏览器把 Cookie 禁了,Session 就废了。Servlet 提供URL 重写兜底:把 SessionId 直接拼在 URL 尾巴上,比如 /list;jsessionid=ABC123。
// 响应里:用 encodeURL 自动给 URL 拼上 SessionId(Cookie 没禁时它原样返回)
// 注意是 ;jsessionid= 不是 ? ,这是路径参数不是查询串
String link = resp.encodeURL("/list");
// 浏览器支持 Cookie 时返回 /list;禁用时返回 /list;jsessionid=xxx
resp.getWriter().println("<a href=\\" + link + "\\">留言列表</a>");
// 重定向地址也要 encodeRedirectURL
resp.sendRedirect(resp.encodeRedirectURL("/home"));
论实际还要不要学
URL 重写有安全隐患(SessionId 出现在 URL 里,容易被浏览器历史、Referer 泄漏),现代 Web 基本都用 JWT / Token 放 Authorization 头,已经不靠这套了。知道"Session 还有这种兜底机制、SessionId 别放 URL"这两点就够,面试能答上即可。
1.(概念题)Cookie 和 Session 区别?谁存在哪?
查看答案
答案:Cookie 存在浏览器(小、不安全),Session 存在服务器内存(大、占内存)。Cookie 是门票(SessionId),Session 是后台档案。
2.(概念题)HttpOnly Cookie 防什么?
查看答案
答案:防 XSS 攻击偷 Cookie。没有 HttpOnly,JS 能读 document.cookie,脚本一注入就把登录态偷走。
3.(概念题)分布式部署 Session 为什么是问题?怎么解决?
查看答案
答案:Session 存在单机内存,负载均衡分到另一台就不认你。解法:① Session 粘滞;② Session 集中存 Redis;③ 不用 Session 用 JWT。
4.(安全题)为什么登录成功后要 changeSessionId()?
查看答案
答案:防 Session 固定攻击。攻击者事先给你种一个 SessionId,你登录后这个 SessionId 就有了你的登录态,攻击者拿它就能冒充。登录后换新 SessionId,旧的就废了。