会话: 用户打开浏览器,访问web服务器的资源,会话建立,直到有一方断开连接,会话结束。在一次会话中可以包含多次请求和响应。
用实际场景来理解下会话,比如在我们访问京东的时候,当打开浏览器进入京东首页后,浏览器和京东的服务器之间就建立了一次会话,后面的搜索商品,查看商品的详情,加入购物车等都是在这一次会话中完成。
思考:下图中总共建立了几个会话?
会话跟踪: 一种维护浏览器状态的方法,服务器需要识别多次请求是否来自于同一浏览器,以便在同一次会话的多次请求间共享数据。
我们需要思考:为什么现在浏览器和服务器不支持数据共享呢?
那么该如何实现会话跟踪技术呢? 具体的实现方式有:
这两个技术都可以实现会话跟踪,它们之间最大的区别:Cookie是存储在浏览器端而Session是存储在服务器端
Cookie:客户端会话技术,将数据保存到客户端,以后每次请求都携带Cookie数据进行访问。
Cookie的工作流程:
Cookie的基本使用
对于Cookie的操作主要分两大类,本别是发送Cookie和获取Cookie:
1. 发送Cookie
// 1. 创建Cookie对象,并设置数据
Cookie cookie = new Cookie("key","value");
// 2. 发送Cookie到客户端:使用==response==对象
response.addCookie(cookie);
2. 获取Cookie
// 1. 获取客户端携带的所有Cookie,使用==request==对象
Cookie[] cookies = request.getCookies();
// 遍历数组,获取每一个Cookie对象:forEach
// 使用Cookie对象方法获取数据
cookie.getName();
cookie.getValue();
测试案例完成Cookie的发送和接收,具体实现步骤如下:
编写一个名称为AServlet的Servlet类, 在AServlet中创建Cookie对象, 存入数据,发送给前端。
package cookie;
/**
* @Author Mr.Lu
* @Date 2022/7/29 9:48
* @ClassName AServlet
* @Version 1.0
*/
import javax.servlet.*;
import javax.servlet.annotation.*;
import javax.servlet.http.*;
import java.io.*;
import java.net.URLEncoder;
@WebServlet(urlPatterns = "/aServlet")
public class AServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 发送Cookie
// 1. 创建Cookie对象
Cookie cookie = new Cookie("username", "zs");
// 2. 发送Cookie
response.addCookie(cookie);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
启动测试,在浏览器查看Cookie对象中的值, 访问http://localhost:8080/cookie-demo/aServlet
编写一个名称为BServlet的Servlet类, BServlet中使用request对象获取Cookie数组,遍历数组,从数据中获取指定名称对应的值。
package cookie;
/**
* @Author Mr.Lu
* @Date 2022/7/29 10:00
* @ClassName BServlet
* @Version 1.0
*/
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
@WebServlet(urlPatterns = "/bServlet")
public class BServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1. 获取Cookie
Cookie[] cookies = request.getCookies();
// 2. 遍历所有Cookie
for(Cookie cookie : cookies){
String key = cookie.getName();
if("username".equals(key)){
String value = cookie.getValue();
System.out.println(key + " : " + value);
}
}
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
启动测试,在控制台打印出获取的值, 访问http://localhost:8080/cookie-demo/bServlet
小结
对于Cookie的实现原理是 基于HTTP协议的, 其中设计到HTTP协议中的两个请求头信息:
现在我们使用上述的测试案例进行验证:
http://localhost:8080/cookie-demo/aServlet
, 使用 Google Chrom 浏览器打开开发者工具(F12或Crtl+Shift+I)进行查看响应头中的数据http://localhost:8080/cookie-demo/bServlet
, 使用 Google Chrom 浏览器打开开发者工具(F12或Crtl+Shift+I)进行查看请求头中的数据// 设置Cookie存活时间
setMaxAge(int seconds)
参数值为:
我们在AServlet中去设置Cookie的存活时间。
package cookie;
/**
* @Author Mr.Lu
* @Date 2022/7/29 9:48
* @ClassName AServlet
* @Version 1.0
*/
import javax.servlet.*;
import javax.servlet.annotation.*;
import javax.servlet.http.*;
import java.io.*;
import java.net.URLEncoder;
@WebServlet(urlPatterns = "/aServlet")
public class AServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 发送Cookie
// 1. 创建Cookie对象
Cookie cookie = new Cookie("username", "zs");
// 设置存货时间, 1周7天, 以秒为单位
cookie.setMaxAge(7 * 24 * 60 * 60); // 易阅读,需程序计算
// cookie.setMaxAge(604800); //不易阅读(可以使用注解弥补),程序少进行一次计算
// 2. 发送Cookie
response.addCookie(cookie);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
修改完代码后,启动测试,访问http://localhost:8080/cookie-demo/aServlet
http://localhost:8080/cookie-demo/bServet
,能在控制台打印出username:zs
,说明Cookie没有随着浏览器关闭而被销毁.抛出问题:
username=zs
的值改成 username=张三
,把汉字张三
存入到Cookie中:package cookie; /**
* @Author Mr.Lu
* @Date 2022/7/29 9:48
* @ClassName AServlet
* @Version 1.0
*/
import javax.servlet.*;
import javax.servlet.annotation.*;
import javax.servlet.http.*;
import java.io.*;
import java.net.URLEncoder;
@WebServlet(urlPatterns = "/aServlet")
public class AServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 发送Cookie
// 1. 创建Cookie对象
Cookie cookie = new Cookie("username", "张三");
// 设置存货时间, 1周7天, 以秒为单位
cookie.setMaxAge(7 * 24 * 60 * 60); // 易阅读,需程序计算
// cookie.setMaxAge(604800); //不易阅读(可以使用注解弥补),程序少进行一次计算
// 2. 发送Cookie
response.addCookie(cookie);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
http://localhost:8080/cookie-demo/aServlet
会发现浏览器会提示错误信息: Control character in cookie value or attribute.
得出结论:
解决方式:URL编码, 想要仔细了解URL编码可以参考文章:https://blog.csdn.net/qq_43751200/article/details/125357013 中的 请求参数中文乱码问题模块 中的 GET请求解决方案。
下面是一个通过URL编码解决中文存储问题的实例:
在AServlet中对中文进行URL编码,采用URLEncoder.encode(),将编码后的值存入Cookie中:
package cookie; /**
* @Author Mr.Lu
* @Date 2022/7/29 9:48
* @ClassName AServlet
* @Version 1.0
*/
import javax.servlet.*;
import javax.servlet.annotation.*;
import javax.servlet.http.*;
import java.io.*;
import java.net.URLEncoder;
@WebServlet(urlPatterns = "/aServlet")
public class AServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 发送Cookie
// 1. 创建Cookie对象
String value = "张三";
value = URLEncoder.encode(value, "UTF-8"); // 对中文进行URL编码
System.out.println("存储数据value : " + value);
Cookie cookie = new Cookie("username", value);
// 设置存货时间, 1周7天, 以秒为单位
cookie.setMaxAge(7 * 24 * 60 * 60); // 易阅读,需程序计算
// 2. 发送Cookie
response.addCookie(cookie);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
在BServlet中获取Cookie中的值, 获取的值为URL编码后的值, 将获取的值在进行URL解码,采用URLDecoder.decode(),就可以获取到对应的中文值:
package cookie; /**
* @Author Mr.Lu
* @Date 2022/7/29 10:00
* @ClassName BServlet
* @Version 1.0
*/
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
import java.net.URLDecoder;
@WebServlet(urlPatterns = "/bServlet")
public class BServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1. 获取Cookie
Cookie[] cookies = request.getCookies();
// 2. 遍历所有Cookie
for(Cookie cookie : cookies){
String key = cookie.getName();
if("username".equals(key)){
String value = cookie.getValue(); // 获取的是URL编码后的值 %E5%BC%A0%E4%B8%89
value = URLDecoder.decode(value, "UTF-8"); // value解码后为 张三
System.out.println(key + " : " + value);
break;
}
}
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
http://localhost:8080/cookie-demo/aServlet
和 http://localhost:8080/cookie-demo/bServlet
查看控制台打印结果:小结
Cookie的使用细节中,讲了Cookie的存活时间
和存储中文
:
Session:服务端会话跟踪技术:将数据保存到服务端。
Session的工作流程
在JavaEE中提供了HttpSession接口,来实现一次会话的多次请求之间数据共享功能。
具体的使用步骤为:
// 获取Session对象,使用的是request对象
HttpSession session = request.getSession();
// Session对象提供的功能:
// 1. 存储数据到 session 域中
void setAttribute(String name, Object o)
// 2. 根据 key,获取值
Object getAttribute(String name)
// 3. 根据 key,删除该键值对
void removeAttribute(String name)
下面通过一个案例完成对Session的使用,具体实现步骤为:
创建名为SessionServlet1的Servlet类, 功能:获取Session对象、存储数据
package session; /**
* @Author Mr.Lu
* @Date 2022/7/30 12:25
* @ClassName SessionServlet1
* @Version 1.0
*/
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
@WebServlet(urlPatterns = "/demo1")
public class SessionServlet1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 存储到Session中
// 1. 获取Session对象
HttpSession session = request.getSession();
// 2. 存储数据
session.setAttribute("username", "zs");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
创建名为SessionServlet2的Servlet类,功能: 获取Session对象、获取数据
package session; /**
* @Author Mr.Lu
* @Date 2022/7/30 12:25
* @ClassName SessionServlet2
* @Version 1.0
*/
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
@WebServlet(urlPatterns = "/demo2")
public class SessionServlet2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 从Session中获取数据
// 1. 获取session对象
HttpSession session = request.getSession();
// 2. 获取数据
Object username = session.getAttribute("username");
System.out.println(username);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
启动测试,
http://localhost:8080/cookie-demo/demo1
, 将数据存入Sessionhttp://localhost:8080/cookie-demo/demo2
, 从Session中获取数据通过案例的效果,能看到Session是能够在一次会话中两次请求之间共享数据。
测试: 一次会话中获取的Session对象是同一个
SessionServlet1
package session; /**
* @Author Mr.Lu
* @Date 2022/7/30 12:25
* @ClassName SessionServlet1
* @Version 1.0
*/
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
@WebServlet(urlPatterns = "/demo1")
public class SessionServlet1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 存储到Session中
// 1. 获取Session对象
HttpSession session = request.getSession();
System.out.println(session);
// 2. 存储数据
session.setAttribute("username", "zs");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
SessionServlet2
package session; /**
* @Author Mr.Lu
* @Date 2022/7/30 12:25
* @ClassName SessionServlet2
* @Version 1.0
*/
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
@WebServlet(urlPatterns = "/demo2")
public class SessionServlet2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 从Session中获取数据
// 1. 获取session对象
HttpSession session = request.getSession();
System.out.println(session);
// 2. 获取数据
Object username = session.getAttribute("username");
// System.out.println(username);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
启动测试,分别访问http://localhost:8080/cookie-demo/demo1
和 http://localhost:8080/cookie-demo/demo2
:
提问: Session是如何保证在一次会话中获取的Session对象是同一个呢?
demo1在第一次获取session对象的时候,session对象会有一个唯一的标识,假如是id:10
demo1在session中存入其他数据并处理完成所有业务后,需要通过Tomcat服务器响应结果给浏览器
Tomcat服务器发现业务处理中使用了session对象,就会把session的唯一标识id:10
当做一个cookie,添加Set-Cookie:JESSIONID=10
到响应头中,并响应给浏览器
浏览器接收到响应结果后,会把响应头中的cookie数据存储到浏览器的内存中
浏览器在同一会话中访问demo2的时候,会把cookie中的数据按照cookie: JESSIONID=10
的格式添加到请求头中并发送给服务器Tomcat
demo2获取到请求后,从请求头中就读取cookie中的JSESSIONID值为10,然后就会到服务器内存中寻找id:10
的session对象,如果找到了,就直接返回该对象,如果没有则新创建一个session对象
关闭打开浏览器后,因为浏览器的cookie已被销毁,所以就没有JESSIONID的数据,服务端获取到的session就是一个全新的session对象
接下来通过实例进行演示:
(1) 使用Google chrome浏览器访问http://localhost:8080/cookie-demo/demo1
,打开开发者模式(F12或Ctrl+Shift+I),查看==响应头(Response Headers)数据:
(2) 使用Google chrome浏览器再次访问http://localhost:8080/cookie-demo/demo2
,查看请求头(Request Headers)==数据:
问题抛出:服务器正常重启后,Session中的数据是否还在?????
下面来进行测试:
启动:进入到项目pom.xml所在目录,执行tomcat7:run
停止:在启动的命令行界面,输入ctrl+c
有了上述两个正常启动和关闭的方式后,接下来的测试流程是:
(1) 先启动Tomcat服务器
(2) 访问http://localhost:8080/cookie-demo/demo1
将数据存入session中
(3) 正确停止Tomcat服务器, 会在这个目录下生成一个SESSIONS.ser文件
(4) 再次重新启动Tomcat服务器, 这个目录下的SESSIONS.ser文件自动消失了
(5) 访问http://localhost:8080/cookie-demo/demo2
查看是否能获取到session中的数据
经过测试,会发现只要服务器是正常关闭和启动,session中的数据是可以被保存下来的。
那么Tomcat服务器到底是如何做到的呢?
具体的原因就是: Session的钝化和活化:
项目目录\target\tomcat\work\Tomcat\localhost\项目名称\SESSIONS.ser
SESSIONS.ser
文件会被删除掉session的销毁会有两种方式:
<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_4_0.xsd"
version="4.0">
<session-config>
<session-timeout>100session-timeout>
session-config>
web-app>
调用Session对象的invalidate()进行销毁
package session;
/**
* @Author Mr.Lu
* @Date 2022/7/30 12:25
* @ClassName SessionServlet2
* @Version 1.0
*/
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
@WebServlet(urlPatterns = "/demo2")
public class SessionServlet2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 从Session中获取数据
// 1. 获取session对象
HttpSession session = request.getSession();
System.out.println(session);
// 销毁
session.invalidate();
// 2. 获取数据
Object username = session.getAttribute("username");
// System.out.println(username);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
启动访问测试,先访问demo1将数据存入到session,再次访问demo2从session中获取数据
该销毁方法一般会在用户退出的时候,需要将session销毁掉。
Cookie和Session的区别是什么?
Cookie和Session的应用场景分别是什么?
结论