会话管理
什么是会话管理
会话管理(Session Management) 是 Web 开发中的一个重要概念,主要用于跟踪用户在网站上的活动状态。由于 HTTP 协议是无状态的(即每次请求都是独立的,服务器不会记住之前的请求),因此需要通过会话管理技术来维护用户的状态信息。
为什么需要会话管理
因为 HTTP 是无状态协议。
- 无状态就是不保存状态,即无状态协议(stateless),HTTP 协议自身不对请求和响应之间的通信状态进行保存,也就是说,在 HTTP 协议这个级别,协议对于发送过的请求或者响应都不做持久化处理。
- 简单理解:浏览器发送请求,服务器接收并响应,但是服务器不记录请求是否来自哪个浏览器,服务器没记录浏览器的特征,就是客户端的状态。
举例:张三去一家饭馆点了几道菜,觉得味道不错,第二天又去了,对老板说,还点上次的那几道菜。
- 无状态:老板没有记录张三是否来过,更没有记录上次他点了那些菜,张三只能重新再点一遍。
- 有状态:老板把每次来吃饭的用户都做好记录,查阅一下之前的记录,查到了张三之前的菜单,直接下单。
会话管理的作用
- 跟踪用户状态:识别用户并保持其状态。
- 维护用户数据:存储和检索用户相关的数据。
- 实现身份验证和授权:管理用户登录和权限控制。
- 提高用户体验:提供个性化的服务和功能。
- 实现跨页面数据共享:在多个页面间传递数据。
- 增强安全性:防止恶意攻击和滥用。
- 支持分布式系统:在集群环境中共享用户状态。
会话管理实现的方式
在 JavaWeb 中,会话管理主要通过以下两种方式实现:Cookie 和 Session。
Cookie
Cookie 是服务器发送到客户端(浏览器)的一小段数据,浏览器会将其保存,并在后续的请求中自动发送回服务器。Cookie 通常用于存储一些简单的用户信息,比如用户偏好、登录状态等。
Cookie 的工作流程
- 服务端创建 Cookie,将 Cookie 放入响应对象中,Tomcat 容器将 Cookie 转化为 set-cookie 响应头,响应给客户端。
- 客户端在收到 Cookie 的响应头时,在下次请求该服务的资源时,会以 Cookie 请求头的形式携带之前收到的 Cookie。
- Cookie 是一种键值对格式的数据,从 tomcat8.5 开始可以保存中文,但是不推荐。
- 由于 Cookie 是存储于客户端的数据,比较容易暴露,一般不存储一些敏感或者影响安全的数据。
Cookie 的特点
- 存储在客户端(浏览器)。
- 大小有限制(通常每个 Cookie 不超过 4KB)。
- 可以设置过期时间,分为会话 Cookie(关闭浏览器后失效)和持久 Cookie(在指定时间内有效)。
- 每次请求都会自动发送到服务器,可能会增加网络开销。
Cookie 的时效性
默认情况下 Cookie 的有效期是一次会话范围内,我们可以通过 Cookie 的 setMaxAge()
方法让 Cookie 持久化保存到浏览器上。
- 会话级 Cookie(Session Cookie)
- 服务器端并没有明确指定 Cookie 的存在时间。
- 在浏览器端,Cookie 数据存在于内存中。
- 只要浏览器还开着,Cookie 数据就一直都在。
- 浏览器关闭,内存中的 Cookie 数据就会被释放。
- 持久化 Cookie(Persistent Cookie)
- 服务器端明确设置了 Cookie 的存在时间。
- 在浏览器端,Cookie 数据会被保存到硬盘上。
- Cookie 在硬盘上存在的时间根据服务器端限定的时间来管控,不受浏览器关闭的影响。
- 持久化 Cookie 到达了预设的时间会被释放。
代码示例
// 创建 Cookie
Cookie cookie = new Cookie("username", "JohnDoe");
cookie.setMaxAge(60 * 60 * 24); // 设置有效期为 1 天
response.addCookie(cookie); // 添加到响应中// 读取 Cookie
Cookie[] cookies = request.getCookies();
if (cookies != null) {for (Cookie c : cookies) {if ("username".equals(c.getName())) {String username = c.getValue();System.out.println("Username: " + username);}}
}
如果请求中没有 Cookie,request.getCookies()
会返回 null。
Session
Session 是服务器端的一种会话管理技术。服务器会为每个用户创建一个唯一的 Session 对象,并分配一个 Session ID。这个 Session ID 通常通过 Cookie 发送到客户端,后续请求中客户端会携带这个 Session ID,服务器通过它来识别用户。
Session 的工作流程
- 服务端在为客户端创建 Session 时,会同时将 Session ID,即
JSESSIONID
以 Cookie 的形式放入响应对象。 - 后端创建完 Session 后,客户端会收到一个特殊的 Cookie,叫做
JSESSIONID
。 - 客户端下一次请求时携带
JSESSIONID
,后端收到后,根据JSESSIONID
找到对应的 Session 对象。 - 通过该机制,服务端通过 Session 就可以存储一些专门针对某个客户端的信息了。
- Session 是域对象。
Session 的特点
- 存储在服务器端,安全性较高。
- 可以存储较大的数据(如用户信息、购物车内容等)。
- 依赖于 Cookie 或 URL 重写来传递 Session ID。
- 默认情况下,Session 在用户关闭浏览器后失效(也可以通过配置设置过期时间)。
代码示例
// 获取 Session
HttpSession session = request.getSession();// 存储数据到 Session
session.setAttribute("username", "JohnDoe");// 从 Session 中获取数据
String username = (String) session.getAttribute("username");
System.out.println("Username: " + username);// 使 Session 失效(用户注销时调用)
session.invalidate();
getSession()
方法的处理逻辑
Session 的时效性
是指 Session 在服务器端的有效时间。Session 是服务器用来跟踪用户状态的一种机制,它的生命周期由服务器控制。Session 的时效性直接影响用户的状态管理,例如用户登录状态、购物车内容等。
- Session 的生命周期
Session 的生命周期从创建开始,到失效结束。具体分为以下几个阶段:
- 创建:当用户第一次访问服务器时,服务器会为该用户创建一个唯一的 Session,并分配一个 Session ID。
- 使用:在用户与服务器的交互过程中,Session 可以存储和读取用户相关的数据。
- 失效:Session 会在以下情况下失效:
- 用户长时间未活动(超过指定的超时时间)。
- 服务器主动销毁 Session(如调用
session.invalidate()
)。 - 用户关闭浏览器(仅对未设置持久化的情况有效)。
- Session 的默认时效性
在大多数 Web 服务器(如 Tomcat)中,Session 的默认超时时间为 30 分钟。这意味着如果用户在 30 分钟内没有与服务器进行任何交互,Session 将自动失效。
查看和修改默认超时时间
在 web.xml
文件中,可以通过 <session-config>
标签设置 Session 的超时时间(单位为分钟):
<session-config><session-timeout>30</session-timeout> <!-- 设置超时时间为 30 分钟 -->
</session-config>
- Session 时效性的控制方法
在 Java 中,可以通过以下方式控制 Session 的时效性:
(1) 设置全局超时时间
在 web.xml
中配置 <session-timeout>
,单位为分钟:
<session-config><session-timeout>60</session-timeout> <!-- 设置超时时间为 60 分钟 -->
</session-config>
(2) 在代码中动态设置超时时间
通过 HttpSession
的 setMaxInactiveInterval()
方法设置 Session 的超时时间(单位为秒):
HttpSession session = request.getSession();
session.setMaxInactiveInterval(60 * 60); // 设置超时时间为 1 小时
(3) 立即销毁 Session
通过 invalidate()
方法可以立即销毁 Session:
HttpSession session = request.getSession();
session.invalidate(); // 销毁 Session
(4) 设置 Session 永久有效
通过将超时时间设置为一个非常大的值,可以让 Session 几乎永久有效:
HttpSession session = request.getSession();
session.setMaxInactiveInterval(Integer.MAX_VALUE); // 设置 Session 永久有效
- Session 失效的触发条件
Session 会在以下情况下失效:
-
超时:用户在规定时间内未与服务器交互。
-
主动销毁:调用
session.invalidate()
方法。 -
服务器重启或崩溃:Session 数据丢失(除非使用持久化存储,如 Redis)。
-
浏览器关闭:如果 Session ID 是通过 Cookie 传递的,浏览器关闭后 Cookie 失效,Session 无法被访问(但服务器端的 Session 仍然存在,直到超时)。
-
Session 时效性的注意事项
- 性能影响:过多的 Session 会占用服务器内存,因此需要合理设置超时时间。
- 分布式环境:在分布式系统中,Session 需要共享(如使用 Redis 存储 Session),以确保不同服务器可以访问同一 Session。
- 安全性:Session 失效后,用户的登录状态和其他敏感信息会被清除,因此合理设置超时时间可以增强安全性。
- Session 时效性的应用场景
- 用户登录状态管理:
- 用户登录后,Session 存储用户信息,超时后自动注销。
- 购物车功能:
- 用户将商品加入购物车后,Session 存储购物车数据,超时后清空购物车。
- 临时数据存储:
- 在多步骤操作(如注册流程)中,每一步的数据可以存储在 Session 中,超时后清除。
- Session 时效性与 Cookie 的关系
Session 的时效性通常依赖于 Cookie 来传递 Session ID。如果浏览器关闭,会话 Cookie 会失效,但服务器端的 Session 仍然存在,直到超时。如果需要持久化 Session,可以将 Session ID 存储在持久 Cookie 中。
Cookie 和 Session 的区别
特性 | Cookie | Session |
---|---|---|
存储位置 | 客户端(浏览器) | 服务器端 |
安全性 | 较低(容易被篡改或窃取) | 较高(数据存储在服务器) |
存储大小 | 较小(通常不超过 4KB) | 较大(取决于服务器配置) |
生命周期 | 可以设置过期时间 | 默认随浏览器关闭失效,可配置 |
性能影响 | 每次请求都会携带,增加网络开销 | 仅在服务器端存储,性能较好 |
会话管理的应用场景
- 用户登录状态管理
- 用户登录后,服务器生成一个 Session 并存储用户信息,同时将 Session ID 通过 Cookie 发送给客户端。
- 后续请求中,客户端携带 Session ID,服务器通过它识别用户并验证登录状态。
- 购物车功能
- 用户将商品加入购物车后,可以将购物车信息存储在 Session 中,确保用户在不同页面间浏览时购物车内容保持一致。
- 用户偏好设置
- 使用 Cookie 存储用户的主题偏好、语言设置等,方便下次访问时自动加载。
会话管理的注意事项
- 安全性
- Cookie 容易被窃取或篡改,因此敏感信息(如密码)不应存储在 Cookie 中。
- 对于 Session,应确保 Session ID 的安全性,防止 Session 劫持。
- 性能
- 如果 Session 中存储了大量数据,可能会影响服务器性能,因此需要合理设计 Session 的使用。
- 分布式环境
- 在分布式系统中,Session 需要共享(如使用 Redis 存储 Session),以确保不同服务器可以访问同一 Session。
三大域对象
什么是域对象
域对象(Scope Object)是一种可以在特定范围内存储和共享数据的对象。它们提供了一种机制,允许在不同的 JavaWeb 组件(如 Servlet、JSP 页面等)之间传递和共享数据,根据数据的共享范围不同,大体可以分为三类:应用域(ServletContext)、会话域(HttpSession)和请求域(HttpSessionRequest)。
ServletContext(应用域)
ServletContext
是 JavaWeb 中作用范围最大的域对象,它代表整个 Web 应用。一个 Web 应用只有一个 ServletContext
对象,所有的 Servlet 和 JSP 页面都可以共享其中的数据。
特点
- 生命周期:
- 创建:Web 应用启动时创建。
- 销毁:Web 应用关闭或服务器停止时销毁。
- 作用范围:整个 Web 应用(所有用户共享)。
- 线程安全性:非线程安全,需要手动同步。
使用场景
- 存储全局配置信息(如数据库连接信息)。
- 共享应用级别的数据(如网站访问次数)。
HttpSession(会话域)
HttpSession
是用于跟踪用户会话的域对象。每个用户都有一个独立的 HttpSession
对象,用于存储用户相关的数据。
特点
- 生命周期:
- 创建:用户第一次访问服务器时创建。
- 销毁:用户长时间未活动(超时)、调用
invalidate()
方法、服务器重启时销毁。
- 作用范围:一次用户会话(同一用户的多次请求共享)。
- 线程安全性:非线程安全,需要手动同步。
使用场景
- 存储用户登录状态。
- 存储购物车信息。
- 存储用户偏好设置。
HttpServletRequest(请求域)
HttpServletRequest
是用于在一次请求中共享数据的域对象。它的生命周期最短,仅在一次请求范围内有效。
特点
- 生命周期:
- 创建:每次请求时创建。
- 销毁:请求结束时销毁。
- 作用范围:一次请求(请求转发时共享)。
- 线程安全性:线程安全(每个请求独立)。
使用场景
- 在请求转发(Forward)时传递数据。
- 存储临时数据(如表单数据)。
所有域合在一起
域对象常用方法
API | 功能 |
---|---|
void setAttribute(String name,String value) | 向域对象中添加/修改数据 |
Object getAttribute(String name); | 从域对象中获取数据 |
removeAttribute(String name); | 移除域对象中的数据 |
代码示例
ServletA
向三大域中放入数据:
@WebServlet("/servletA")
public class ServletA extends HttpServlet {@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 向请求域中放入数据req.setAttribute("request","request-message");//req.getRequestDispatcher("servletB").forward(req,resp);// 向会话域中放入数据HttpSession session = req.getSession();session.setAttribute("session","session-message");// 向应用域中放入数据ServletContext application = getServletContext();application.setAttribute("application","application-message");}
}
ServletB
从三大域中取出数据:
@WebServlet("/servletB")
public class ServletB extends HttpServlet {@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 从请求域中获取数据String reqMessage =(String)req.getAttribute("request");System.out.println(reqMessage);// 从会话域中获取数据HttpSession session = req.getSession();String sessionMessage =(String)session.getAttribute("session");System.out.println(sessionMessage);// 从应用域中获取数据ServletContext application = getServletContext();String applicationMessage =(String)application.getAttribute("application");System.out.println(applicationMessage);}
}
三大域对象的对比
特性 | ServletContext(应用域) | HttpSession(会话域) | HttpServletRequest(请求域) |
---|---|---|---|
生命周期 | 应用启动到关闭 | 用户会话期间 | 一次请求期间 |
作用范围 | 整个 Web 应用(全局共享) | 一次用户会话(用户共享) | 一次请求(请求共享) |
线程安全性 | 非线程安全 | 非线程安全 | 线程安全 |
适用场景 | 全局配置、共享数据 | 用户状态、购物车 | 请求转发、临时数据 |
如何选择合适的域对象
- ServletContext:适合存储全局共享的数据,所有用户都可以访问。
- HttpSession:适合存储用户级别的数据,如登录状态、购物车等。
- HttpServletRequest:适合存储临时数据,如表单数据或请求转发时的数据。
注意事项
- 性能:
ServletContext
存储的数据会一直存在,占用内存,因此不要存储过多数据。HttpSession
存储的数据在用户会话期间有效,可能会占用较多内存,尤其是在用户量大的情况下。
- 线程安全:
ServletContext
和HttpSession
是非线程安全的,多线程环境下需要手动同步。HttpServletRequest
是线程安全的,每个请求独立。
- 分布式环境:
- 在分布式系统中,
HttpSession
需要共享(如使用 Redis 存储),以确保不同服务器可以访问同一 Session。
- 在分布式系统中,
总结
三大域对象是 JavaWeb 开发中非常重要的数据共享机制,合理使用它们可以提高代码的可维护性和性能。以下是它们的核心特点:
- ServletContext:全局共享,生命周期最长。
- HttpSession:用户会话共享,生命周期中等。
- HttpServletRequest:请求共享,生命周期最短。