1、会话

会话指的是一个客户端(浏览器)与Web服务器之间连续发生一系列请求和响应的过程。当用户打开一个浏览器,访问了一系列网页后,再关闭浏览器的这个过程就是一次会话。

注:

  • 当用户打开浏览器,访问web服务器的资源时,会话建立;直到有一方断开连接,会话结束。
  • 一次会话中可以包含多次请求和响应。

2、会话跟踪的两种技术

由于HTTP协议是无状态协议,服务器仅从网络连接上无法获取客户端(浏览器)信息。一旦数据交换完毕,客户端与服务器端的连接就会关闭,再次交换数据需要建立新的连接,此时,服务器就不知道是否还与之前的客户端(浏览器)会话了。这就意味着服务器无法从连接上跟踪会话

为了解决上述问题,在Servlet技术中为我们提供了两个用于保存会话数据的对象,分别是CookieSession

2.1、Cookie(客户端会话跟踪技术)

2.1.1、什么是Cookie

Cookie是一种客户端会话跟踪技术。即:将数据保存到客户端,以后每次请求都携带Cookie数据进行访问。

  • Cookie是一种记录客户端状态的机制。
  • Cookie是在服务器端创建的,然后由服务器端发送给客户端,客户端以键值对的形式存储Cookie,并标注Cookie的来源。客户端再次访问服务器端时,客户端存储的Cookie会保存在请求协议中,服务器端可以获取上次存储的缓存文件内容。
  • 服务器通过请求(request.getCookies())获取客户端提交的所有Cookie,并以Cookie[]数组形式返回。
  • 服务器通过响应(response.addCookie(Cookie cookie)向客户端设置(发送)Cookie
  • Java中把Cookie封装成了javax.servlet.http.Cookie类。
  • 每个Cookie都是一个Cookie类的对象。
  • 服务器通过操作Cookie类对象对客户端Cookie进行操作。

2.1.2、Cookie的工作原理(重点)

Cookie的工作原理是:

  • 当客户端(浏览器)请求服务器时,如果服务器需要记住该客户端(浏览器)的状态,那么服务器就通过响应response)向客户端(浏览器)颁发一个Cookie
  • 然后,浏览器把Cookie数据保存在客户端本地。
  • 当以后浏览器再访问该网站时,浏览器将会把请求的内容与Cookie一起打包,通过请求request)的方式提交给服务器。
  • 然后,服务器再通过检查Cookie的方式来辨认当前会话的用户(浏览器)是否是之前会话的用户(浏览器)。

2.1.3、Cookie的常见应用场景

  • 记录浏览器上次访问时间(将浏览器访问时间存储在Cookie中)
  • 网站访客信息统计(其中包含有多少人访问过,多少人是新用户等)
  • 用户自动登录功能(第一次登录时,将用户名和密码存储在Cookie中)

2.1.4、Cookie的特点

  • 一个Cookie(键值对)只能保存一个信息。
  • Cookie只能存储文本,每个Cookie不能超过4KB
  • 一个Web站点(域名)可以给浏览器发送多个Cookie,每个站点(域名)最多在客户端(浏览器)中保存20个Cookie
  • 一个浏览器最多可以保存约300个Cookie
  • 不同浏览器之间不能共享Cookie缓存文件。
  • 每个Cookie都有有效期(默认未设置有效期,浏览器关闭时自动清除)。
  • Cookie使用明文(未加密)传递,安全性低。
  • 浏览器可以禁用Cookie

2.1.5、如何使用Cookie(示例)

接下来,我们将以记录浏览器上次访问时间为例,详细介绍Cookie的使用方法。具体实现步骤如下:

(1)使用Maven模板创建一个MavenWeb项目(具体步骤参考《Maven详解》),项目名称为javaWeb-cookie-session

创建MavenWeb项目

(2)填写项目名称和Maven项目GAV。

GAV

(3)配置该MavenWeb项目的安装目录、配置文件和本地仓库。点击Finish按钮完成项目创建,等待Maven依赖包导入完毕。

配置Maven信息

(4)同样地,为了避免以后出现兼容性问题,建议我们先将该项目中的web.xml文件替换成“web-app_4_0”版本(具体步骤参考《Servlet详解》)。

(5)同样地,在该项目的main文件夹下新建一个java文件夹和一个resource文件夹,并将它们分别标记为“源码根目录”和“资源根目录”。

(6)同样地,配置Tomcat服务器,deploy(发布)javaWeb-cookie-session:war包,并为该项目添加虚拟路径映射:/cs(具体步骤参考《Tomcat详解》)。

(7)在项目的pom.xml文件中添加项目依赖,并重新加载Maven项目。

pom.xml文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet.jsp/javax.servlet.jsp-api -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
<scope>provided</scope>
</dependency>
</dependencies>
重新加载Maven

(8)同样地,在该项目的java目录下新建一个com.atangbiji.servlet包,并在该包下新建一个CookieServlet1类,用于实现Cookie。同样地,让CookieServlet1类继承(extends)HttpServlet类,以实现Servlet接口。然后,在CookieServlet1类中重写doGetdoPost方法。

CookieServlet1.java文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class CookieServlet1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置请求和响应的编码集
resp.setContentType("text/html;charset=utf-8");
req.setCharacterEncoding("utf-8");

PrintWriter out = resp.getWriter();
//服务器通过请求获取客户端提交的所有Cookie
Cookie[] cookies = req.getCookies();//这里返回数组,说明Cookie可能存在多个
//判断Cookie是否存在
if (cookies != null){
//若存在,则遍历Cookie数组
out.print("你上一次访问的时间是:");
for (int i = 0; i < cookies.length; i++) {
Cookie cookie = cookies[i];
//若Cookie的名字是“lastLoginTime”,则获取并输出cookie的值(即上次的登录时间)。
String cookieName = cookie.getName();
if (cookieName.equals("lastLoginTime")){
String cookieValue = cookie.getValue();
long lastLoginTime = Long.parseLong(cookieValue);
Date date = new Date(lastLoginTime);
out.print(date.toLocaleString());
}
}
}else {
out.print("这是你第一次访问本站!");
}

//服务器通过响应向客户端设置Cookie(即记录当前登录的时间)
Cookie cookie = new Cookie("lastLoginTime", String.valueOf(System.currentTimeMillis()));
cookie.setMaxAge(24*60*60);//设置Cookie的有效期为一天(单位:秒)
resp.addCookie(cookie);

}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp); //因为get和post只是不同的Http请求方式,所以它们之间可以相互调用。
}
}

(9)同样地,在该项目的Web配置文件(web.xml)中注册(映射)我们自己编写的Servlet,并为它提供一个浏览器可以访问的路径。

web.xml文件:

1
2
3
4
5
6
7
8
<servlet>
<servlet-name>CookieServlet</servlet-name>
<servlet-class>com.atangbiji.servlet.CookieServlet1</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>CookieServlet</servlet-name>
<url-pattern>/c1</url-pattern>
</servlet-mapping>

(10)在IDEA中启动Tomcat。第一次在浏览器中输入http://localhost:8080/cs/c1,访问结果如下图所示:

第一次访问

在不关闭浏览器的情况下,再次输入http://localhost:8080/cs/c1,访问结果如下图所示:

第二次访问

由于我们设置了cookie:lastLoginTime有效期为一天,如果关闭浏览器,一天后或者手动删除浏览器缓存的cookie:lastLoginTime后重新访问http://localhost:8080/cs/c1,那么访问页面则会再次显示:“这是你第一次访问本站!”

注:

  • **若关闭浏览器,则本次会话结束。**与此同时,**过期的cookie**会被浏览器自动清除。

  • 我们可以通过F12查看cookie,如下图所示。

    查看cookie1

    查看cookie2

    查看cookie3

  • 谷歌浏览器的Cookie文件一般会存放本地的C:\Users\xxx\AppData\Local\Google\Chrome\User Data\Default目录下。

2.1.6、如何删除cookie

**(1)方式一:**不设置Cookie的有效期。浏览器关闭时cookie自动清除。

**(2)方式二:**设置Cookie的有效期为0。

1
2
3
Cookie cookie = new Cookie("lastLoginTime", String.valueOf(System.currentTimeMillis()));
cookie.setMaxAge(0);//将Cookie的有效期设置为0秒
resp.addCookie(cookie);//响应后,cookie立即删除

2.2、Session(服务端会话跟踪技术)(重点)

2.2.1、什么是Session

Session是一种服务端会话跟踪技术。即:将数据保存到服务端,以后每次请求都在Session中进行匹配。

  • Session是另一种记录客户端状态的机制。
  • 服务器会给每一个用户(浏览器)创建一个Session对象,用于记录客户端(浏览器)状态。
  • 为了标识用户(浏览器)身份,每个Session对象都有一个唯一标识SessionId
  • Java中把Session封装成了javax.servlet.http.HttpSession类。

2.2.2、Session的工作原理(重点)

  • 当客户端(浏览器)第一次请求服务器的时候,服务器会自动在服务端创建一个Session对象,并将SessionIdCookie的形式响应给客户端。

  • 当以后浏览器再访问该网站时,每次请求都会自动携带SessionId来访问服务器。

  • 然后,服务器便可以通过SessionId从对应的Session中查找该客户(浏览器)的信息了。

2.2.3、Session的常见应用场景

  • 验证登录信息
  • 长久保存购物车信息
  • 我们可以将整个网站中经常使用的数据保存在Session中。

2.2.4、Session的特点

  • Session(键值对)中既可以存储文本数据,也可以存储一个类的对象
  • Session 默认的生命周期是 30 分钟,可以自己设置。
  • Session可以放在文件、数据库或内存中。

2.2.5、如何使用Session(示例)

(1)在该项目的java目录下新建一个com.atangbiji.pojo包,用于存放简单的Java对象(Plain Ordinary Java Object,并在该包下新建一个Person类,用于管理用户信息

Person.java文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package com.atangbiji.pojo;

//用户信息实体类
public class Person {
private String name;
private int age;

public Person() {
}

public Person(String name, int age) {
this.name = name;
this.age = age;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}

(2)同样地,在该项目的java目录下新建一个com.atangbiji.servlet包,并在该包下新建一个SessionServlet1类,用于向Session中存储数据。同样地,让SessionServlet1类继承(extends)HttpServlet类,以实现Servlet接口。然后,在SessionServlet1类中重写doGetdoPost方法。

SessionServlet1.java文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class SessionServlet1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置请求和响应的编码集
resp.setContentType("text/html;charset=utf-8");
req.setCharacterEncoding("utf-8");

//获得Session
HttpSession session = req.getSession();
//向Session中存储数据
session.setAttribute("websiteName","阿汤笔迹");//存储文本
session.setAttribute("user",new Person("阿汤",18));//存储对象
//获取Session的Id
String sessionId = session.getId();
//判断Session是不是新创建的
if (session.isNew()){
resp.getWriter().write("Session创建成功!SessionID为:" + sessionId);
}else {
resp.getWriter().write("服务器上已经存在该Session!SessionID为:" + sessionId);
}
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp); //因为get和post只是不同的Http请求方式,所以它们之间可以相互调用。
}
}

(3)同样地,在com.atangbiji.servlet包下新建一个SessionServlet2类,用于获取Session中存储的数据。同样地,让SessionServlet2类继承(extends)HttpServlet类,以实现Servlet接口。然后,在SessionServlet2类中重写doGetdoPost方法。

SessionServlet2.java文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class SessionServlet2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置请求和响应的编码集
resp.setContentType("text/html;charset=utf-8");
req.setCharacterEncoding("utf-8");

//获得Session
HttpSession session = req.getSession();
//获取Session中存储的数据
String websiteName = (String) session.getAttribute("websiteName");//获取文本
Person user = (Person)session.getAttribute("user");//获取对象

//在服务器后台输出获取到的Session值
System.out.println("=====================================");
System.out.println("成功获取SessionServlet1向Session中存储的文本数据,网站名称:" + websiteName);
System.out.println("成功获取SessionServlet1向Session中存储的对象数据,用户信息:" + user.toString());
System.out.println("=====================================");
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp); //因为get和post只是不同的Http请求方式,所以它们之间可以相互调用。
}
}

(4)同样地,在该项目的Web配置文件(web.xml)中注册(映射)我们自己编写的Servlet,并为它提供一个浏览器可以访问的路径。

web.xml文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<servlet>
<servlet-name>SessionServlet1</servlet-name>
<servlet-class>com.atangbiji.servlet.SessionServlet1</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>SessionServlet1</servlet-name>
<url-pattern>/s1</url-pattern>
</servlet-mapping>

<servlet>
<servlet-name>SessionServlet2</servlet-name>
<servlet-class>com.atangbiji.servlet.SessionServlet2</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>SessionServlet2</servlet-name>
<url-pattern>/s2</url-pattern>
</servlet-mapping>

(5)在IDEA中启动Tomcat。先重启浏览器,然后第一次在浏览器中输入http://localhost:8080/cs/s1,访问结果如下图所示:

session访问结果1

在不关闭浏览器的情况下,再次输入http://localhost:8080/cs/s1,访问结果如下图所示:

session访问结果2

在不关闭浏览器的情况下,再次输入http://localhost:8080/cs/s2,服务器后台输出结果如下图所示:

服务器后台输出Session

如果重启浏览器后,再重新访问http://localhost:8080/cs/s1,那么访问结果如下图所示:

session访问结果3

注:

  • 若关闭浏览器,则本次会话结束。若重启浏览器,则开启新的会话。

  • 当客户端(浏览器)第一次请求服务器的时候,服务器会自动在服务端创建一个Session对象,并将SessionIdCookie的形式响应给客户端,如下图所示。

    SessionId传给客户端

  • 一次会话过程中,只要浏览器已经完成了第一请求,那么以后浏览器再访问该网站时,每次请求都会**自动携带SessionId**来访问服务器。

    请求携带SessionId

    携带SessionId请求

2.2.6、如何注销Session

**(1)方式一:**在服务端调用invalidate()接口注销。

1
2
3
4
HttpSession session = req.getSession();//获得Session
session.removeAttribute("websiteName");
session.removeAttribute("user");
session.invalidate();//注销Session

**(2)方式二:**在web.xml文件中注销。

1
2
3
4
5
<!-- 设置Session默认的失效时间 -->
<session-config>
<!-- 15分钟后Session自动失效,默认单位为分钟 -->
<session-timeout>15</session-timeout>
</session-config>

注:

  • Session会将数据在服务器上保存一段时间。当用户增多时,可能会占用服务器较多内存,为了减轻服务器压力,我们应及时注销不再使用的Session
  • Session注销后,本次会话就结束了。
  • 注销Session相当于手动关闭了浏览器。
  • 以上两种注销方式可以同时使用。

3、Cookie与Session的主要区别

对比项 Cookie Session
数据存放位置 客户端 服务端
安全性 不安全 安全
对服务器性能的影响 较小 当访问量较大时,会降低服务器性能
存储数据类型 文本 文本和对象
使用数量是否有限制 有限制 无限制(建议定期清理)
存储数据大小 小于4KB 相对较大

(本讲完,系列博文持续更新中…… )

阿汤笔迹微信公众平台

关注**“阿汤笔迹”** 微信公众号,获取更多学习笔记。
原文地址:http://www.atangbiji.com/2022/10/07/CookieAndSession
博主最新文章在个人博客 http://www.atangbiji.com/ 发布。