文章目录
- 会话管理
- 一、Cookie
- 1.1 Cookie的使用
- 1.2 Cookie的时效性
- 1.3 Cookie的提交路径
- 二、Session
- 2.1 HttpSession的使用
- 2.2 HttpSession时效性
- 三、三大域对象
- 3.1 域对象概述
- 3.2 域对象的使用
- 总结
会话管理
HTTP是无状态协议
- 无状态就是不保存状态,即无状态协议(stateless)
- HTTP协议自身不对请求和响应之间的通信状态进行保存
- 发送请求和接受响应不做持久化处理
无状态 每次都要重新输入
有状态 按照上次的直接显示 不用再输入一次
实现会话管理的方式:
Cookie和Session配合解决
cookie
是在客户端保留少量数据的技术
- 主要通过响应头向客户端响应一些客户端要保留的信息
session
是在服务端保留更多数据的技术,
- 主要通过
HttpSession对象
保存一些和客户端相关的信息- cookie和session配合记录请求状态
举例:
- 第一次输入登录信息 发送给服务器后 将信息存储到session中,返回响应给了一个cookie;
- 下一次登录时候,浏览器发送cookie 从服务器存储的session中拿到上一次的登录信息 来使用
- 也就是:在名为session的保险库里存放信息后,会给一个cookie是用于取出信息的凭证。(黑客帝国)
一、Cookie
cookie
是一种客户端会话技术,cookie
由服务端产生,服务器存放在浏览器的一小份数据,浏览器以后每次访问该服务器的时候都会将这小份数据携带到服务器去。
- 服务端创建
cookie
,将cookie
放入响应对象中,Tomcat
容器将cookie
转化为set-cookie
响应头,响应给客户端 - 客户端在收到
cookie
的响应头时,在下次请求该服务的资源时,会以cookie请求头
的形式携带之前收到的Cookie cookie
是一种键值对格式的数据,从tomcat8.5开始可以保存中文,但是不推荐- 由于
cookie
是存储于客户端的数据,比较容易暴露,一般不存储一些敏感或者影响安全的数据
原理图:
应用场景举例
- 记录用户名 :
第一次输入用户名,下一次用户名会自动填充- 保存视频播放进度 :
播放视频中途退出,下次进还在上次退出的地方继续播放。
因为播放的时候会将播放进度保存到cookie中。
1.1 Cookie的使用
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("keyA", "valueA");
Cookie cookie2 = new Cookie("keyB", "valueB");
//将cookie放入响应对象
resp.addCookie(cookie1);
resp.addCookie(cookie2);
}
}
servletB从请求中读取Cookie
@WebServlet("/servletB")
public class ServletB extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Cookie[] cookies = req.getCookies();
if (null != cookies && cookies.length != 0) {
for (Cookie cookie : cookies) {
System.out.println(cookie.getName() + "=" + cookie.getValue());
}
}
}
}
1.2 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("keyA", "valueA");
Cookie cookie2 = new Cookie("keyB", "valueB");
//servletA COOKIE 存放 60秒 后消失
cookie1.setMaxAge(60);
//将cookie放入响应对象
resp.addCookie(cookie1);
resp.addCookie(cookie2);
}
}
- servletB接收Cookie,浏览器中间发生一次重启再请求servletB测试
60秒后 servletA消失
1.3 Cookie的提交路径
- 访问互联网资源时,不能每次都把所有Cookie带上。
- 访问不同的资源时,可以携带不同的cookie,
- 我们可以通过cookie的
setPath(String path)
对cookie的路径进行设置
@WebServlet("/servletA")
public class ServletA extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//创建cookie
Cookie cookie1 = new Cookie("keyA", "valueA");
Cookie cookie2 = new Cookie("keyB", "valueB");
//设置cookie的提交路径
cookie1.setPath(req.getContextPath()+"/servletB");
// " /cookie_session "
System.out.println(req.getContextPath());
//将cookie放入响应对象
resp.addCookie(cookie1);
resp.addCookie(cookie2);
}
}
第一次访问 响应cookie:
访问servletB
访问servletC 只拿到 没有设置path 的"keyB" (设定了固定路径的拿不到)
二、Session
HttpSession
是一种保留更多信息在服务端的一种技术,
- 服务端会为每一个客户端开辟一块内存空间,即
session对象
.- 客户端在发送请求时,都可以使用自己的session.
- 这样服务端就可以通过session来记录某个客户端的状态了
- 服务端在为客户端创建
session
时,会同时将session对象
的id,即JSESSIONID
以cookie的形式放入响应对象 - 后端创建完session后,客户端会收到一个特殊的cookie,叫做JSESSIONID
- 客户端下一次请求时携带JSESSIONID,后端收到后,根据JSESSIONID找到对应的session对象
- 通过该机制,服务端通过session就可以存储一些专门针对某个客户端的信息了
- session也是域对象
原理图:
应用场景
- 记录用户的登录状态
用户登录后,将用户的账号等敏感信息存入session- 记录用户操作的历史
例如记录用户的访问痕迹,用户的购物车信息等临时性的信息
2.1 HttpSession的使用
用户提交form表单到ServletA,携带用户名,ServletA获取session 将用户名存到Session,用户再请求其他任意Servlet,获取之间存储的用户
- 表单
<form action="servletC" method="post">
用户名:
<input type="text" name="username">
<input type="submit" value="提交">
</form>
- 定义servletC,将用户名存入session
@WebServlet("/servletC")
public class ServletC extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter("username");
//获取请求中的session对象
HttpSession session = req.getSession();
//获取session的ID
String id = session.getId();
System.out.println(id);
//判断session是不是新创建的session
boolean isNew = session.isNew();
System.out.println(isNew);
//向session对象中存入数据
session.setAttribute("username",username);
}
}
- 响应中收到JSESSIONID的cookie
- 定义其他的servlet,从session中读取用户名
@WebServlet("/servletD")
public class ServletD extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取session对象
HttpSession session = req.getSession();
//获取session的ID
String id = session.getId();
//判断是否是新创建的session
boolean isNew = session.isNew();
System.out.println(isNew);
//从session中取出数据
String username = (String) session.getAttribute("username");
System.out.println(username);
}
}
- 请求中携带 JSESSIONID的cookie
- session 的ID
getSession
方法的处理逻辑
2.2 HttpSession时效性
为什么要设置session的时效
- 用户量很大之后,
Session对象
相应的也要创建很多。- 如果一味创建不释放,那么服务器端的内存迟早要被耗尽。
- 客户端关闭行为无法被服务端直接侦测,或者客户端较长时间不操作也经常出现,类似这些的情况,就需要对session的时限进行设置了
默认的session最大闲置时间(两次使用同一个session中的间隔时间) 在
tomcat/conf/web.xml
配置为30分钟
我们可以自己在当前项目的
web.xml
对最大闲置时间进行重新设定
- 也可以通过
HttpSession
的API 对最大闲置时间进行设定
// 设置最大闲置时间
session.setMaxInactiveInterval(60);
- 也可以直接让session失效
// 直接让session失效
session.invalidate();
三、三大域对象
3.1 域对象概述
域对象:
- 一些用于存储数据和传递数据的对象,
- 传递数据不同的范围,我们称之为不同的域,
- 不同的域对象代表不同的域,共享数据的范围也不同
- web项目中,熟练使用的域对象分别是
请求域
,会话域
,应用域
- 请求域对象是
HttpServletRequest
,传递数据的范围是一次请求之内及请求转发 - 会话域对象是
HttpSession
,传递数据的范围是一次会话之内,可以跨多个请求 - 应用域对象是
ServletContext
,传递数据的范围是本应用之内,可以跨多个会话
三大域对象的数据作用范围图解 :
- 请求域
客户端发送一次的请求,以及,请求转发
- 会话域 :
同一个客户端,跨多个请求 传递信息,
举例:servletA可以servletB、C、D也可以接受信息,只要拿到session对应的cookie
- 应用域
跨客户端 , 另一个客户端也能取到数据
- 所有域 合体
3.2 域对象的使用
域对象的API:
API | 功能 |
---|---|
void setAttribute(String name,String value) | 向域对象中添加/修改数据 |
Object getAttribute(String name); | 从域对象中获取数据 |
removeAttribute(String name); | 移除域对象中的数据 |
API测试:
@WebServlet("/servletE")
public class ServletE extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 创建 请求域
req.setAttribute("reqKeyE","reqValueE");
// 创建 会话域
HttpSession session = req.getSession();
System.out.println("E sessionID: "+session.getId());
session.setAttribute("sessionKeyE","sessionValueE");
// 创建 应用域
ServletContext application = this.getServletContext();
application.setAttribute("servletContextKeyE","servletContextValueE");
// 请求域 获取 一次请求
String reqKeyE = (String)req.getAttribute("reqKeyE");
System.out.println("ServletE REQ : "+reqKeyE);
// 请求域 之 请求转发
req.getRequestDispatcher("servletF").forward(req,resp);
}
}
@WebServlet("/servletF")
public class ServletF extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 尝试直接获取servletE的请求域 (不可能获取到)
String reqKeyE = (String)req.getAttribute("reqKeyE");
System.out.println("ServletF REQ: " + reqKeyE);
// 请求域 servletE开启请求转发 是可以获取 servletE的域中值
String reqKeyEForward = (String)req.getAttribute("reqKeyE");
System.out.println("ServletF Forward: " + reqKeyEForward);
//会话域 获取
HttpSession session = req.getSession();
String sessionKeyE = (String)session.getAttribute("sessionKeyE");
System.out.println("F get session id : "+session.getId());
System.out.println("F session By KeyE: "+ sessionKeyE);
// 应用域 获取
ServletContext application = this.getServletContext();
String servletContextKeyE = (String)application.getAttribute("servletContextKeyE");
System.out.println("ServletF 应用域获取: "+servletContextKeyE);
}
}
结果:
- 开启servletE 的请求转发 给servletF:
- 没开启请求转发:
总结:
- 请求域 : 域值传入与读取的范围 -> 当前servlet内 一次请求 以及 请求转发
- 会话域:同一客户端,两个或多个servlet 信息传递
- 应用域:不同客户端,开启共享域,都可以获取到存入的数据,也都能存入
总结
- 请求转发时,请求域可以传递数据
请求域内一般放本次请求业务有关的数据
,- 如:查询到的所有的部门信息
- 同一个会话内,不用请求转发,会话域可以传递数据
会话域内一般放本次会话的客户端有关的数据
,- 如:当前客户端登录的用户
- 同一个APP内,不同的客户端,应用域可以传递数据
应用域内一般放本程序应用有关的数据
- 如:Spring框架的IOC容器