Skip to content

session从入门到精通

一、什么是session

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

alt text

二、session和cookie的区别

alt text

对比项SessionCookie
存储位置服务器客户端(浏览器)
安全性更安全(不暴露数据)容易被篡改
数据大小可以存较大数据受浏览器限制(一般 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 认证流程

  1. 创建 Session

    • 用户首次请求服务器时,服务器根据用户提交的信息创建一个 Session
    • 服务器生成一个唯一的 SessionID 并将其返回给客户端。
    • Session 可存储在 内存Redis(推荐 Redis 以提高性能)。
  2. 存储 SessionID

    • 浏览器收到响应后,将 SessionID 存入 Cookie,并绑定到当前域名。
    • 下次访问该域名时,浏览器会自动携带 Cookie 信息。
  3. 请求验证

    • 服务器接收到请求后,解析 Cookie,获取 SessionID
    • 服务器查询 SessionID 是否存在:
      • 存在:说明用户已登录,可执行后续操作。
      • 不存在:用户未登录或 Session 失效,需要重新认证。
  4. (可选)签名加密

    • SessionID 可进行 签名加密,防止伪造和篡改。
    • 服务器使用 secret 密钥解密 SessionID,确保数据安全。

alt text

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

  • 云端代码
js
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"));
  • 客户端代码
vue
<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>

上次更新于: