26. 会话技术-Session的使用

2026-02-27 03:59:20

26. 会话技术-Session的使用一、 Session1.1 概述session是服务器端的会话技术

代码语言:javascript复制# session的作用

在一次会话的多次请求之间共享数据,将数据保存到服务器端

# HttpSession是一个域对象

HttpSession是一个接口

域对象可以看成是map(存储多个键值对), cookie是一个entry(只能存一个键值对)

1. 域对象的方法

a. 存储数据

void setAttribute(String name,Object value)

b. 获取数据

Object getAttribute(String name)

c. 删除数据

void removeAttribute(String name)

2. 生命周期: 一次会话的多次请求之间

pageContext(JSP) < request < session < servletContext

从api上来说, 小域对象可以获取大域对象

1.2 工作原理Session基于Cookie技术实现下面来使用一个上医院看病的示例,来简单理解 Session

1591321065800

方法介绍代码语言:javascript复制1. 获取session对象:

HttpSession session = request.getSession()

1). 通过请求对象创建一个会话对象,如果当前用户会话不存在,创建会话。

2). 如果会话已经存在,这个方法返回已经存在的会话对象。

2. 获取session的id

String sessionId = session.getId();

3. 使当前session失效

ression.invalidate();

“记得把项目中的index.jsp干掉(影响到 JSESSIONID的cookie)

编写示例代码index.html: 编写一个页面,模拟两个请求第一次请求至服务端:第一次去医院,生成一个session,并且保存信息第二次请求至服务端:再次去医院,查看之前保存的session信息。查询session id 是否与 上一次保存的 session ID 一致image-20210217165529664

代码语言:javascript复制

Title

第一次访问, 创建Session

第二次访问, 查看Session信息

Session01Servlet:创建Sessionimage-20210217165651306

代码语言:javascript复制package com.lijw;

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;

/**

* @author Aron.li

* @date 2021/2/17 16:56

*/

@WebServlet("/Session01Servlet")

public class Session01Servlet extends HttpServlet {

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

doGet(request, response);

}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

//1. (挂号) 获取session对象

HttpSession session = request.getSession();

//2. (看病) 存数据

session.setAttribute("sick","有点虚");

//3. tomcat自动实现,将sessionId通过cookie返回给浏览器

// String id = session.getId();

// Cookie cookie = new Cookie("JSESSIONID", id);

// response.addCookie(cookie);

}

}

Session02Servlet:读取Session信息image-20210217165806174

代码语言:javascript复制package com.lijw;

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;

/**

* @author Aron.li

* @date 2021/2/17 16:57

*/

@WebServlet("/Session02Servlet")

public class Session02Servlet extends HttpServlet {

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

doGet(request, response);

}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

//1. (挂号) 获取session对象

HttpSession session = request.getSession();

Object sick = session.getAttribute("sick");

System.out.println("value:" + sick + ",id:" + session.getId());

}

}

测试在 index.html 执行第一次请求,创建 sessionimage-20210217165909891

image-20210217170010048

在 index.html 执行第二次信息,查看 session 的 IDimage-20210217170111939

image-20210217170137124

可以通过获取的 session ID 信息,我们可以知道 session 是同一个的。

1.3 Session细节代码语言:javascript复制# 找不到当前会话中的session原因分析 (通俗版)

问题: 什么时候找不到班长原来的病历本?

1. 班长的原因

0). 只要班长把病例本编号弄丢了 (id弄丢了)

1). 用户清除cookie(清除浏览记录)

2). 用户关闭浏览器, 保存id的cookie默认会话级别,自动销毁了

-> 通过持久化cookie,达到session持久化 (下一个)

2. 医院的原因

0). 医院把病历本弄丢了

1). 手动销毁session

session.invalidate();

2). 自动销毁session

默认30分钟内不访问,自动销毁 (可以改)

a. tomcat/conf/web.xml 默认设置

(对发布在此tomcat上所有项目生效)

b. 在当前项目中修改 web.xml (会覆盖tomcat默认设置)

30

3). 服务器非正常关闭

突然断电, 数据来不及保存

正常关闭: session数据会会从内存保存硬盘上

-> session 钝化和活化

代码语言:javascript复制# 找不到当前会话中的session原因分析(专业版)

1. 浏览器方面的原因

0). 核心: 保存JSESSIONID的cookie被销毁

1). 因为cookie存活时间默认为会话,所以用户关闭浏览器就会销毁(用户无意识)

-> session持久化

2). 用户清除浏览记录(包含cookie)

2. 服务器方面的原因

0). 核心: session对象是存在服务器的内存,被销毁

1). session手动销毁:session.invalidate();

备注: session对象立即销毁

2). 过期销毁:session默认存活时间--30min

备注: (该用户连续30分钟不访问,服务器会自动销毁session)

文件配置: web.xml (tomcat中的默认设置)

1. tomcat/config/web.xml 中有session-config配置 (全局有效)

2. 我们可以在项目中web.xml覆盖其配置 (只对当前项目有效)

30

3). 非正常关闭tomcat(比如突然断电)

备注: 如果正常关闭tomcat,tomcat在停止之前会钝化session,下次启动时活化

1.4 session的持久化代码语言:javascript复制#浏览器关闭后,session的持久化方案

1. 问题: 从以上的分析我们得知, 浏览器关闭之后,就找不到原来的session了

2. 原因:

1. 浏览器关闭,服务器中的session是在的

2. 但是前端的JESSIONID这个cookie消失了

3. 浏览器提交请求没有这个id,服务器自然就找不到之前的session了

3. 解决: 浏览器关闭,session依然找到的

1. 在Servlet中手动创建name=JESSIONID的cookie;

2. 这个cookie存储session的id,设置持久化级别 setMaxAge(秒);

3. 将JESSIONID的cookie响应给浏览器;

代码语言:javascript复制@WebServlet("/Session01Servlet")

public class Session01Servlet extends HttpServlet {

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

this.doPost(request, response);

}

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

//1. (挂号) 获取session对象

HttpSession session = request.getSession();

//2. (看病) 存数据

session.setAttribute("sick","有点虚");

//3. tomcat自动实现,将sessionId通过cookie返回给浏览器

// String id = session.getId();

// Cookie cookie = new Cookie("JSESSIONID", id);

// response.addCookie(cookie);

//4. 我们手动实现(设置持久级别),覆盖tomcat的自动实现

String id = session.getId();

Cookie cookie = new Cookie("JSESSIONID", id);

cookie.setMaxAge(60*30); //30分钟

response.addCookie(cookie);

}

}

1.5 session的钝化和活化代码语言:javascript复制# 之前提到, 当服务器正常关闭,重启后,还可以再获取session(跟之前的一样)

这是因为tomcat已实现以下二个功能

1. 钝化(序列化: ObjectOutputStream) 保存

当服务器正常关闭时,session中的数据,会序列化到硬盘 (持久化)

序列化的目的: 将内存中对象或数据结构 保存 到硬盘 (编码: 看得懂 -> 看不懂)

内存: 临时性存储设备, 断电了数据就消失

硬盘: 持久性存储设备, 断电了数据依然在

2. 活化(反序列化: ObjectInputStream) 读取

当服务器开启后,从磁盘文件中,将数据反序列化到内存中

反序列化的目的: 将硬盘上的数据读取到内存,形成对象或数据结构 (解码: 看不懂 -> 看得懂)

备注: 钝化和活化的本质是序列化技术, 所以保存的存储数据类型需要实现serializable接口

我们使用的idea工具有坑:

代码语言:javascript复制1. 我们正常关闭tomcat,tomcat确实将session钝化到磁盘(下图的位置中的sessions.ser)

2. 坑: 但是在idea重启tomcat时,会默认删除之前保存的sessions.ser文件,造成tomcat没有活化数据

3. 解决: 设置idea重启时,不清除session会话(下图)

“支持钝化

下面我们来演示一下 idea 工具的坑 到底是什么 坑!

1.5.1 首先查看 Idea 中操作 tomcat 的克隆空间(临时空间)image-20210217221007671

根据这个路径,我们可以打开我们电脑的文件夹,如下:

image-20210217221059089

1.5.2 停止 tomcat ,查看钝化的效果,生成 session.ser 文件,将 session 信息保存在硬盘中image-20210217221217785

停止 tomcat 后,那么将会将 session 保存如下:

image-20210217221540189

这个 SESSIONS.ser 就是 tomcat 钝化后的文件,提供后续启动 tomcat 的时候读取 session 数据,进行活化。

1.5.3 Idea默认启动的时候会删除之前钝化保存的文件,导致 tomcat 再次读取 session 数据 活化失败image-20210217221824738

1.5.4 我们可以设置idea重启时,不清除session数据image-20210217222001702

1.6 URL重写(了解)代码语言:javascript复制# URL重写是为了解决cookie禁用问题

1. 问题: 浏览器是默认启用cookie,但是用户也可以禁用浏览器的Cookie(浏览器自带功能: 不允许浏览器保存cookie), 由于Session基于Cookie技术实现,所以一旦禁用了之后,Session功能就会出现问题

2. 解决: url重写技术

response.encodeURL(path)

会在url,拼接JSESSIONID

3. 备注: 开发中,一般我们是不关注禁用cookie的用户,若用户禁用了cookie,会给很多功能的实现带来很大的麻烦

1.6.1 首先禁止浏览器使用 cookieimage-20210217232943160

image-20210217233029543

此时点击 第二次访问,查看 Session 信息,由于没有 Session ID,导致没有 Session 信息,如下:

image-20210217233322018

1.6.2 修改第一次访问的请求 Servletindex.html

image-20210217233653491

UrlOverrideServlet

image-20210217234101372

代码语言:javascript复制package com.lijw;

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;

/**

* @author Aron.li

* @date 2021/2/17 23:37

*/

@WebServlet("/UrlOverrideServlet")

public class UrlOverrideServlet extends HttpServlet {

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

doGet(request, response);

}

/*

* 正常session 有个前提:

* 1. 浏览器需要携带 Jsessionid 过来

* 2. 默认情况下: Jsessionid是用cookie保存的

* 3. 用户在浏览器中设置: 禁用cookie -> 浏览器不再保存cookie

*

* 解决:

* jsessionid 作为参数放在url后面

*

* url = http://localhost:8081/Session02Servlet

*

* 重写url

* http://localhost:8081/Session02Servlet;jsessionid = ?

*

* 参数分割符 ? , 还有分号

* url?name=value -> value = request.getParameter(name)

* url;name=value 不能上面的api获取, tomcat可以获取就行了

* */

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

// 创建session

HttpSession session = request.getSession();

session.setAttribute("sick", "很虚");

String id = session.getId();

System.out.println(id);

//重写URL,携带sessionId参数

String url = "Session02Servlet";

String newUrl = response.encodeURL(url); //重写url,拼接jsessionid

System.out.println(newUrl); // Session02Servlet;jsessionid = ?

//将重写后的URL,作为超链接显示在浏览器上

response.setContentType("text/html;charset=utf-8");

response.getWriter().print("第二次去医院" );

}

}

1.6.3 再次测试禁用cookie的情况下,获取session数据image-20210217234224356

image-20210217234305695

image-20210217234318230

image-20210217234336678

在后台成功读取到了 session 数据。

1.7 Session特点代码语言:javascript复制# session是服务器端的会话技术

作用: 在一次会话的多次请求之间共享数据

从浏览器第一次向服务器发起请求建立会话, 直到其中一方断开为止会话结束

1. session存储数据在服务器

2. session存储任意类型的数据(Object)

3. session存储大小和数量没有限制(在服务器内存)

4. session存储相对安全

cookie和session的对比

1591287669163

cookie和session的选择

代码语言:javascript复制1. cookie将数据保存在浏览器端,数据相对不安全.

建议敏感的或大量的数据不要放在cookie中,而且数据大小是有限制的

成本低,对服务器要求不高

2. session将数据保存在服务器端内存,数据相对安全.

数据的大小要比cookie中数据灵活很多

成本较高,对服务器压力较大

二、 3大域对象总结request < session < ServletContext

2.1 域对象方法代码语言:javascript复制# 域对象方法都一致

1. 设置数据

void setAttribute(String name, Object o)

2. 获取数据

Object getAttribute(String name)

3. 删除数据

void removeAttribute(String name)

# 小域对象可以获取大域对象

# 不同域对象: 生命周期不一样

2.2 生命周期2.2.1 ServletContext域对象代码语言:javascript复制* 何时创建

服务器正常启动,项目加载时,创建

* 何时销毁

服务器关闭或项目卸载时,销毁

* 作用范围

整个web项目(共享数据)

2.2.2 HttpSession域对象代码语言:javascript复制* 何时创建

用户第一次调用request.getSession()方法时,创建【不太标准..】

用户访问携带的jsessionid与服务器里的session不匹配时,就会创建的

* 何时销毁

1. 服务器非正常关闭

2. 未活跃状态30分钟

3. 手动销毁

* 作用范围

一次会话中,多次请求间(共享数据)

# 会话的定义: 双方建立连接,连接期间的多次请求响应,直到一方断开连接为止

(B/S) 从浏览器第一次访问这个服务器,期间多次请求响应,直到浏览器关闭为止 -> 狭义的一次会话

cookie和session默认都是会话级别,都可以设置持久级别

2.2.3 HttpServletRequest域对象代码语言:javascript复制* 何时创建

服务器接收到请求时,创建

* 何时销毁

服务器做出响应后,销毁

* 作用范围

一次请求中,多次请求转发间(共享数据)

2.3 小结能用小的不用大的:request(一次请求)

“因为生命周期长的域对象销毁时间比较晚,占用服务器内存时间太长

”常用的场景:用户登录状态验证码购物车request:一次请求中(请求转发共享)session:存放当前会话的私有数据servletContext:若需要所有的servlet都能访问到,才使用这个域对象.

“一般情况下,web阶段很少使用这个域对象,在框架spring的学习中会涉及到

”三、 用户登录-验证码案例3.1 用户登录(验证码)需求

用户访问带有验证码的登录页面,输入用户名,密码以及验证码实现登录功能。

3.1.1 需求分析1591781837951

3.1.2 代码实现1.编写生成验证码的Servletimage-20210218232805983

代码语言:javascript复制package com.lijw;

import javax.imageio.ImageIO;

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.awt.*;

import java.awt.image.BufferedImage;

import java.io.IOException;

import java.util.Random;

/**

* @author Aron.li

* @date 2021/2/18 23:24

*/

@WebServlet("/CheckcodeServlet")

public class CheckcodeServlet extends HttpServlet {

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

doGet(request, response);

}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

// 创建画布

int width = 120;

int height = 40;

BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);

// 获得画笔

Graphics g = bufferedImage.getGraphics();

// 填充背景颜色

g.setColor(Color.white);

g.fillRect(0, 0, width, height);

// 绘制边框

g.setColor(Color.red);

g.drawRect(0, 0, width - 1, height - 1);

// 生成随机字符

// 准备数据

String data = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890";

// 准备随机对象

Random r = new Random();

// 声明一个变量 保存验证码

String code = "";

// 书写4个随机字符

for (int i = 0; i < 4; i++) {

// 设置字体

g.setFont(new Font("宋体", Font.BOLD, 28));

// 设置随机颜色

g.setColor(new Color(r.nextInt(255), r.nextInt(255), r.nextInt(255)));

String str = data.charAt(r.nextInt(data.length())) + "";

g.drawString(str, 10 + i * 28, 30);

// 将新的字符 保存到验证码中

code = code + str;

}

// 绘制干扰线

for (int i = 0; i < 6; i++) {

// 设置随机颜色

g.setColor(new Color(r.nextInt(255), r.nextInt(255), r.nextInt(255)));

g.drawLine(r.nextInt(width), r.nextInt(height), r.nextInt(width), r.nextInt(height));

}

// 将验证码 打印到控制台

System.out.println(code);

// TODO: 将验证码放到session中

request.getSession().setAttribute("code_session", code);

// 将画布显示在浏览器中

ImageIO.write(bufferedImage, "jpg", response.getOutputStream());

}

}

在网页测试请求,看看验证码的效果如下:

image-20210218232906641

2.编写用户登录的页面 login.htmlimage-20210218233852836

代码语言:javascript复制

Title

登录页面




在浏览器访问页面效果如下:

image-20210218233938755

3.编写处理登录业务的 LoginServletimage-20210218234843686

代码语言:javascript复制package com.lijw;

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;

/**

* @author Aron.li

* @date 2021/2/18 23:40

*/

@WebServlet("/LoginServlet")

public class LoginServlet extends HttpServlet {

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

doGet(request, response);

}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

//解决请求参数的中文乱码

request.setCharacterEncoding("UTF-8");

//解决响应中文乱码

response.setContentType("text/html;charset=utf-8");

//1. 接收验证码 code,判断验证码是否正确

//1.1 从session中获取图片的验证码

String code_session = (String)request.getSession().getAttribute("code_session");

//1.2 获取请求的验证码参数

String code = request.getParameter("code");

//1.3 判断验证码是否正确

if (!code.equalsIgnoreCase(code_session)) {

// 如果验证码不正确,则什么都不处理,直接返回

response.setContentType("text/html;charset=utf-8");

response.getWriter().print("验证码错误,请重新输入");

return;

}

//1. 获取请求参数

String username = request.getParameter("username");

String password = request.getParameter("password");

//2. 业务处理

if("jack".equalsIgnoreCase(username) && "123".equalsIgnoreCase(password)){

//登录成功

response.getWriter().write("登录成功");

}else{

//登录失败

response.getWriter().write("登录失败");

}

}

}

4.测试用户登录登录成功的情况image-20210218234941346

image-20210218234955269

登录失败的情况image-20210218235031275