当前位置:网站首页>会话技术之Coookie && Session详解
会话技术之Coookie && Session详解
2022-07-31 07:08:00 【碳烤小肥羊。。。】
Cookie && Session
会话跟踪技术的概述
会话: 用户打开浏览器,访问web服务器的资源,会话建立,直到有一方断开连接,会话结束。在一次会话中可以包含多次请求和响应。
- 从浏览器发出请求到服务端响应数据给前端之后,一次会话(在浏览器和服务器之间)就被建立了
- 会话被建立后,如果浏览器或服务端都没有被关闭,则会话就会持续建立着
- 浏览器和服务器就可以继续使用该会话进行请求发送和响应,上述的整个过程就被称之为会话。
用实际场景来理解下会话,比如在我们访问京东的时候,当打开浏览器进入京东首页后,浏览器和京东的服务器之间就建立了一次会话,后面的搜索商品,查看商品的详情,加入购物车等都是在这一次会话中完成。
思考:下图中总共建立了几个会话?
- 每个浏览器都会与服务端建立了一个会话,加起来总共是3个会话。
会话跟踪: 一种维护浏览器状态的方法,服务器需要识别多次请求是否来自于同一浏览器,以便在同一次会话的多次请求间共享数据。
- 服务器会收到多个请求,这多个请求可能来自多个浏览器,如上图中的6个请求来自3个浏览器
- 服务器需要用来识别请求是否来自同一个浏览器
- 服务器用来识别浏览器的过程,这个过程就是会话跟踪
- 服务器识别浏览器后就可以在同一个会话中多次请求之间来共享数据
我们需要思考:为什么现在浏览器和服务器不支持数据共享呢?
- 浏览器 和 服务器之间使用的是 HTTP 请求来进行数据传输。
- HTTP协议是无状态的,每次浏览器向服务器请求时,服务器都会将该请求视为新的请求。
- HTTP协议设计成无状态的目的是让每次请求之间相互独立,互不影响。
- 请求与请求之间独立后,就无法实现多次请求之间的数据共享。
那么该如何实现会话跟踪技术呢? 具体的实现方式有:
- 客户端会话跟踪技术:Cookie
- 服务端会话跟踪技术:Session
这两个技术都可以实现会话跟踪,它们之间最大的区别:Cookie是存储在浏览器端而Session是存储在服务器端
Cookie
Cookie的基本使用
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:
- 创建Cookie对象,并设置值:Cookie cookie = new Cookie(“key”,“value”);
- 发送Cookie到客户端使用的是Reponse对象:response.addCookie(cookie);
- 获取Cookie:
- 使用Request对象获取Cookie数组:Cookie[] cookies = request.getCookies();
- 遍历数组;
- 获取数组中每个Cookie对象的值:cookie.getName() 和 cookie.getValue();
Cookie的原理分析
对于Cookie的实现原理是 基于HTTP协议的, 其中设计到HTTP协议中的两个请求头信息:
- 响应头: set-cookie
- 请求头: cookie
现在我们使用上述的测试案例进行验证:
- 访问AServlet对应的地址
http://localhost:8080/cookie-demo/aServlet
, 使用 Google Chrom 浏览器打开开发者工具(F12或Crtl+Shift+I)进行查看响应头中的数据
- 访问BServlet对应的地址
http://localhost:8080/cookie-demo/bServlet
, 使用 Google Chrom 浏览器打开开发者工具(F12或Crtl+Shift+I)进行查看请求头中的数据
Cookie的使用细节
Cookie的存活时间
// 设置Cookie存活时间
setMaxAge(int seconds)
参数值为:
- 正数:将Cookie写入浏览器所在电脑的硬盘,持久化存储。到时间自动删除
- 负数:默认值,Cookie在当前浏览器内存中,当浏览器关闭,则Cookie被销毁
- 零:删除对应Cookie
我们在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
- 通过浏览器查看Cookie的内容,会发现Cookie的相关信息
- 访问一个AServlet后,把浏览器关闭重启后,再去访问
http://localhost:8080/cookie-demo/bServet
,能在控制台打印出username:zs
,说明Cookie没有随着浏览器关闭而被销毁.
Cookie存储中文
抛出问题:
- 我们把AServlet类中的
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.
得出结论:
- Cookie不能直接存储中文
解决方式: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的存活时间
和存储中文
:
- 存活时间,使用 setMaxAage() 进行设置
- 存储中文,使用 URL 编码和解码
Session
Session:服务端会话跟踪技术:将数据保存到服务端。
- Session是存储在服务端而Cookie是存储在客户端
- 存储在客户端的数据容易被窃取和截获,存在很多不安全的因素
- 存储在服务端的数据相比于客户端来说就更安全
Session的工作流程
- 在服务端的AServlet获取一个Session对象,把数据存入其中
- 在服务端的BServlet获取到相同的Session对象,从中取出数据
- 就可以实现一次会话中多次请求之间的数据共享了
- 问题提出 ?? 如何保证AServlet和BServlet使用的是同一个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
, 将数据存入Session - 在访问
http://localhost:8080/cookie-demo/demo2
, 从Session中获取数据 - 查看控制台
- 先访问
通过案例的效果,能看到Session是能够在一次会话中两次请求之间共享数据。
Session的原理分析
- Session是基于Cookie实现的
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
的格式添加到请求头中并发送给服务器Tomcatdemo2获取到请求后,从请求头中就读取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的使用细节
Session钝化与活化
问题抛出:服务器正常重启后,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的钝化和活化:
- 钝化:在服务器正常关闭后,Tomcat会自动将Session数据写入硬盘的文件中
- 钝化的数据路径为:
项目目录\target\tomcat\work\Tomcat\localhost\项目名称\SESSIONS.ser
- 钝化的数据路径为:
- 活化:再次启动服务器后,从文件中加载数据到Session中
- 数据加载到Session中后,路径中的
SESSIONS.ser
文件会被删除掉
- 数据加载到Session中后,路径中的
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_4_0.xsd" version="4.0">
<!-- 默认情况下30分钟进行销毁, 在这里可以对session进行设置 -->
<session-config>
<session-timeout>100</session-timeout>
</session-config>
</web-app>
调用Session对象的invalidate()进行销毁
- 在SessionServlet2类中添加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();
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 都是来完成一次会话内多次请求间数据共享的。
Cookie和Session的区别是什么?
- 区别:
- 存储位置:Cookie 是将数据存储在客户端,Session 将数据存储在服务端
- 安全性:Cookie不安全,Session安全
- 数据大小:Cookie最大3KB,Session无大小限制
- 存储时间:Cookie可以通过setMaxAge()长期存储,Session默认30分钟
- 服务器性能:Cookie不占服务器资源,Session占用服务器资源
Cookie和Session的应用场景分别是什么?
- 应用场景:
- 购物车:使用Cookie来存储
- 以登录用户的名称展示:使用Session来存储
- 记住我功能:使用Cookie来存储
- 验证码:使用session来存储
结论
- Cookie是用来保证用户在未登录情况下的身份识别
- Session是用来保存用户登录后的数据
边栏推荐
猜你喜欢
机器学习---线性回归、Logistic回归问题相关笔记及实现
CY7C68013A之LED闪烁
Environment_Variable_and_SetUID
Vscode:Project-tree插件
2022.07.20_Daily Question
正则表达式绕过
How to set the computer password?How to add "safety lock" to your computer
MySQL详解
Embedded system driver primary [2] - _ parameters and dependencies under the kernel module
Yu Mr Series 】 【 2022 July 022 - Go Go teaching course of container in the dictionary
随机推荐
【面试题】从输入URL到游览器渲染完成,经历了什么
Collation and sharing of related classic papers and datasets in the field of deep learning communication
Visual Studio新功能出炉:低优先级构建
2022.07.13_每日一题
Thread 类的基本用法——一网打尽
NK-RTU980烧写裸机程序
超级详细的mysql数据库安装指南
2022.07.29_Daily Question
03-SDRAM: Write operation (burst)
2022.07.14_每日一题
CY7C68013A之LED闪烁
NK - RTU980 burning bare-metal program
2022.07.22_每日一题
ros little turtle drawing
PCB抄板
【Go】Go 语言切片(Slice)
opencv、pil和from torchvision.transforms的Resize, Compose, ToTensor, Normalize等差别
Introduction and self-order of bcos
Client navicat installation tutorial
2022.07.18 _ a day