什么是 Session?为什么需要它?
想象一下你去超市购物,你推着购物车,把商品一件件放进去,这个“购物车”Session,它是一个临时的、属于你个人的存储空间。

在 Web 应用中,Session 是一种服务器端的会话机制,它允许服务器在多个用户请求之间为特定用户(浏览器)保存信息。
核心问题:HTTP 是无状态的
- HTTP 协议本身是无状态的,意味着服务器不会主动记录你之前做了什么,你第一次请求 A 页面,服务器不知道你是谁;你接着请求 B 页面,服务器也认为这是一个全新的、独立的请求。
- 但现实应用中,我们需要记录用户状态,
- 用户登录后,后续请求都应该知道“这个用户已经登录了”。
- 购物车里的商品。
- 用户在多步骤表单中填写的信息。
Session 如何解决这个问题?
- 首次访问:当一个用户(浏览器)首次访问服务器时,服务器会创建一个唯一的 Session 对象,并为其分配一个
Session ID(通常是一个长字符串,如JSESSIONID=1234567890ABCDEFFEDCBA9876543210)。 - 响应 Cookie:服务器会将这个
Session ID通过一个名为JSESSIONID的 Cookie 发送给浏览器。 - 后续请求:浏览器在后续的每一次请求中,都会自动带上这个
JSESSIONIDCookie。 - 服务器识别:服务器收到请求后,会读取
Session ID,然后在服务器内存中查找对应的 Session 对象,如果找到了,就说明这个请求属于这个用户,服务器就可以从 Session 中获取之前保存的用户信息。
- Session 是服务器端的对象,用于存储用户会话数据。
- Session ID 是客户端(浏览器)的凭证,用于在多次请求中标识同一个用户。
- Cookie 是 Session ID 的载体,用于在浏览器和服务器之间传递这个凭证。
如何在 Java Web 中使用 Session?(以 Servlet 为例)
在 Java Web 开发中,最直接使用 Session 的方式是通过 Servlet API,我们主要使用 HttpSession 接口。

基本操作步骤
假设我们有一个登录场景,用户登录成功后,将用户信息存入 Session。
在 Servlet 中获取 Session
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1. 获取用户提交的用户名和密码
String username = request.getParameter("username");
String password = request.getParameter("password");
// 2. 模拟验证逻辑 (实际应用中应查询数据库)
if ("admin".equals(username) && "password".equals(password)) {
// 3. 获取 Session 对象
// 如果当前请求没有关联的 Session,则创建一个新的。
HttpSession session = request.getSession();
// 4. 向 Session 中存入数据
session.setAttribute("user", username); // "user" 是键, username 是值
session.setAttribute("loginTime", System.currentTimeMillis());
// 5. 登录成功,重定向到主页
response.sendRedirect("welcome.jsp");
} else {
// 登录失败,返回登录页面
response.sendRedirect("login.html?error=1");
}
}
}
在其他页面或 Servlet 中获取 Session 数据
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/welcome")
public class WelcomeServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1. 获取 Session 对象
// 注意:这里使用 false,如果请求中没有 Session,则返回 null,而不是创建新的。
// 这是为了避免未登录用户访问此页面时,服务器给他创建一个空的 Session。
HttpSession session = request.getSession(false);
// 2. 检查 Session 是否存在以及用户是否已登录
if (session != null && session.getAttribute("user") != null) {
// 3. 从 Session 中获取数据
String username = (String) session.getAttribute("user");
long loginTime = (Long) session.getAttribute("loginTime");
// 4. 显示欢迎信息
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.println("<h1>欢迎, " + username + "!</h1>");
out.println("<p>您于 " + new java.util.Date(loginTime) + " 登录。</p>");
out.println("<a href='logout'>退出登录</a>");
} else {
// 如果未登录,重定向到登录页面
response.sendRedirect("login.html");
}
}
}
销毁 Session(用户退出登录)
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
@WebServlet("/logout")
public class LogoutServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1. 获取 Session
HttpSession session = request.getSession(false);
// 2. 销毁 Session
if (session != null) {
session.invalidate(); // 销毁整个 Session,并使所有关联的 Session ID 失效
// 或者使用 session.removeAttribute("user"); // 只移除某个属性,Session 对象本身还存在
}
// 3. 重定向到登录页面
response.sendRedirect("login.html");
}
}
Session 的生命周期和配置
生命周期
- 创建:当调用
request.getSession()或request.getSession(true)时,如果尚不存在有效的会话,则创建一个新的会话。 - 活跃:当客户端持续发送带有有效
JSESSIONID的请求时,会话保持活跃。 - 销毁:
- 被动销毁(超时):如果客户端在一定时间内(默认30分钟)没有发送任何请求,服务器会自动销毁该 Session,这个时间可以配置。
- 主动销毁:调用
session.invalidate()。 - 服务器关闭或重启:所有 Session 都会被销毁。
配置 Session 超时时间
在 web.xml 文件中配置全局的 Session 超时时间(单位:分钟):
<session-config>
<!-- 设置 Session 超时时间为 15 分钟 -->
<session-timeout>15</session-timeout>
</session-config>
也可以在 Servlet 代码中动态设置:
// 设置 Session 超时时间为 10 分钟 (单位是秒) session.setMaxInactiveInterval(10 * 60);
Session vs. Cookie
| 特性 | Session | Cookie |
|---|---|---|
| 存储位置 | 服务器端 | 客户端(浏览器) |
| 安全性 | 高,敏感数据不经过网络,只传输一个 ID。 | 低,数据存储在客户端,可以被用户查看、修改,甚至禁用。 |
| 存储容量 | 较大,只受服务器内存限制。 | 较小,通常限制在 4KB 左右。 |
| 生命周期 | 可以设置超时时间,或手动销毁。 | 可以设置为“会话 Cookie”(关闭浏览器即失效)或“持久化 Cookie”(可设置过期时间)。 |
| 依赖性 | 依赖 Cookie(默认通过 JSESSIONID Cookie 传递 ID),如果禁用 Cookie,Session 机制会失效(除非使用 URL 重写)。 |
独立,不依赖任何其他技术。 |
现代框架中的 Session(如 Spring Boot)
在现代框架(如 Spring MVC / Spring Boot)中,对 Session 的使用进行了更高层次的封装,底层原理依然是 Servlet API,但使用起来更方便。

Spring Boot 中使用 Session
在 Spring Boot 中,你可以通过在 Controller 方法的参数中直接注入 HttpSession 对象来使用它。
示例:
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import javax.servlet.http.HttpSession;
@Controller
public class LoginController {
@GetMapping("/login")
public String loginPage() {
return "login"; // 返回 login.html 视图
}
@PostMapping("/login")
public String login(@RequestParam String username,
@RequestParam String password,
HttpSession session,
RedirectAttributes redirectAttributes) {
// 验证逻辑
if ("admin".equals(username) && "password".equals(password)) {
// 直接往 session 里存东西,和 Servlet 一样
session.setAttribute("user", username);
return "redirect:/welcome"; // 重定向到欢迎页
} else {
// 使用 RedirectAttributes 传递重定向时的数据
redirectAttributes.addFlashAttribute("error", "用户名或密码错误");
return "redirect:/login";
}
}
@GetMapping("/welcome")
public String welcome(HttpSession session) {
// 从 session 里取东西
String user = (String) session.getAttribute("user");
if (user != null) {
// ... 业务逻辑
return "welcome"; // 返回 welcome.html 视图
}
return "redirect:/login";
}
@GetMapping("/logout")
public String logout(HttpSession session) {
// 销毁 session
session.invalidate();
return "redirect:/login";
}
}
@SessionAttributes 注解
Spring MVC 还提供了 @SessionAttributes 注解,它可以将 Model 中的数据自动保存到 Session 中,并指定在哪些请求中需要从 Session 中取出数据,这在需要跨多个请求保持数据的场景下非常有用。
Session 的优缺点与最佳实践
优点
- 安全性高:敏感数据不暴露在客户端。
- 存储容量大:适合存储对象、列表等复杂数据。
缺点
- 占用服务器资源:每个 Session 都会占用服务器的内存,如果用户量巨大,会给服务器带来很大压力。
- 依赖 Cookie:如果用户禁用了浏览器 Cookie,Session 机制会失效(虽然可以通过 URL 重写来解决,但体验较差)。
最佳实践
- 只存必要数据:Session 中只存储少量、关键的用户信息(如用户ID、用户名),不要将大量数据或大对象(如 List, Map)存入 Session,这会消耗大量内存并影响序列化/反序列化性能。
- 及时清理:用户退出登录时,务必调用
session.invalidate()或移除不再需要的属性。 - 注意线程安全:在 Servlet 容器中,一个
HttpSession对象通常只被一个用户(一个线程)访问,Session 本身是线程安全的,但如果将 Session 对象传递给其他线程(如线程池),就需要自己处理同步问题。 - 考虑替代方案:
- 对于简单数据:可以直接使用 Cookie。
- 对于分布式系统:如果应用部署在多台服务器上(负载均衡),传统的本地内存 Session 就不适用了,这时需要使用分布式 Session 方案,如:
- Redis:将 Session 数据存储在 Redis 中,所有服务器都可以共享。
- Memcached:类似 Redis。
- Spring Session:一个优秀的 Spring 项目,可以无缝集成 Redis 等后端,实现分布式 Session 管理。
- 防止 Session 固定攻击:在用户登录成功后,可以重新生成一个新的 Session ID,并销毁旧的 Session ID,以防止攻击者利用已知的 Session ID 进行攻击。
// 登录成功后,防止 Session 固定攻击
HttpSession oldSession = request.getSession(false);
if (oldSession != null) {
oldSession.invalidate();
}
HttpSession newSession = request.getSession(true);
newSession.setAttribute("user", username); 