【Cookie和Session】

news/2025/3/19 13:23:21/

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

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


目录

一、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 {@Overrideprotected 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.@Overrideprotected 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.ppmy.cn/news/96904.html

相关文章

ChatGPT与讯飞星火实测对比

文章目录 一、推理测试测试提示词1&#xff1a;假设树上有10只鸟&#xff0c;开枪打死1只&#xff0c;那么树上还有几只鸟?- 测试提示词2&#xff1a;一艘船10天可以渡过太平洋&#xff0c;请计算10艘船多少天可以渡过太平洋。测试提示词3&#xff1a;我爸妈结婚的时候为什么不…

Qt - UI进阶

Qt - UI 进阶 布局控件及其坐标pos/position/scenePosition/globalPosition 场景和视图&#xff1f;&#xff1f;&#xff1f; 布局 https://blog.csdn.net/kongcheng253/article/details/128769765 控件及其坐标 pos/position/scenePosition/globalPosition pos()函数返回的…

动态组件、插槽、自定义指令、Eslint和prettierrc配置、axios全局挂载

动态组件、插槽、自定义指令、Eslint和prettierrc配置、axios全局挂载 动态组件插槽体验插槽的基础用法作用域插槽 自定义指令Eslint和prettierrc配置prettierrc axios全局挂载 动态组件 动态组件指的是动态切换组件的显示与隐藏。 如何实现动态组件渲染 vue 提供了一个内置的…

《Spring Guides系列学习》guide51 - guide55

要想全面快速学习Spring的内容&#xff0c;最好的方法肯定是先去Spring官网去查阅文档&#xff0c;在Spring官网中找到了适合新手了解的官网Guides&#xff0c;一共68篇&#xff0c;打算全部过一遍&#xff0c;能尽量全面的了解Spring框架的每个特性和功能。 接着上篇看过的gui…

英睿达内存条正品鉴别教程(镁光颗粒)

我们打算买一款二手镁光颗粒的英睿达内存条,需要从正面内存标签上的条形码、字串,从背面颗粒上的两行字符一一分析、检查、鉴别,最终确认是否正品,以及内存条等级如何。通过本片文章,您能学会如何进行镁光颗粒的英睿达内存条正品鉴别。 一、标签检查 首先,用百度条形码…

HTML+CSS实训——Day03——仿网易云音乐的发现页界面

仓库链接:https://github.com/MengFanjun020906/HTML_SX 一些今天需要用到的知识点 弹性盒子 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedg…

如何在华为OD机试中获得满分?Java实现【字符串重新排序】一文详解!

✅创作者&#xff1a;陈书予 &#x1f389;个人主页&#xff1a;陈书予的个人主页 &#x1f341;陈书予的个人社区&#xff0c;欢迎你的加入: 陈书予的社区 &#x1f31f;专栏地址: Java华为OD机试真题&#xff08;2022&2023) 文章目录 1、题目描述2、输入描述3、输出描述…

根据DataFrame1中指定列c1的每个值a1 从DataFrame2中指定列c2中的每个值a2 找到与a1最临近的值a2,进行所在行合并

【小白从小学Python、C、Java】 【计算机等级考试500强双证书】 【Python-数据分析】 根据DataFrame1中指定列c1的每个值a1 从DataFrame2中指定列c2中的每个值a2 找到与a1最临近的值a2,进行所在行合并 [太阳]选择题 关于以下代码说法错误的是&#xff1a; import pandas as pd…