目录
🍂1.联系
🍂2.区别
🍁今日良言:欲望以提升热忱,毅力以磨平高山
🐎一、Cookie和Session的工作流程
在前面介绍HTTP协议格式的博客中,详细介绍过Cookie(header部分的重要属性):
HTTP协议格式以及Fiddler用法_程序猿小马的博客-CSDN博客
Session(会话机制) 是什么?
服务器同一时刻,收到的请求非常多,所以服务器需要清楚的区分每个请求属于哪个用户,于是在服务器这边需要记录每个用户的序号以及该用户信息的对应关系.
会话的本质就是一个 "哈希表", 存储了一些键值对结构. key 就是 序号(token/sessionId), value 就是用户信息.sessionId 是由服务器生成的一个 "唯一性字符串", 从 session 机制的角度来看, 这个唯一性字符串 称为 "sessionId". 但是站在整个登录流程中看待, 也可以把这个唯一性字符串称为 "token". sessionId 和 token 就可以理解成是同一个东西的不同叫法(不同视角的叫法).
简单介绍了Session以后,接下来介绍一下Cookie 和 Session的工作流程,清楚这个工作流程可以更好的掌握对于Cookie和Session的理解:
Cookie 有一个最典型的应用:标识用户的身份信息.
以用户登录码云为例:
首先,用户输入URL以后,给码云服务器发送请求,码云服务器接收到请求后,返回响应.
然后用户输入用户名和密码以后,浏览器发起一个登录请求
当码云服务器收到这个请求以后,会查询数据库,验证用户输入的用户名和密码是否正确.
如果正确,登录成功. 码云服务器就会把当前这个用户的身份信息,在内存中也保存一份.
同时给这个用户分配一个表示身份的序号(唯一性字符串),这个序号就是sessionId,
服务器使用像 hash 表这样的结构,把序号作为key,身份信息作为 value 存储起来.
服务器把生成的这些键值对称为Session(会话)
登录成功以后,码云服务器将这个身份序号设置到响应报头的Set-Cookie字段中,返回浏览器
当浏览器收到这个响应以后,就把这个身份序号保存到 Cookie 里了.
后续当用户访问码云的其他主页时,浏览器给服务器发送的请求就会带上Cookie,
当服务器收到请求以后,就会根据 Cookie 里的身份序号,查询上述 hash 表(session 会话),判断这个用户是谁.
如果查到了,就知道用户是谁了,避免重复登录.
如果没查到,就会要求用户重新登录.
服务器是如何组织会话的?
当每个客户端登录的时候,都会有一个键值对(会话).
key 是 sessioId value 是一个 HttpSession 对象.
服务器要管理多个这样的对话,就可以使用一个hash表,把这些对话组织起来.
观察上图: 每个会话是一个键值对(一个sessionId 对应一个 HttpSession 对象)
HttpSession 自身也可以存储键值对(attribute).
🐎二、模拟网页登录功能
通过简单的模拟网页登录功能来加深对Cookie 和 Session 的工作流程.
基本逻辑:
首先需要明确,总共有两个页面,一个登录页面(login) 一个主页面(index).
当用户点击提交按钮以后,发送一个登录请求,所以需要一个LoginServlet 验证用户名和密码是否正确.
用户名和密码都正确的话就跳转到主页面.
用户名或者密码错误的话,重新登录.
期望最后的主页面上显示:欢迎xxxx回来.
所以也需要一个主页面的 IndexServlet
前端代码如下:
<!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="提交">
<br>
</form>
</body>
</html>
在 idea 中启动smart tomcat 服务器以后,在浏览器中输入URL,效果显示如下:
假设这里用户名只有zhangsan 和 lisi 正确,密码只有 123时正确.
当输入用户名和密码以后,点击提交,浏览器会给服务器发送一个 post 请求,然后LoginServlet 处理这个请求.
LoginServlet 代码如下:
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 26568
* @date 2023-03-21 15:55
*/
// 用于验证用户输入的登录信息是否正确
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// form表单构造的 post 请求,通过getParameter 方法获取请求中的参数
String username=req.getParameter("username");
String password = req.getParameter("password");
// 判断输入的用户名和密码
if (!username.equals("zhangsan") && !username.equals("lisi")) {
System.out.println("登陆失败,用户名错误");
// 重定向到登录页面
resp.sendRedirect("login");
return;
}
if (!password.equals("123")) {
System.out.println("登陆失败,用户名或者密码错误");
// 重定向到登录页面
resp.sendRedirect("login");
return;
}
// 登录成功
// 1.创建一个会话
HttpSession session = req.getSession(true);
// 把当前的用户名保存到会话中
session.setAttribute("username",username);
// 3.重定向到主页
resp.sendRedirect("index");
}
}
1.假设用户名和密码都输入正确, 此时服务器就会先构造一个 HttpSession 对象
2.然后给这个用户构造一个唯一的sessionId
3.服务器会把这个sessionId 和这个 HttpSession 对象 作为键值对(会话) 插入哈希表.
4.最后,服务器会把这个sessionId 设置到响应报文的Set-Cookie 字段中
服务器返回的响应报头中也会带有一个Loaction字段,这个字段里面的内容就是 index ,也就是接下来要跳转到的主页面.
当浏览器接收到响应以后,先将这个sessionId 保存下来,然后会向服务器发送一个get请求,请求访问 index 页面,同时这个请求的 Cookie 字段里面会带上sessionId.
当服务器接收到这个get请求的时候,IndexServlet 处理这个get请求.
IndexServlet 代码:
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 26568
* @date 2023-03-21 15:56
*/
// 主页面
@WebServlet("/index")
public class IndexServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
HttpSession session = req.getSession(false);
if (session == null) {
// 说明没有保存当前用户的信息 重新登录
System.out.println("请返回登录");
resp.sendRedirect("login");
return;
}
// 存在这个会话 返回响应
String username = (String)session.getAttribute("username");
resp.setContentType("text/html;charset=utf8");
resp.getWriter().write("欢迎"+username+"回家");
}
}
注: getSession 方法中的参数解释:
true 如果存在会话就返回这个会话,如果没有就创建一个
false 如果存在会话就返回这个会话,如果没有就不创建.
接下来通过 Fiddler 抓包来观察一下上述流程:
Fiddler 的具体使用,博主之前介绍过:
HTTP协议格式以及Fiddler用法_程序猿小马的博客-CSDN博客
首先,输入用户名和密码:
先输入不正确的情况:
点击提交后,出现的页面:
同时,会看到服务器的控制台输出:
观察 Fiddler 中的抓包,响应数据如下:
输入正确的情况:
点击提交后,出现的页面:
首先观察 post 请求的抓包结果:
请求如下:
响应如下:
JSESSIONID 就是 sessionId,服务器将其设置到了Set-Cookie 字段中.
正下方的Loaction 告诉了浏览器接下来需要访问的主页面.
然后观察 get 请求的抓包结果:
请求如下:
可以观察到,这次请求的hearder 就会带上 Cookie.
当服务器收到这个 get 请求以后,就会通过 Cookie 字段中的 sessionId 来查询哈希表中对应的用户信息.
响应如下:
这就是 IndexServlet 最后写回的数据.
以上就是一个简单的模拟网页登录流程.
🐎三、Cookie和Session的区别和联系
🍂1.区别
1). Cookie 是客户端的存储机制. Session 是服务器的存储机制.
2). Cookie 里面可以存各种键值对, Session 则专门用来保存用户的身份信息.
3).Cookie 完全可以单独使用,不搭配Session (非登录场景下)
Session 也可以不搭配 Cookie 使用(手机app 登录服务器,服务器也需要Session,此时就没有Cookie的概念), Cookie 是跟浏览器强相关的.
4). Cookie 是属于 HTTP协议中的一部分.
Session 则可以和 HTTP 无关(Tcp、websocket...也可以用session)
🍂2.联系
在网站的登录功能中, Cookie 和 Session 需要搭配使用.