会话指的就是客户端和服务端之间进行的多次请求和响应
一、为什么需要会话管理
HTTP协议是无状态协议: 对于数据没有记忆能力。每次请求-响应都是独立的
无状态指的是客户端发送HTTP请求给服务端之后,服务端根据请求响应数据,响应完后,不会记录任何信息。
- 缺点: 多次请求间不能共享数据
- 优点: 速度快
请求之间无法共享数据会引发的问题:服务器无法判断这两次请求是同一个客户端发过来的,还是不同的客户端发过来的。
- 如:京东购物。加入购物车和去购物车结算是两次请求
- 由于HTTP协议的无状态特性,加入购物车请求响应结束后,并未记录加入购物车是何商品
- 发起去购物车结算的请求后,因为无法获取哪些商品加入了购物车,会导致此次请求无法正确展示数据
具体使用的时候,我们发现京东是可以正常展示数据的,原因是Java早已考虑到这个问题,并提出了使用会话技术(Cookie、Session)来解决这个问题。
二、会话管理实现的手段
Cookie和Session配合解决
- cookie是在客户端保留少量数据的技术,主要通过响应头向客户端响应一些客户端要保留的信息
- session是在服务端保留更多数据的技术,主要通过HttpSession对象保存一些和客户端相关的信息
- cookie和session配合记录请求状态
三、Cookie
1)概述
cookie是一种客户端会话技术,cookie由服务端产生,它是服务器存放在浏览器的一小份数据,浏览器以后每次访问该服务器的时候都会将这小份数据携带到服务器去。
- 服务端创建cookie,将cookie放入响应对象中,Tomcat容器将cookie转化为set-cookie响应头,响应给客户端
- 客户端在收到cookie的响应头时,在下次请求该服务的资源时,会以cookie请求头的形式携带之前收到的Cookie
- cookie是一种键值对格式的数据,从tomcat8.5开始可以保存中文,但是不推荐
- 由于cookie是存储于客户端的数据,比较容易暴露,一般不存储一些敏感或者影响安全的数据
2)原理图
3)应用场景举例
-
记录用户名
当我们在用户名的输入框中输入完用户名后,浏览器记录用户名,下一次再访问登录页面时,用户名自动填充到用户名的输入框.
-
保存电影播放进度
在网页上播放电影的时候,如果中途退出浏览器了,下载再打开浏览器播放同一部电影的时候,会自动跳转到上次退出时候的进度,因为在播放的时候会将播放进度保存到cookie中
4)使用
servletA向响应中增加Cookie
@WebServlet("/servletA")
public class ServletA extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 创建Cookie
Cookie cookie1 = new Cookie("c1","c1_message");
Cookie cookie2 = new Cookie("c2","c2_message");
// 将cookie放入响应对象
resp.addCookie(cookie1);
resp.addCookie(cookie2);
}
}
浏览器收到多少个Cookie,就会存储多少个键值对

浏览器存储后,那我怎么知道以后的每次请求都会携带这两个cookie呢?我们只需要请求任何资源,然后F12抓包查看请求头即可

servletB从请求中读取Cookie
@WebServlet("/servletB")
public class ServletB extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取请求中携带的 cookie
Cookie[] cookies = req.getCookies();
// 请求中的多个 cookie 会进入该数组,如果没有 cookie,cookies 数组为 null
if (null != cookies && cookies.length!= 0) {
for (Cookie cookie : cookies) {
System.out.println(cookie.getName()+":"+cookie.getValue());
}
}
}
}
5)Cookie的时效性
默认情况下Cookie的有效期是一次会话范围内,我们可以通过 cookie
的 setMaxAge()
方法让Cookie持久化保存到浏览器上
- 会话级Cookie
- 服务器端并没有明确指定Cookie的存在时间
- 在浏览器端,Cookie数据存在于内存中
- 只要浏览器还开着,Cookie数据就一直都在
- 浏览器关闭,内存中的Cookie数据就会被释放
- 持久化Cookie
- 服务器端明确设置了Cookie的存在时间
- 在浏览器端,Cookie数据会被保存到硬盘上
- Cookie在硬盘上存在的时间根据服务器端限定的时间来管控,不受浏览器关闭的影响
- 持久化Cookie到达了预设的时间会被释放
cookie.setMaxAge(int expiry)
参数单位是秒,表示cookie的持久化时间,如果设置参数为0,表示将浏览器中保存的该cookie删除
- servletA设置一个Cookie为持久化cookie
@WebServlet("/servletA")
public class ServletA extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 创建Cookie
Cookie cookie1 = new Cookie("c1","c1_message");
cookie1.setMaxAge(60);
Cookie cookie2 = new Cookie("c2","c2_message");
// 将cookie放入响应对象
resp.addCookie(cookie1);
resp.addCookie(cookie2);
}
}

- servletB接收Cookie,浏览器中间发生一次重启再请求servletB测试
@WebServlet("/servletB")
public class ServletB extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取请求中的cookie
Cookie[] cookies = req.getCookies();
//迭代cookies数组
if (null != cookies && cookies.length!= 0) {
for (Cookie cookie : cookies) {
System.out.println(cookie.getName()+":"+cookie.getValue());
}
}
}
}
过一段时间请求,发现只有c1在了

6)Cookie的提交路径
Cookie默认会往我们当前项目下面的任意一个资源进行提交。
访问互联网资源时不能每次都需要把所有Cookie带上。访问不同的资源时,可以携带不同的cookie,我们可以通过cookie的 setPath(String path)
对cookie的路径进行设置
- 从ServletA中获取cookie
public class ServletA extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 创建Cookie
Cookie cookie1 = new Cookie("c1","c1_message");
// 设置cookie的提交路径
cookie1.setPath("/web03_war_exploded/servletB"); // 当前Cookie只有在请求web03_war_exploded下的servletB的时候才进行提交,请求别的资源的时候就不提交。
Cookie cookie2 = new Cookie("c2","c2_message");
// 将cookie放入响应对象
resp.addCookie(cookie1);
resp.addCookie(cookie2);
}
}

- 向ServletB请求时携带携带了 c1

- 向其他资源请求时就不携带c1了

四、Session
PS:未来我们自己写Cookie的情况不是特别多,因为大部分我们用Cookie都是配合Session来做的,而Session的相关API其实就隐含了Cookie的相关操作。
1)HttpSession概述
Session的全称叫做HttpSession,HttpSession是一种保留更多信息在服务端的一种技术,服务器会为每一个客户端开辟一块内存空间,即session对象。客户端在发送请求时,都可以使用自己的session,这样服务端就可以通过session来记录某个客户端的状态了
- 客户端第一次发请求的时候没有携带任何的Cookie,服务端就会为当前客户端产生一个Session对象
- 服务端在为客户端创建session时,会同时将session对象的id,即JSESSIONID以cookie的形式放入响应对象
- 后端创建完session后,客户端会收到一个特殊的cookie,叫做JSESSIONID
- 客户端下一次请求时携带JSESSIONID,后端收到后,根据JSESSIONID找到对应的session对象
- 通过该机制,服务端 通过 session对象 就可以存储一些专门针对某个客户端的信息了
- session也是域对象(后续详细讲解)
因此如果后端要使用Session的话,前提是它一定要配合Cookie,没有Cookie的话,我们的Session使用就会出现问题。
2)原理图
3)应用场景
-
记录用户的登录状态
用户登录后,将用户的账号等敏感信息存入session
-
记录用户操作的历史
例如记录用户的访问痕迹,用户的购物车信息等临时性的信息
4)HttpSession的使用
用户提交form表单到ServletA,携带用户名,ServletA获取session 将用户名存到Session,用户再请求其他任意Servlet,获取之间存储的用户
- 定义表单页,提交用户名,提交后
<form action="servletA" method="post">
用户名:
<input type="text" name="username">
<input type="submit" value="提交">
</form>
- 定义ServletA,将用户名存入session
@WebServlet("/servletA")
public class ServletA extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException {
// 获取请求中 username 参数
String username = req.getParameter("username");
// 获取 session 对象
HttpSession session = req.getSession();
// 判断请求中有没有一个特殊的 cookie JSESSIONID 值 *** ***
// 1 有
// 根据 JSESSIONID 找到 session 对象
// 1 找到了
// 返回之前的 session
// 2 没找到
// 创建一个新的 session 返回, 并且向 response 对象中存放一个 JSESSIONID 的 cookie
// 2 没有
// 创建一个新的 session 返回, 并且向 response 对象中存放一个 JSESSIONID 的 cookie
System.out.println(session.getId());
// 判断它是不是新的,如果是新的,返回true,否则返回false
// 新的指的是它是不是新创建的,还是已经有了JSESSIONID
System.out.println(session.isNew());
// 将 username 存入 session
session.setAttribute("username", username);
// 客户端响应信息
resp.setContentType("text/html;charset=UTF-8");
resp.getWriter().write("成功");
}
}
- 响应中收到了一个JSESSIONID的cookie

- 定义其他Servlet,从session中读取用户名
@WebServlet("/servletB")
public class ServletB extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取session对象
HttpSession session = req.getSession();
// 获取Session的ID
String jSessionId = session.getId();
System.out.println(jSessionId);
// 判断session是不是新创建的session
boolean isNew = session.isNew();
System.out.println(isNew);
// 从session中取出数据
String username = (String)session.getAttribute("username");
System.out.println(username);
}
}
- 请求中携带了一个JSESSIONID的cookie

5)getSession方法的处理逻辑

6)HttpSession时效性
为什么要设置session的时效?
- 用户量很大之后,Session对象相应的也要创建很多。如果一味创建不释放,那么服务器端的内存迟早要被耗尽。
- 客户端关闭行为无法被服务端直接侦测,或者客户端较长时间不操作也经常出现,类似这些的情况,就需要对session的时限进行设置了
默认的session最大闲置时间(两次使用同一个session中的间隔时间) 在
tomcat/conf/web.xml
配置为30分钟
我们可以自己在当前项目的web.xml对最大闲置时间进行重新设定
Tomcat里的 web.xml
是所有项目公共的 web.xml
。正常情况下,每一个项目都有自己独立的 web.xml
,但是这两个文件中有一些配置是一样的,如果我们不处理这些一样的配置的话,web.xml
中就会有很多很多代码,我们一旦创建后,这个 web.xml
文件就会变得特别大。
此时Tomcat就会这样做:将我们自己项目的 web.xml
抽取出来,放到conf中,然后形成 web.xml
。意思就是说,如果我们自己项目中没写的配置,就会按照conf中的 web.xml
配置来运行,但如果在我们自己当前项目里又写了一遍,那就会使用自己当前项目里 web.xml
中的配置。
也可以通过HttpSession的API 对最大闲置时间进行设定
// 设置最大闲置时间
session.setMaxInactiveInterval(60);
也可以直接让session失效
// 直接让session失效
session.invalidate();