Cookie
已经能完成一次会话多次请求之间的数据共享,之前我们还提到过Session
也可以实现,那么:
- 什么是
Session
? Session
如何来使用?Session
是如何实现的?Session
的使用注意事项有哪些?
1. Session
的基本使用
1.1 概念
Session
:服务端会话跟踪技术:将数据保存到服务端。
Session
是存储在服务端而Cookie
是存储在客户端- 存储在客户端的数据容易被窃取和截获,存在很多不安全的因素
- 存储在服务端的数据相比于客户端来说就更安全
1.2 Session的工作流程
- 在服务端的
AServlet
获取一个Session
对象,把数据存入其中 - 在服务端的
BServlet
获取到相同的Session
对象,从中取出数据 - 就可以实现一次会话中多次请求之间的数据共享了
- 现在最大的问题是如何保证
AServlet
和BServlet
使用的是同一个Session
对象?
1.3 Session
的基本使用
在JavaEE
中提供了HttpSession
接口,来实现一次会话的多次请求之间数据共享功能。
具体的使用步骤为:
- 获取
Session
对象,使用的是request
对象
HttpSession session = request.getSession();
-
Session
对象提供的功能:-
存储数据到
session
域中void setAttribute(String name, Object o)
-
根据
key
,获取值Object getAttribute(String name)
-
根据
key
,删除该键值对void removeAttribute(String name)
-
介绍完Session
相关的API
后,接下来通过一个案例来完成对Session
的使用,具体实现步骤为:
需求:在一个
Servlet
中往Session
中存入数据,在另一个Servlet
中获取Session
中存入的数据1.创建名
为SessionDemo1
的Servlet
类2.创建名为
SessionDemo2
的Servlet
类3.在
SessionDemo1
的方法中:获取Session
对象、存储数据4.在
SessionDemo2
的方法中:获取Session
对象、获取数据5.启动测试
(1)创建名为SessionDemo1
的Servlet
类
package com.dcxuexi.web.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 java.io.IOException;
/***
* @Title SessionDemo1
* @Description TOTD
* @Auter DongChuang
* @Date 2023/1/11 21:30
* @Version 1.0.0
*/
@WebServlet("/demo1")
public class SessionDemo1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
}
(2)创建名为SessionDemo2
的Servlet
类
package com.dcxuexi.web.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 java.io.IOException;
/***
* @Title SessionDemo2
* @Description TOTD
* @Auter DongChuang
* @Date 2023/1/11 21:31
* @Version 1.0.0
*/
@WebServlet("/demo2")
public class SessionDemo2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
}
(3)SessionDemo1
:获取Session
对象、存储数据
package com.dcxuexi.web.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;
/***
* @Title SessionDemo1
* @Description TOTD
* @Auter DongChuang
* @Date 2023/1/11 21:30
* @Version 1.0.0
*/
@WebServlet("/demo1")
public class SessionDemo1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//存储到Session中
//1. 获取Session对象
HttpSession session = req.getSession();
//2. 存储数据
session.setAttribute("username","zs");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
}
(4)SessionDemo2
:获取Session
对象、获取数据
package com.dcxuexi.web.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;
/***
* @Title SessionDemo2
* @Description TOTD
* @Auter DongChuang
* @Date 2023/1/11 21:31
* @Version 1.0.0
*/
@WebServlet("/demo2")
public class SessionDemo2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取数据,从session中
//1. 获取Session对象
HttpSession session = req.getSession();
//2. 获取数据
Object username = session.getAttribute("username");
System.out.println("username = " + username);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
}
(5)启动测试,
- 先访问
http://localhost:8080/cookie-demo/demo1
,将数据存入Session
- 在访问
http://localhost:8080/cookie-demo/demo2
,从Session
中获取数据 - 查看控制台
通过案例的效果,能看到Session
是能够在一次会话中两次请求之间共享数据。
小结
至此Session
的基本使用就已经完成了,重点:
-
Session
的获取HttpSession session = request.getSession();
-
Session
常用方法的使用void setAttribute(String name, Object o) Object getAttribute(String name)
注意:
Session
中可以存储的是一个Object
类型的数据,也就是说Session
中可以存储任意数据类型。
介绍完Session
的基本使用之后,那么Session
的底层到底是如何实现一次会话两次请求之间的数据共享呢?
2. Session
的原理分析
Session
是基于Cookie
实现的
这句话其实不太能详细的说明Session
的底层实现,接下来,咱们一步步来分析下Session
的具体实现原理:
(1)前提条件
Session
要想实现一次会话多次请求之间的数据共享,就必须要保证多次请求获取Session
的对象是同一个。
那么它们是一个对象么?要验证这个结论也很简单,只需要在上面案例中的两个Servlet
中分别打印下Session
对象
SessionDemo1
package com.dcxuexi.web.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;
/***
* @Title SessionDemo1
* @Description TOTD
* @Auter DongChuang
* @Date 2023/1/11 21:30
* @Version 1.0.0
*/
@WebServlet("/demo1")
public class SessionDemo1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//存储到Session中
//1. 获取Session对象
HttpSession session = req.getSession();
System.out.println("SessionDemo1 session = " + session);
//2. 存储数据
session.setAttribute("username","zs");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
}
SessionDemo2
package com.dcxuexi.web.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;
/***
* @Title SessionDemo2
* @Description TOTD
* @Auter DongChuang
* @Date 2023/1/11 21:31
* @Version 1.0.0
*/
@WebServlet("/demo2")
public class SessionDemo2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取数据,从session中
//1. 获取Session对象
HttpSession session = req.getSession();
System.out.println("SessionDemo2 session = " + session);
//2. 获取数据
Object username = session.getAttribute("username");
System.out.println("username = " + username);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
}
启动测试,分别访问
http://localhost:8080/cookie-demo/demo1
http://localhost:8080/cookie-demo/demo2
通过打印可以得到如下结论:
- 两个
Servlet
类中获取的Session
对象是同一个 - 把
demo1
和demo2
请求刷新多次,控制台最终打印的结果都是同一个
那么问题又来了,如果新开一个浏览器,访问demo1
或者demo2
,打印在控制台的Session
还是同一个对象么?
注意:在一台电脑上演示的时候,如果是相同的浏览器必须要把浏览器全部关掉重新打开,才算新开的一个浏览器。
当然也可以使用不同的浏览器进行测试,就不需要把之前的浏览器全部关闭。
测试的结果:如果是不同浏览器或者重新打开浏览器后,打印的Session
就不一样了。
所以Session
实现的也是一次会话中的多次请求之间的数据共享。
那么最主要的问题就来了,Session
是如何保证在一次会话中获取的Session
对象是同一个呢?
(1)demo1
在第一次获取session
对象的时候,session
对象会有一个唯一的标识,假如是id:10
(2)demo1
在session
中存入其他数据并处理完成所有业务后,需要通过Tomcat
服务器响应结果给浏览器
(3)Tomcat
服务器发现业务处理中使用了session
对象,就会把session
的唯一标识id:10
当做一个cookie
,添加Set-Cookie:JESSIONID=10
到响应头中,并响应给浏览器
(4)浏览器接收到响应结果后,会把响应头中的coookie
数据存储到浏览器的内存中
(5)浏览器在同一会话中访问demo2
的时候,会把cookie
中的数据按照cookie: JESSIONID=10
的格式添加到请求头中并发送给服务器Tomcat
(6)demo2
获取到请求后,从请求头中就读取cookie
中的JSESSIONID
值为10,然后就会到服务器内存中寻找id:10
的session
对象,如果找到了,就直接返回该对象,如果没有则新创建一个session
对象
(7)关闭打开浏览器后,因为浏览器的cookie
已被销毁,所以就没有JESSIONID
的数据,服务端获取到的session
就是一个全新的session
对象
至此,Session
是基于Cookie
来实现的这就话,我们就解释完了,接下来通过实例来演示下:
(1)使用chrome
浏览器访问http://localhost:8080/cookie-demo/demo1
,打开开发者模式(F12
或Ctrl+Shift+I
),查看 响应头(Response Headers
) 数据:
(2)使用chrome
浏览器再次访问http://localhost:8080/cookie-demo/demo2
,查看 请求头(Request Headers
) 数据:
小结
介绍完Session
的原理,我们只需要记住
Session
是基于Cookie
来实现的
3. Session
的使用细节
3.1 Session
钝化与活化
首先需要大家思考的问题是:
- 服务器重启后,
Session
中的数据是否还在?
要想回答这个问题,我们可以先看下下面这幅图,
(1)服务器端AServlet
和BServlet
共用的session
对象应该是存储在服务器的内存中
(2)服务器重新启动后,内存中的数据应该是已经被释放,对象也应该都销毁了
所以session
数据应该也已经不存在了。但是如果session
不存在会引发什么问题呢?
举个例子说明下,
(1)用户把需要购买的商品添加到购物车,因为要实现同一个会话多次请求数据共享,所以假设把数据存入Session
对象中
(2)用户正要付钱的时候接到一个电话,付钱的动作就搁浅了
(3)正在用户打电话的时候,购物网站因为某些原因需要重启
(4)重启后session
数据被销毁,购物车中的商品信息也就会随之而消失
(5)用户想再次发起支付,就会出为问题
所以说对于session
的数据,我们应该做到就算服务器重启了,也应该能把数据保存下来才对。
分析了这么多,那么Tomcat
服务器在重启的时候,session
数据到底会不会保存以及是如何保存的,我们可以通过实际案例来演示下:
注意:这里所说的关闭和启动应该要确保是正常的关闭和启动。
那如何才是正常关闭Tomcat
服务器呢?
需要使用命令行的方式来启动和停止Tomcat
服务器:
启动 :进入到项目pom.xml
所在目录,执行tomcat7:run
停止 :在启动的命令行界面,输入ctrl+c
有了上述两个正常启动和关闭的方式后,接下来的测试流程是:
(1)先启动Tomcat
服务器
(2)访问http://localhost:8080/cookie-demo/demo1
将数据存入session
中
(3)正确停止Tomcat
服务器
(4)再次重新启动Tomcat
服务器
(5)访问http://localhost:8080/cookie-demo/demo2
查看是否能获取到session
中的数据
经过测试,会发现只要服务器是正常关闭和启动,session
中的数据是可以被保存下来的。
那么Tomcat
服务器到底是如何做到的呢?
具体的原因就是:Session
的钝化和活化:
-
钝化:在服务器正常关闭后,
Tomcat
会自动将Session
数据写入硬盘的文件中- 钝化的数据路径为:
项目目录\target\tomcat\work\Tomcat\localhost\项目名称\SESSIONS.ser
- 钝化的数据路径为:
-
活化:再次启动服务器后,从文件中加载数据到
Session
中- 数据加载到
Session
中后,路径中的SESSIONS.ser
文件会被删除掉
- 数据加载到
对于上述的整个过程,大家只需要了解下即可。因为所有的过程都是Tomcat
自己完成的,不需要我们参与。
小结
Session
的钝化和活化介绍完后,需要我们注意的是:
-
session
数据存储在服务端,服务器重启后,session
数据会被保存 -
浏览器被关闭启动后,重新建立的连接就已经是一个全新的会话,获取的
session
数据也是一个新的对象 -
session
的数据要想共享,浏览器不能关闭,所以session
数据不能长期保存数据 -
cookie
是存储在客户端,是可以长期保存
3.2 Session
销毁
session
的销毁会有两种方式:
-
默认情况下,无操作,30分钟自动销毁
-
对于这个失效时间,是可以通过配置进行修改的
-
在项目的
web.xml
中配置<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> <session-config> <session-timeout>100</session-timeout> </session-config> </web-app>
-
如果没有配置,默认是30分钟,默认值是在
Tomcat
的web.xml
配置文件中写死的
-
-
-
调用
Session
对象的invalidate()
进行销毁-
在
SessionDemo2
类中添加session
销毁的方法package com.dcxuexi.web.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; /*** * @Title SessionDemo2 * @Description TOTD * @Auter DongChuang * @Date 2023/1/11 21:31 * @Version 1.0.0 */ @WebServlet("/demo2") public class SessionDemo2 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //获取数据,从session中 //1. 获取Session对象 HttpSession session = req.getSession(); System.out.println("SessionDemo2 session = " + session); // 销毁 session.invalidate(); //2. 获取数据 Object username = session.getAttribute("username"); System.out.println("username = " + username); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this.doGet(req, resp); } }
-
启动访问测试,先访问
demo1
将数据存入到session
,再次访问demo2
从session
中获取数据 -
该销毁方法一般会在用户退出的时候,需要将
session
销毁掉。
-
4. Cookie
和Session
小结
Cookie
和Session
都是来完成一次会话内多次请求间 数据共享 的。
所需两个对象放在一块,就需要思考:
Cookie
和Session
的区别是什么?
Cookie
和Session
的应用场景分别是什么?
- 区别:
- 存储位置:
Cookie
是将数据存储在客户端,Session
将数据存储在服务端 - 安全性:
Cookie
不安全,Session
安全 - 数据大小:
Cookie
最大3KB,Session
无大小限制 - 存储时间:
Cookie
可以通过setMaxAge()
长期存储,Session
默认30分钟 - 服务器性能:
Cookie
不占服务器资源,Session
占用服务器资源
- 存储位置:
- 应用场景:
- 购物车:使用
Cookie
来存储 - 以登录用户的名称展示:使用
Session
来存储 - 记住我功能:使用
Cookie
来存储 - 验证码:使用
session
来存储
- 购物车:使用
- 结论
Cookie
是用来保证用户在未登录情况下的身份识别Session
是用来保存用户登录后的数据
介绍完Cookie
和Session
以后,具体用哪个还是需要根据具体的业务进行具体分析。