【Cookie和Session】

news2024/11/26 18:32:40

🎉🎉🎉点进来你就是我的人了
博主主页:🙈🙈🙈戳一戳,欢迎大佬指点!

欢迎志同道合的朋友一起加油喔🤺🤺🤺


目录

一、Cookie和Session(面试常考) 

1. Cookie

2 Session 

3. Cookie和Session是如何一起工作的? 

4. Cookie和Session的过期校验

5. Cookie和Session的关联和区别

关联:

区别:

注意:

二、核心方法

三、交互过程

1. 理解交互过程

 2. 解析交互过程

3. 案例登录淘宝(加深理解) ​编辑

四、模拟场景实现一个用户登录的案例

约定前后端的交互接口

1. 代码实现

使用 form表单来构造 post 请求:

LoginServlet 类 (用来构造 POST响应)

IndexServlet 类 (用来构造跳转页面)

2. 代码分析

详解 LoginServlet 类 第1步代码

详解 IndexServlet 类 第1步代码

3. 理解代码交互的过程

4. 展示抓包结果

 浏览器展示结果



一、Cookie和Session(面试常考) 

1. Cookie

Cookie是一种浏览器(客户端)提供的持久化存储数据的机制

  • Cookie从哪里来?是客户端发送请求自带的吗?

Cookie中的数据是来自于服务器的,服务器向浏览器发送 HTTP 响应时,它可以在响应头中包含一个或多个 Set-Cookie 头部来设置 Cookie,每一个 Set-Cookie 头部都包含了一个 Cookie 的名称、值以及其它一些属性(如域名、路径、过期时间等)。

  • Cookie 有什么用? Cookie要到哪里去?

Cookie 会在第一次请求的下次请求中,自动被添加到 HTTP请求中的请求头,发给服务器。服务器通过 Cookie 中的内容,以此来验证用户的身份。

  • Cookie是在哪里存的?

Cookie存在浏览器所在主机的硬盘中,Cookie在存的时候是按照 浏览器 + 域名(地址)来进行细分的,不同的浏览器各自存各自的Cookie,同一个浏览器不同的域名对应不同的Cookie,Cookie里的内容不光是键值对还有过期时间      

  • Cookie保存的数据是什么格式?

多组键值对(键=值,多个键值对用分号间隔)(key = value;)

2 Session 

Session是一种服务端保存会话的技术,一次会话指登陆没有注销或者超时

由于HTTP协议是无状态的,所谓无状态,就是一次请求,一次响应,服务端无法感知之前登陆的用户,所以在服务端使用Map<String,Session>的数据结构来保存用户信息

3. Cookie和Session是如何一起工作的? 

以登陆功能举例: 

  1. 服务端校验账号密码成功后,生成一个随机字符串(sessionId用来标识用户身份)及一个Session对象(标识用户的该次对话),把sessionId作为键,Session对象作为值存入Map<String,Session>如果需要保存用户信息就保存在Session对象Map<String,Object>中,相当于登陆时服务端使用Session保存用户信息 
  2. 登陆响应,服务端返回给客户端的HTTP响应数据包中,Set-Cookie响应头包含sessionId=xxx  (相当于给客户端返回了一个身份id)
  3. 客户端收到响应后,保存Cookie信息,将响应的Set-Cookie中的内容保存在客户端本地(和此次服务器地址绑定)
  4. 客户端每次请求时,都携带sessionId=xxx在Cookie头中 (相当于客户端访问带着身份id访问)
  5. 服务端获取客户端请求时,先获取Cookie请求头中的内容,查找sessionId对应的值,然后从保存的Map结构中查找,如果存在就是登录用户,如果为null就是未登录 (服务器通过身份id确定客户端的身份,将保存的用户信息返回给客户端)

4. Cookie和Session的过期校验

🌵Session的过期校验

服务端保存的Session信息有默认的过期时间(可通过程序设置) 

服务器有Session的过期校验机制:通过单独的线程扫描,发现当前时间和Session最后一次使用的时间超时就删掉

服务器存放Session的地方,web服务器默认是存放在内存中,所以重启服务器Session也就没了,但是有些服务器把数据保存在服务器硬盘,重启就还有

如果用户注销登录,相当于服务端删除Map中的Session

所以超时后,注销后,重启服务器后需要访问页面就需要重新登陆

🌳Cookie的过期校验 

Cookie也有过期时间(可以通过程序设置) 

如果Cookie过期,浏览器发请求时就不会携带这些信息,服务端验证sessionId时就会验证失败,也就是没有登陆

如果在客户端手动删除Cookie,就相当于服务端还有Session信息,但是客户端请求时也不会携带Cookie信息,服务端验证sessionId失败,也就意味没有登陆

5. Cookie和Session的关联和区别

关联:

  • 在网站的登陆功能中,需要配合使用.

区别:

  1. Cookie是客户端的存储机制. Session是服务器的存储机制.
  2. Cookie 里面可以存各种键值对(还可以存别的).Session 则专门用来保存用户的身份信息.
  3. Cookie 完全可以单独使用, 不搭配 session (实现非 登陆 场景下)
  4. Session 也可以不搭配 Cookie 使用. (手机 app 登陆服务器, 服务器也需要 Session, 此时就没有Cookie的概念) Cookie跟浏览器强相关的~~
  5. Cookie 是属于 HTTP 协议中的一个部分
  6. Session 则可以和HTTP 无关(TCP, websocket .. 也可以用 session)

注意:

  • 此处 Cookie 和 Session 之间的配合,主要是针对一些主流的网页实现。Cookie 字段存在的意义就是:为了浏览器能够安全可靠地访问服务器的存储信息。在很多情况下,服务器会生成一个唯一的Session ID,并将其存储在一个Cookie中发送给浏览器。这样,当浏览器再次向服务器发送请求时,服务器可以通过读取Session ID来查找对应的Session

二、核心方法

HttpServletRequest 类中的相关方法:

方法描述
HttpSession getSession()在服务器中获取会话. 参数如果为 true, 则当不存在会话时新建会话; 参数如果为 false, 则当不存在会话时返回 null
Cookie[] getCookies()返回一个数组, 包含客户端发送该请求的所有的 Cookie 对象. 会自动把Cookie 中的格式解析成键值对.

getSession 方法:既能用于获取到服务器上的会话,也能用于创建会话。具体行为取决于参数:

  • 如果参数为true:若会话不存在,则创建;若会话存在,则获取
  • 如果参数为false:若会话不存在,则返回null;若会话存在,则获取

就还拿之前那个医院看病的例子来说:

当你来到一家新医院的时候,你要先挂号,那么人家挂号的医生就会问你,你有没有这里的"就诊卡"?有就不需要再办了,如果没有就需要办一张新的卡。而当你挂了号之后,你不仅会得到一张"就诊卡",同时在医院的服务器里也会给你开一个档案来存储你的就诊信息(这就是你的会话)。
但是如果这个医院比较特殊,人流量特别多的话,而这个医院的承载病人的能力是有上限的,那么这个时候医生就会问你在这里建过档没有,如果建过才会给你挂号,没有建过的话就不给你挂(就像生孩子的时候得提前预约(当你发现自己怀孕了就提前去建个档),因为妇产科人是特别多的!)

调用getSession()的时候具体要做的事情:

  • 创建会话
  • 首先先获取到请求中 Cookie 里面的 sessionId字段(相当于会话的身份标识),判定这个 sessionId 是否在当前服务器上存在,如果不存在,就会进入到创建会话的逻辑⬇️⬇️⬇️

创建会话: 就会创建一个HttpSession 对象 ,并且生成一个 sessionId(这个sessionId 是一个很长的数字,通常是用十六进制来表示,能够保证唯一性),接下来就会把这个 sessionId 作为 key,把这个Httpsession 对象作为value,把这个键值对给保存到服务器内存的一个"哈希表"(也不一定是他,只是类似这种结构)这样的结构中,再然后服务器就会返回一个HTTP 响应,把 sessionId 通过 Set-Cookie 字段返回给浏览器,浏览器就可以保存这个 sessionId 到 Cookie 中了。        

  • 获取会话
  • 先获取到请求中的 Cookie 里面的 sessionId字段(会话的身份标识),判定这个sessionId 是否在当前服务器上存在(也就是在哈希表中是否存在),如果有就直接查询出这个 HttpSession 对象,并且通过返回值返回回去。

那么HttpSession 对象到底是什么呢❓🤔
这个对象本质上也是一个“键值对”的结构,允许程序猿往HttpSession 对象(value)中,存储任意的键值对数据——key必须是String,value是一个Object
图解如下:

 getCookies()方法:获取到请求中的Cookie数据,返回值是Cookie类型的数组,每个元素是一个Cookie对象,每个Cookie对象又包含了两个属性,name和value(还是键值对形式😂)

HTTP 请求中的 Cookie 字段就是按照键值对的方式来组织的,这里的这些键值对,大概的格式是使用 ; 来分割键值对,使用 = 来分割键和值,这些键值对都会在请求中通过 Cookie 字段传给服务器,服务器收到请求后,就会进行解析,解析成 Cookie[] 这样的形式。
由于Cookie这里是可以保存任意自定制的键值对,所以如果是一般的键值对,直接通过getCookies来获取;如果是特殊的键值对(表示sessionId的键值对),不需要使用getCookies,直接使用getSession其实就自动帮我们从Cookie中取sessionId了。

HttpServletResponse 类中的相关方法:

方法描述
void addCookie(Cookie cookie)把指定的 cookie 添加到响应中

Cookie 类中相关的方法:

每个 Cookie 对象就是一个键值对

方法描述
String getName()

该方法返回 cookie 的名称。名称在创建后不能改变。(这个值是 SetCooke 字段设置给浏览器的)

String getValue()该方法获取与 cookie 关联的值
void setValue(String newValue)该方法设置与 cookie 关联的值

三、交互过程

1. 理解交互过程

在下图中,用户 Tom 从浏览器登录,那么浏览器就要给服务器返回响应,假设登录成功,服务器就会返回登录成功的响应,那么第二次 Tom 想获取购物车的请求,服务器是怎么知道的,我即将访问的购物车数据是 Tom 的呢?

 2. 解析交互过程

3. 案例登录淘宝(加深理解) 

服务器以key,value的键值对形式保存每个用户的信息,key是表示当前用户身份的序号(唯一),value存储的用户的信息,后续浏览器访问带着唯一身份序号(sessionId)访问,服务器在上述hash表中按照key去查询对应的value,将用户信息返回给浏览器

四、模拟场景
实现一个用户登录的案例

  1. 首先,要有一个 html,包含用户名密码的输入框,以及登录按钮。
  2. 其次,要有一个 LoginServlet,来处理登录请求。
  3. 最后,要有一个 IndexServlet,模拟登录完成后,跳转到的主页,在这个主页里面就能够获取到当前用户的身份信息。 ( 这里就可以存储开发人员自定义的用户数据,比如可以存一个当前用户访问的次数 )

约定前后端的交互接口

我们这里需要两组交互:一个是登录,另一个是获取主页。而针对前后端交互接口的话,实际上有很多种约定方式,我们选择下面这种来进行约定

1. 代码实现

之前说了一些理论的交互流程,而 Servlet 已经对这里的流程进行了封装,开发人员在使用的时候,相对简单,因为大部分工作,已经在 Servlet 的内部封装好了,只需要通过 一些API 进行调用即可。

使用 form表单来构造 post 请求:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>登录</title>
</head>
<body>
    <form action="login" method="post">
        <input type="text" name ='username'>
        <br>
        <input type="password" name="password">
        <br>
        <input type="submit" value="提交">
    </form>
</body>
</html>

LoginServlet 类 (用来构造 POST响应)

@WebServlet("/login")
public class LoginServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        //验证用户名密码是否正确
        //正确情况下,用户名和密码是用数据库来保存
        //此处直接写死
        //此处约定, 用户名合法的是 zhangsan 和 lisi
        //密码合法的都是 123
        /*//这个代码固然能写出效果,但是嵌套太深了,还是等价转换一下
        if(username.equals("zhangsan") || username.equals("lisi")) {
            if(password.equals("123")) {
                //登录成功
            } else {
                //登录失败

            }
        } else {
            //登录失败
        }*/
        if(!username.equals("zhangsan") && !username.equals("lisi")) {
            //登录失败 -> 重定向到 登录页面
            System.out.println("登录失败,用户名错误");
            resp.sendRedirect("login.html");
            return;
        }
        if(!password.equals("123")) {
            //登录失败 -> 重定向到 登录页面
            System.out.println("登录失败,m密码错误");
            resp.sendRedirect("login.html");
            return;
        }
        //登录成功
        //1. 创建一个会话
        HttpSession session = req.getSession(true);
        //2. 把当前的用户名保存到会话中
        session.setAttribute("username",username);
        //3. 重定向到主页
        resp.sendRedirect("index");
    }
}

IndexServlet 类 (用来构造跳转页面)

@WebServlet("/index")
public class IndexServlet extends HttpServlet {
    //通过重定向,浏览器发送的是 GET.
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //先判断用户的登录状态
        //如果用户还没登录,要求先登录
        //已经登录了,根据 会话 中的用户名,来显示到页面上
        //1.这个操作不会触发会话的创建
        HttpSession session = req.getSession(false);
        if(session == null) {
            //未登录状态
            System.out.println("用户未登录!");
            resp.sendRedirect("login.html");
            return;
        }
        //2.已经登录
        String username = (String) session.getAttribute("username");
        //3.构造页面
        resp.setContentType("text/html;charSet=utf8");
        resp.getWriter().write("欢迎" + username + "回来!");
    }
}

2. 代码分析

详解 LoginServlet 类 第1步代码

HttpSession session = req.getSession(true);

上面的代码表示:session 这个会话在第一次登录成功之前,并没有创建,由于 getSession 方法的参数是 true,所以登录成功后,【不存在会话,就创建。】即创建了一个 session 对象,同时生成了一个 sessionID.

只不过,我们在代码中看不到生成的 sessionID 是什么,这是因为 Servlet 已经为我们封装好了代码。

详解 IndexServlet 类 第1步代码

HttpSession session = req.getSession(false);

上面的代码表示:通过 session 对象这个会话机制,来判定 session 对象有没有被创建出来,以此来判定用户是否已经登录过页面。

由于 getSession 方法中的参数是 false,【不存在,不创建】。既然不创建,也就只能用来判断用户是否已然是登录状态了。( 在 LoginServlet 类中,我们可以看到:只要用户名或密码输入错误,也就意味着登录失败,代码的逻辑是什么也不做,最终也就返回了,故而,session 这个对象就没有被创建出来,显然,用户没有成功登录。)

如果登录成功,说明 session 对象早已经被创建了,那么它的里面也就存储了键值对结构,包括用户名和密码。

后续,如果我们通过值来找到对象,或者通过对象找值,也就很方便了。总之,我们就将其看作键值对的结构即可。

注意:
上面的两个不同类中的 session 表示的是同一个对象,也就是说,session 只有一份,我们可以将它想象成一个存钱罐,【存钱的时候,就往里面放对象;取钱的时候,就从里面拿对象。】

3. 理解代码交互的过程

我们将 Cookie 想象成一个身份令牌,同一个账户只有唯一一个令牌,每个令牌对应的sessionID 不相同,所以每个账户的令牌都不相同。那么,每个账户对应的 Cookie 也不相同,这也就实现了网页登录的安全性。此外,在第一次登录之后,客户端发送第二次请求的时候,就会自动带上 Cookie,这样一来,服务器就能直接通过 Cookie 中的 sessionID 就行识别,之后的操作,服务器就只对当前用户进行数据访问了。

4. 展示抓包结果

第一次交互:

 第二次交互:

 浏览器展示结果

在浏览器为我们呈现的结果就是,当我们输入正确的用户名和密码后,服务器端就会响应,并且跳转到另一个页面。这和我们平时通过网页登录就很相似了,当然,这里,我并没有将前端页面设置的很好看,重在表达逻辑。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/578086.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

目标检测YOLO实战应用案例100讲-基于深度学习的无人机航拍图像目标检测算法研究与应用

基于深度学习的无人机航拍图像目标检测算法研究与应用 无人机是无线遥控装置和内置的程序控制装置操纵,亦或由车载计算机完全地 或间歇地规律操控的不载人飞机。无人机的地面航拍成像在军事探察、地质勘探、 公安侦查等领域[1-2]得到广泛应用。在军事领域,能够通过无人机进行…

chatgpt赋能Python-python_读取png

前言 Python是一种高级编程语言&#xff0c;尤为擅长处理数据科学和机器学习方面的应用。PNG&#xff08;Portable Network Graphics&#xff09;是一种流行的图像格式&#xff0c;它使用无损压缩技术来存储图像数据。在本文中&#xff0c;我们将介绍如何使用Python读取PNG图像…

SAP MM采购申请审批-成本中心

抬头审批的采购申请中行项目里的成本中心必须是同一个! 1、创建特性成本中心CT04 2、把特性分配给类CL02 3、维护分类审批策略 这些成本中心都可以使用&#xff0c;如果是单项就需要再CT04维护成多值。 如下采购申请&#xff0c;系统找不到审批策略, 2个行项目中&#xff0c;成…

【C语言】简单排序:冒泡排序和选择排序(含图解)

文章目录 1. 冒泡排序1.1 思想1.2 代码实现 2. 选择排序2.1 思想2.2 代码实现 1. 冒泡排序 1.1 思想 选择排序算法思想&#xff1a;以升序为例 比较相邻的元素。如果第一个比第二个大&#xff0c;就交换他们两个。对每一对相邻元素作同样的工作&#xff0c;从开始第一对到结…

了解list

list 1. list的介绍及使用1.1 list的介绍1.2 list的使用1.2.1 list的构造1.2.2 list iterator的使用1.2.3 list capacity1.2.4 list element access1.2.5 list modifiers1. resize2. push_back/pop_back/push_front/pop_front3. insert /erase4. swap/clear 1.2.6 list operati…

chatgpt赋能python:Python中的数字转换

Python中的数字转换 在Python中&#xff0c;数字转换是一项非常基础但是非常重要的任务。无论您是在进行数据分析、机器学习还是编写Web应用程序&#xff0c;数字转换都是必不可少的。在这篇文章中&#xff0c;我们将介绍Python中的数字转换并提供一些实用的示例。 将字符串转…

Unity之SpriteShapeController

Detail&#xff1a;精灵形状的质量 高中低三种质量 Is Open Ended&#xff1a;是否是开放的&#xff0c;不封闭的 Adaptive UV&#xff1a;自适应UV&#xff0c;如果开启&#xff0c;会自动帮助我们判断是平铺还是拉伸 开启后只有宽度够才会平铺&#xff0c;如果宽度不够会拉…

micropython固件编译——把自己的py库添加进固件

目录 0. 前言1. 编写自己库的代码2. 移植库3. 验证 0. 前言 本节编译自己写的py库&#xff0c;增强移植性&#xff0c;往后烧录自己的固件即可轻易移植代码 没装好环境或者没有基础可以先看看这个&#xff1a; Ubuntu下ESP-IDF的环境搭建 Ubuntu下编译esp32micropython固件编…

antV 事件多次触发问题,解绑

由于最近刚刚接触 antV - 数据可视化,对于他的事件应用还比较陌生,在应用中莫名其妙多次调用,想了很多方式如节流……,但是没有用。 业务介绍 当我点击流程图中的某一项进行提示,每次双击都会递增调用。 解决过程 当时想着用节流的方式,但是很遗憾,他还是疯狂递增调用…

Go语言的命令

常用命令 假如你已安装了golang环境&#xff0c;你可以在命令行执行go命令查看相关的Go语言命令&#xff1a; Go语言是一门编译型语言&#xff0c;通过命令行工具来编译、运行和管理代码。以下是Go语言的一些常用命令及其用法&#xff1a; go run&#xff1a;用于编译并直接…

chatgpt赋能python:Python补全:介绍和优点

Python补全&#xff1a;介绍和优点 Python是一种高级编程语言&#xff0c;自20世纪90年代以来一直广受欢迎。Python被认为是一种非常易学易用的语言&#xff0c;因为它的代码看起来就像是英文一样流畅自然。它是一种解释性语言&#xff0c;这意味着代码可以直接在计算机上运行…

LeetCode 24. 两两交换链表中的节点

C代码&#xff1a; class Solution { public:ListNode* swapPairs(ListNode* head) {ListNode* dummyHead new ListNode(0);//设置一个虚拟头结点dummyHead->next head;// 将虚拟头结点指向head&#xff0c;这样方面后面做删除操作ListNode* cur dummyHead;//初始时&…

Android SharedPreferences转为MMKV

开篇 开局一张图&#xff0c;说明一切问题。 MMKV优势 可以看出MMKV相比SP的优势还是比较大的&#xff0c;除了需要引入库&#xff0c;有一些修改上的成本以外&#xff0c;就没有什么能够阻挡MMKV了。当然了&#xff0c;MMKV也有着不广为人知的缺点&#xff0c;放在最后。 MM…

【STM32F103ZE实验】【实验1】点亮LED

STM32CubeMx生成keil工程 步骤1&#xff1a;打开STM32CubeMx&#xff0c; 选择MCU类型 步骤2&#xff1a; 设置Debug类型 步骤3&#xff1a; 选择时钟源 步骤4&#xff1a; 配置时钟 步骤5&#xff1a; 配置GPIO控制LED 首先配置PE5 点击GPIO_Output进行相关配置&#…

如何使用Node.js REPL

目录 1、Nodejs REPL 2、_特殊变量 3、向上箭头键 4、点命令 5、从JavaScript文件运行REPL 1、Nodejs REPL REPL代表Read-Evaluate-Print-Loop&#xff0c;是交互式解释器。 node命令是我们用来运行Node.js脚本的命令&#xff1a; node script.js 如果我们运行node命令…

chatgpt赋能python:Python数据处理中如何选取指定范围的数据

Python数据处理中如何选取指定范围的数据 Python已经成为了数据科学家和工程师的标配&#xff0c;尤其在数据处理和数据分析中&#xff0c;Python具有广泛的应用。在数据处理中&#xff0c;选取指定范围的数据是一个很重要的功能。本文将介绍Python中如何实现指定范围的数据选…

SpringBoot——原理(起步依赖+自动配置(概述和案例))

在Spring家族中提供了很多优秀的框架&#xff0c;所有的框架都是基于同一个基础框架——Spring Framework. 使用spring框架开发麻烦的一批&#xff0c;光是搞依赖和配置就够人喝一壶了。因此在spring4.0版本之后又推出了springboot框架。springboot框架用起来比spring框架简单…

chatgpt赋能python:Python行长度的重要性及最佳实践

Python 行长度的重要性及最佳实践 Python 行长度的重要性 对于一门编程语言而言&#xff0c;行长度是指每一行代码的字符数&#xff0c;Python 也不例外。同时&#xff0c;Python 的行长度限制也是相当明确的&#xff0c;官方建议不要超过 79 个字符&#xff0c;而 PEP 8 规范…

【编译、链接、装载一】预处理、编译、汇编、链接

【编译和链接一】预处理、编译、汇编、链接 一、被隐藏了的过程二、预处理器&#xff08;Prepressing&#xff09;——cpp1、预处理指令2、预处理过程3、预处理生成的hello.i文件 三、编译器&#xff08;Compilation&#xff09;——cc1、编译指令2、编译的过程3、编译生成的文…

chatgpt赋能python:Python读取Mat文件的完整教程

Python 读取Mat文件的完整教程 在数据科学领域&#xff0c;Matlab&#xff08;或简称Mat&#xff09;是最受欢迎的编程语言之一。Matlab可用于数学计算、数据预处理、建模和数据分析。然而&#xff0c;Matlab的开销和许可证成本会限制公司和个人的使用。因此&#xff0c;Pytho…