Servlet Session 跟踪
四种会话跟踪方案、HttpSession 的用法与失效策略。
Key Points容器用 JSESSIONID 把多次请求关联到同一个 HttpSession;Session 存在服务端,Cookie 里只放 ID。
HTTP 是无状态的
Between two requests the server does not know you. To "remember" a user, the client must send the same identifier every time; the four approaches differ only in where that identifier lives.
| Approach | Where | Pros / Cons |
|---|---|---|
| Cookie | 客户端 Cookie | 简单、自动携带;可被禁用 |
| HttpSession | 服务端 + Cookie 里的 ID | 安全、容量大;占服务端内存 |
| URL rewriting | URL 的 ;jsessionid= | Cookie 禁用时可用;URL 难看易泄露 |
| Hidden field | 表单隐藏字段 | 只对表单提交有效 |
HttpSession 基本用法
Java
@WebServlet("/cart")
public class CartServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
req.setCharacterEncoding("UTF-8");
HttpSession session = req.getSession(); // 没有就创建
String item = req.getParameter("item");
@SuppressWarnings("unchecked")
List<String> cart = (List<String>) session.getAttribute("cart");
if (cart == null) {
cart = new ArrayList<>();
session.setAttribute("cart", cart);
}
if (item != null && !item.isBlank()) {
cart.add(item);
}
resp.setContentType("text/plain;charset=UTF-8");
resp.getWriter().println("购物车:" + cart);
}
}getSession() equals getSession(true): create if absent. To query without creating, use getSession(false), which returns null when there is none — mandatory in auth checks, or every request silently spawns a session.
Session 的常用操作
Java
HttpSession s = req.getSession(false);
if (s != null) {
s.setAttribute("uid", 42); // 存
Object uid = s.getAttribute("uid"); // 取
s.removeAttribute("uid"); // 删单个
s.invalidate(); // 销毁整个会话(登出)
String id = s.getId(); // JSESSIONID 的值
long created = s.getCreationTime();
int timeout = s.getMaxInactiveInterval(); // 秒
}超时与失效
XML
<session-config>
<session-timeout>30</session-timeout> <!-- 单位:分钟 -->
<cookie-config>
<http-only>true</http-only>
<secure>true</secure>
</cookie-config>
<tracking-mode>COOKIE</tracking-mode>
</session-config>- 默认 30 分钟无活动即失效,从最后一次请求算起
setMaxInactiveInterval(-1)表示永不过期,生产环境不要用- 登出时
invalidate(),不要只removeAttribute
Sessions live in container memory by default: a restart loses them all, and two servers do not share them. For durability or clustering you need session persistence or a central store such as Redis, or users get logged out at random.
登出与防固定攻击
Java
// 登出:销毁旧会话
HttpSession old = req.getSession(false);
if (old != null) old.invalidate();
// 登录成功后换一个新会话,防会话固定攻击
HttpSession fresh = req.getSession(true);
req.changeSessionId(); // 或直接 invalidate 后 getSession(true)
fresh.setAttribute("uid", user.getId());HTTP 是无状态的
两次请求之间,服务器不认识你。要「记住」用户,只能让客户端每次带上同一个标识。四种方案的区别就是标识放在哪里。
| 方案 | 标识位置 | 优劣 |
|---|---|---|
| Cookie | 客户端 Cookie | 简单、自动携带;可被禁用 |
| Session | 服务端 + Cookie 里的 ID | 安全、容量大;占服务端内存 |
| URL 重写 | URL 的 ;jsessionid= | Cookie 禁用时可用;URL 难看易泄露 |
| 隐藏域 | 表单隐藏字段 | 只对表单提交有效 |
HttpSession 基本用法
Java
@WebServlet("/cart")
public class CartServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
req.setCharacterEncoding("UTF-8");
HttpSession session = req.getSession(); // 没有就创建
String item = req.getParameter("item");
@SuppressWarnings("unchecked")
List<String> cart = (List<String>) session.getAttribute("cart");
if (cart == null) {
cart = new ArrayList<>();
session.setAttribute("cart", cart);
}
if (item != null && !item.isBlank()) {
cart.add(item);
}
resp.setContentType("text/plain;charset=UTF-8");
resp.getWriter().println("购物车:" + cart);
}
}getSession() 等价于 getSession(true):不存在就新建。只想查询、不想创建时用 getSession(false),返回 null 表示没有会话——登录校验里必须这么写,否则每次访问都会凭空造一个 Session。
Session 的常用操作
Java
HttpSession s = req.getSession(false);
if (s != null) {
s.setAttribute("uid", 42); // 存
Object uid = s.getAttribute("uid"); // 取
s.removeAttribute("uid"); // 删单个
s.invalidate(); // 销毁整个会话(登出)
String id = s.getId(); // JSESSIONID 的值
long created = s.getCreationTime();
int timeout = s.getMaxInactiveInterval(); // 秒
}超时与失效
XML
<session-config>
<session-timeout>30</session-timeout> <!-- 单位:分钟 -->
<cookie-config>
<http-only>true</http-only>
<secure>true</secure>
</cookie-config>
<tracking-mode>COOKIE</tracking-mode>
</session-config>- 默认 30 分钟无活动即失效,从最后一次请求算起
setMaxInactiveInterval(-1)表示永不过期,生产环境不要用- 登出时
invalidate(),不要只removeAttribute
Session 默认存在容器内存里,重启就全丢;多台服务器之间也不共享。要跨重启、跨实例,得配 Session 持久化或集中存储(Redis),否则用户会莫名其妙被登出。
登出与防固定攻击
Java
// 登出:销毁旧会话
HttpSession old = req.getSession(false);
if (old != null) old.invalidate();
// 登录成功后换一个新会话,防会话固定攻击
HttpSession fresh = req.getSession(true);
req.changeSessionId(); // 或直接 invalidate 后 getSession(true)
fresh.setAttribute("uid", user.getId());