Skip to content

会话管理

Cookie 是一种客户端会话技术,它由服务端产生,是服务端存储在客户端的一小份数据,浏览器以后每次访问该服务器的时候都会将其携带到请求头中。

cookie

提示

  1. 同一个请求的响应体中,可以存储多个 Cookie;
  2. 默认情况下,当请求了 Cookie 保存在浏览器端后,后续发起 任何请求 都会在请求头中携带 Cookie,通过 cookie.setPath() 方法可以定义只在请求哪个路径时,携带该 Cookie;
java
@WebServlet("/servletA")
public class ServletA extends HttpServlet {
  @Override
  protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    // 创建Cookie,需要使用键值对的形式创建
    Cookie cookie1 = new Cookie("username", "YiboWang");
    // 设置过期时间
    cookie1.setMaxAge(60 * 5); // 5分钟
    // 设置只在请求哪个路径时,携带该Cookie
    cookie1.setPath("/Demo05/servletB");

    Cookie cookie2 = new Cookie("password", "123456");

    // 添加Cookie到响应体中
    resp.addCookie(cookie1);
    resp.addCookie(cookie2);
  }
}

注意

当客户端没有携带 Cookie 时,req.getCookies() 的返回值是 null,需要处理空指针问题!

java
@WebServlet("/servletB")
public class ServletB extends HttpServlet {
  @Override
  protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    // 获取Cookie,返回值是一个数组
    Cookie[] cookies = req.getCookies();
    // 若客户端没有传递任何Cookie时,该返回值为null
    if (null != cookies) {
      for (Cookie cookie : cookies) {
        System.out.println(cookie.getName() + " : " + cookie.getValue());
      }
    }
  }
}

Session

Session 是一种保留更多信息在服务端的技术,服务器会为每一个客户端开辟一块内存空间,即 Session 对象。

客户端在发送请求时,都可以使用自己的 Session,这样服务端就可以通过 Session 来记录每个客户端的状态了。

session

设置Session

提示

每次创建 Session,都会生成唯一的 JSESSIONID 属性,它存储在客户端的 Cookie 中,下一次请求的时候就会携 JSESSIONID 的值,然后服务端根据 JSESSIONID 获取对应的 Session 信息。

java
@WebServlet("/servletC")
public class ServletC extends HttpServlet {
  @Override
  protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    // 获取客户端携带的username属性
    String username = req.getParameter("username");

    // 获取Session对象(查看JSESSIONID和是否为新的Session)
    HttpSession session = req.getSession();
    System.out.println("JSESSIONID = " + session.getId());
    System.out.println("IsNew = " + session.isNew());

    // 将username属性存储到Session对象
    session.setAttribute("username", username);

    // 向客户端返回设置成功的信息
    resp.setContentType("text/html;charset=UTF-8"); // 避免乱码
    resp.getWriter().write("设置Session成功!");
  }
}

获取Session

java
@WebServlet("/servletD")
public class ServletD extends HttpServlet {
  @Override
  protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    // 获取Session对象(查看JSESSIONID和是否为新的Session)
    HttpSession session = req.getSession();
    System.out.println("JSESSIONID = " + session.getId());
    System.out.println("IsNew = " + session.isNew());

    // 查看username属性
    Object username = session.getAttribute("username");
    System.out.println("username = " + username);
  }
}

Session时效性

默认情况下,Tomcat 的 web.xml 配置文件中,配置了 Session 的默认过期时间是30分钟。

xml
<session-config>
  <session-timeout>30</session-timeout>
</session-config>

如果想改变默认的30分钟,可以在我们当前项目的 WEB-INF/web.xml 中,重写上面的配置,修改过期时间。

xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
         version="6.0">
  <!--配置Session的过期时间-->
  <session-config>
    <session-timeout>60</session-timeout>
  </session-config>
</web-app>

如果想为某个 Session 单独设置其过期时间,可以使用如下方法(一般不常用):

java
// 单位:秒
session.setMaxInactiveInterval(120); // 120秒

Session失效

java
// 让 Session 直接失效
session.invalidate();

二者对比

特性对比:

特性CookieSession
存储位置客户端浏览器(硬盘/内存)服务端内存(或者Redis、数据库等)
安全性安全性较低相对安全,数据存储在服务端
存储容量每个域名最多4KB,单个浏览器最多300个依据服务器资源而定
生命周期默认浏览器关闭就消失,或设定 maxAge 持久化默认浏览器关闭,或Session过期后失效
数据访问客户端每次请求自动携带客户端携带SessionID,服务器通过ID查找数据

使用场景对比:

CookieSession
“记住我”功能需要较多的服务端交互的处理(如多步骤表单、用户认证)
存储不敏感的客户端信息(如主题、语言偏好)存储敏感信息(如用户登录信息、购物车、权限)
跨页面记录某些状态(如浏览记录)防止数据被篡改(因为保存在服务端)

三大域对象

在 web 项目中,有三大域对象需要熟练掌握:

  • 请求域(HttpServletRequest):传递数据的范围是当前请求内和请求转发;
  • 会话域(HttpSession):传递数据的范围是一次会话之内,可以跨多个请求(但不能跨浏览器);
  • 应用域(ServletContext):传递数据的范围是本应用内,只要程序不停止,可以任意地址访问(全局唯一);

请求域

请求域

java
@WebServlet("/servletA")
public class ServletA extends HttpServlet {
  @Override
  protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    req.setAttribute("request", "requestMsg");

    // 当前请求内可以获取请求域的内容
    String requestMsg = (String) req.getAttribute("request");
    System.out.println("ServletA => request = " + requestMsg); // requestMsg

    // 请求转发到 servletB 之后,servletB也能访问到
    RequestDispatcher dispatcher = req.getRequestDispatcher("servletB");
    dispatcher.forward(req, resp);
  }
}
java
@WebServlet("/servletB")
public class ServletB extends HttpServlet {
  @Override
  protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    // 获取 servletA 转发的请求域内容
    String requestMsg = (String) req.getAttribute("request");
    System.out.println("ServletB => request = " + requestMsg); // requestMsg
  }
}

会话域

会话域

java
@WebServlet("/servletA")
public class ServletA extends HttpServlet {
  @Override
  protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    // 会话域只要是同一个浏览器内,可以跨标签页使用,但不能跨浏览器使用
    HttpSession session = req.getSession();
    session.setAttribute("session", "sessionMsg");
  }
}
java
@WebServlet("/servletB")
public class ServletB extends HttpServlet {
  @Override
  protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    // 获取会话域内的数据
    HttpSession session = req.getSession();
    String sessionMsg = (String) session.getAttribute("session");
    System.out.println("ServletB => sessionMsg = " + sessionMsg); // sessionMsg
  }
}

应用域

应用域

java
@WebServlet("/servletA")
public class ServletA extends HttpServlet {
  @Override
  protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    // ServletContext全局只有一份,所以只要程序不停,任何请求都能访问
    ServletContext servletContext = super.getServletContext();
    servletContext.setAttribute("application", "applicationMsg");
  }
}
java
@WebServlet("/servletB")
public class ServletB extends HttpServlet {
  @Override
  protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    // 访问应用域内的数据
    ServletContext servletContext = super.getServletContext();
    String application = (String) servletContext.getAttribute("application");
    System.out.println("application = " + application);
  }
}

Released under the MIT License.