session从入门到精通
一、什么是session
session对于前端并不是一个陌生的东西,因为看到session我们首先会想到window.sessionstorage,本意也是存储一类的东西。
但其实session是另一种记录服务器和客户端会话状态的机制,通常用于存储用户的登录信息、权限控制、购物车等数据。

二、session和cookie的区别

| 对比项 | Session | Cookie |
|---|---|---|
| 存储位置 | 服务器 | 客户端(浏览器) |
| 安全性 | 更安全(不暴露数据) | 容易被篡改 |
| 数据大小 | 可以存较大数据 | 受浏览器限制(一般 4KB) |
| 生命周期 | 可设定超时时间 | 受浏览器控制 |
| 适用场景 | 用户登录、购物车等 | 轻量级数据存储,如主题颜色 |
1. 存储位置
- Cookie:存储在客户端(用户的浏览器)中,通常是以键值对的形式保存在浏览器的本地存储中。
- Session:存储在服务器端,服务器会根据每个用户的会话信息存储数据,并为每个用户分配一个唯一的会话ID。
2. 工作原理
- Cookie:浏览器会在每次请求时自动将存储的Cookie信息发送到服务器,服务器可以使用这些信息进行用户身份验证或其他功能。
- Session:每次客户端发出请求时,服务器会生成一个唯一的会话ID,这个ID会通过Cookie的方式发送到客户端。客户端随后会在后续请求中携带该ID,服务器利用这个ID来查询存储在服务器端的会话数据。
3. 数据存储
- Cookie:数据存储在客户端,数据量较小(通常限制在4KB以内)。存储在浏览器中时,数据会随着每次请求发送给服务器,可能会有泄露或被篡改的风险。
- Session:数据存储在服务器上,只有会话ID保存在客户端的Cookie中,数据量较大。用户的敏感数据或状态信息可以安全地保存在服务器端。
4. 安全性
- Cookie:因为Cookie存储在客户端,理论上用户可以修改或窃取Cookie中的数据。为了安全起见,可以设置Cookie的HttpOnly(防止JavaScript访问)和Secure(只在HTTPS连接中发送)标志来增强安全性。
- Session:数据存储在服务器端,因此不会暴露在客户端,通常更安全。只有会话ID会暴露在客户端,但这个ID本身并不包含用户的具体数据。
5. 生命周期
- Cookie:可以设置过期时间,过期后自动删除。如果没有设置过期时间,Cookie会在浏览器关闭时自动删除。
- Session:Session通常与浏览器的会话生命周期绑定,当用户关闭浏览器时,Session也会结束(除非设置了持久化Session,或者服务器重启)。
总结
- Session和Cookie的关系:Session存储在服务器端,Cookie存储在客户端,Session的ID通常通过Cookie在客户端和服务器之间传递。
- 它们的不同:Session是存储在服务器的会话信息,而Cookie是客户端保存的小数据块。Session通常比Cookie更安全,因为敏感数据保存在服务器端。
总结 Session和Cookie的关系:Session存储在服务器端,Cookie存储在客户端,Session的ID通常通过Cookie在客户端和服务器之间传递。 它们的不同:Session是存储在服务器的会话信息,而Cookie是客户端保存的小数据块。Session通常比Cookie更安全,因为敏感数据保存在服务器端。 虽然它们是相互依赖的,但它们的作用、存储位置和安全性等方面是不同的,所以说Session和Cookie不是一个东西。
三、session-cookie 认证流程:
Session 认证流程
创建 Session
- 用户首次请求服务器时,服务器根据用户提交的信息创建一个
Session。 - 服务器生成一个唯一的
SessionID并将其返回给客户端。 Session可存储在 内存 或 Redis(推荐 Redis 以提高性能)。
- 用户首次请求服务器时,服务器根据用户提交的信息创建一个
存储 SessionID
- 浏览器收到响应后,将
SessionID存入Cookie,并绑定到当前域名。 - 下次访问该域名时,浏览器会自动携带
Cookie信息。
- 浏览器收到响应后,将
请求验证
- 服务器接收到请求后,解析
Cookie,获取SessionID。 - 服务器查询
SessionID是否存在:- 存在:说明用户已登录,可执行后续操作。
- 不存在:用户未登录或
Session失效,需要重新认证。
- 服务器接收到请求后,解析
(可选)签名加密
SessionID可进行 签名加密,防止伪造和篡改。- 服务器使用
secret密钥解密SessionID,确保数据安全。

session在服务端一般都会存哪些信息
1. 用户身份信息(用户认证)
- 用户ID、用户名:用来标识用户身份。
- 权限信息:例如,用户的角色(普通用户、管理员等),权限控制等。
2. 用户登录状态
- 登录时间、登录IP、Session有效期:帮助跟踪用户的登录状态。
- Session ID:虽然Session ID存储在客户端Cookie中,但在服务器端,Session ID与实际数据绑定。
3. 购物车信息
- 商品信息:例如用户购物车中选择的商品ID、数量、价格等。
- 临时订单数据:一些电商网站会在用户未提交订单前,将临时数据存储在Session中。
4. 表单数据和用户输入
- 表单提交的数据:比如用户填写的注册信息或其他交互数据,避免用户在提交表单后页面刷新丢失数据。
- 用户搜索记录:在搜索功能中,存储用户的搜索记录,以便用户能够看到自己的历史查询。
5. 用户偏好设置
- 语言选择、主题偏好:例如用户选择的语言、界面主题(暗黑模式、亮色模式等)。
- 布局、显示设置:比如个性化的布局设置或其他用户界面偏好。
6. 应用状态
- 多步骤流程数据:在多步骤表单或操作中,存储用户的进度和状态(例如,完成了哪个步骤,下一步是什么)。
- 弹窗状态:例如某些弹窗是否被关闭,避免页面重新加载后再次弹出。
7. 防止CSRF攻击的token
- CSRF Token:一些网站使用Session存储CSRF(跨站请求伪造)防护的token,确保请求来源的合法性。
8. 临时数据和缓存
- 页面缓存:例如,存储用户临时访问的数据,避免重复计算或加载相同的内容。
- 未保存的草稿数据:如文本编辑器中的未提交内容,用户关闭浏览器后重新打开时恢复。
9. 错误信息
- 错误日志或错误状态:某些应用可能会在Session中存储暂时的错误信息,用于后续展示或调试。
10. 第三方应用数据
- 第三方服务的身份认证信息:例如与OAuth、OpenID等认证相关的数据,用户的授权令牌(Access Token)等。
四、session放在服务器需要解决哪些问题
1.占用服务器内存,影响性能
- 内存有限:如果并发用户多,每个 Session 都占用一定的内存,导致内存使用过多,影响服务器性能。
- 扩展性差:随着用户增多,服务器内存不足时,Session 不能无限增长,可能导致 OOM(内存溢出)。
2. 服务器重启,Session 丢失
- Session 存在内存中,一旦服务器重启,所有 Session 数据都会丢失,用户需要重新登录,影响用户体验。
- 解决方案:可以使用 Redis、数据库、文件存储等方式持久化 Session。
3.多台服务器时,Session 共享困难
- 如果是 分布式部署(多台服务器),Session 只存内存会导致:
- 用户第一次访问 A 服务器,Session 存在 A 的内存中;
- 第二次访问 B 服务器,B 服务器没有这个 Session,导致登录失效。
- 解决方案:
- Session 复制(让所有服务器同步 Session,但会有性能开销)。
- Session 共享(Redis、数据库存储)### 1. 占用服务器内存,影响性,所有服务器访问同一个存储。
- JWT 方案,让客户端存储身份信息,服务器无状态化。
4.内存数据难以持久化
- 如果需要对 Session 数据进行日志记录、统计分析、跨设备同步,存在内存中很难实现。
- 解决方案:可以存入 Redis 或数据库,支持更丰富的数据管理。”
| 问题 | 影响 | 解决方案 |
|---|---|---|
| 占用内存 | 并发高时导致 OOM | 限制 Session 过期时间,定期清理,或使用 Redis |
| 服务器重启丢失 | 用户需要重新登录 | 用 Redis 或数据库存储 Session |
| 分布式部署困难 | 负载均衡导致 Session 丢失 | 用 Redis 共享 Session,或使用 JWT |
| 数据不持久化 | 无法做统计、日志分析 | 存入数据库或日志系统 |
算下服务器内存占用
在一个 4GB 的服务器内存中能够存储多少 Session 数据 取决于以下几个因素:
1. 每个 Session 的大小
每个 Session 在服务器端存储的大小是决定服务器能够存储多少个 Session 的关键因素。每个 Session 的大小包括以下内容:
- Session ID:通常是一个唯一的标识符,通常为 32 字节或 64 字节。
- Session 数据:存储在 Session 中的实际数据量(如用户信息、购物车、临时数据等)。这个数据量的大小可能根据应用而异。
假设一个典型的 Session 数据大小为 1KB,这意味着每个 Session 数据大约占用 1KB 的内存。如果 Session 数据比较复杂,大小可能会更大(例如 2KB 或 3KB)。
2. 内存计算公式
要计算一个 4GB 内存的服务器能够存储多少个 Session,可以使用以下公式:
存储的 Session 数量=服务器总内存(字节)/ 每个 Session 大小(字节), 存储的 Session 数量 = 服务器总内存(字节)/ 每个 Session 大小(字节)
假设每个 Session 占用 1KB(1024 字节),服务器的总内存为 4GB(4 × 1024 × 1024 × 1024 字节),我们可以计算存储的 Session 数量:
存储的 Session 数量=(4×1024×1024×1024)/1024=4,194,304
这意味着,在 1KB 每个 Session 的情况下,4GB 的内存大约可以存储 4,194,304 个 Session。
3.实际情况
然而,实际情况可能有所不同,以下因素会影响实际存储的 Session 数量:
- Session 数据的大小:如果每个 Session 存储的数据比 1KB 大,那么能够存储的 Session 数量将减少。比如如果每个 Session 占用 2KB,那么最多能存储的 Session 数量会减少一半,即约为 2,097,152 个。
- 服务器的操作系统开销:操作系统和应用程序自身也需要内存,不能完全用来存储 Session。
- Session 的过期机制:如果 Session 有过期时间,过期的 Session 会被清理,腾出内存空间。
4.Session 存储的实现
此外,如果你使用 内存数据库(例如 Redis)来存储 Session 数据,而不是直接存储在服务器的内存中,你可以利用数据库的压缩和存储优化特性,从而提高存储效率。
五、使用node.js完成一个demo
- 云端代码
const express = require("express");
const session = require("express-session");
const RedisStore = require("connect-redis").default;
const Redis = require("ioredis");
const app = express();
const redisClient = new Redis(); // 连接 Redis 默认 127.0.0.1:6379
// 配置 session
app.use(
session({
store: new RedisStore({ client: redisClient }), // 使用 Redis 存储 session
secret: "my_secret_key", // 用于加密 session ID
resave: false,
saveUninitialized: false,
cookie: { maxAge: 60000 }, // 1 分钟后过期
}),
);
// 设置 session 示例
app.get("/login", (req, res) => {
req.session.user = { id: 1, name: "张三" };
res.send("登录成功,session 已保存");
});
// 获取 session
app.get("/profile", (req, res) => {
if (req.session.user) {
res.send(`欢迎 ${req.session.user.name},你的 session 还在`);
} else {
res.send("请先登录");
}
});
// 退出登录
app.get("/logout", (req, res) => {
req.session.destroy(() => {
res.send("已退出登录,session 被销毁");
});
});
app.listen(3000, () => console.log("Server running on http://localhost:3000"));- 客户端代码
<template>
<div>
<button @click="login">登录</button>
<button @click="getProfile">获取 Session</button>
<button @click="logout">退出登录</button>
<p>{{ message }}</p>
</div>
</template>
<script>
import axios from "axios";
export default {
data() {
return { message: "" };
},
methods: {
async login() {
await axios.get("http://localhost:3000/login", { withCredentials: true });
this.message = "已登录";
},
async getProfile() {
const res = await axios.get("http://localhost:3000/profile", {
withCredentials: true,
});
this.message = res.data;
},
async logout() {
await axios.get("http://localhost:3000/logout", {
withCredentials: true,
});
this.message = "已退出";
},
},
};
</script>