Skip to content

会话控制与会话控制介绍(Day13 1/3) ​

本页对应 Day13_Node&会话控制,共 7 段视频,约 1 小时 44 分。

学习目标 ​

  1. 能够解释 HTTP 协议无状态特性带来的登录状态识别问题,并说明会话控制的作用。
  2. 能够描述 Cookie 的设置、携带、读取和删除流程,区分响应头与请求头在其中的作用。
  3. 能够使用 Express 与 cookie-parser 完成 Cookie 的设置、读取、删除,并演示域名限制、有效期等属性效果。
  4. 能够列举 Cookie 在数量容量限制、请求流量增加和客户端可篡改方面的缺点。
  5. 能够解释 Session 将数据存储在服务端、通过 Cookie 保存 Session ID 进行客户端匹配的实现原理。
  6. 能够使用 express-session 完成 Session 的初始化配置、设置、读取、删除单个数据及销毁会话。

知识点 ​

为什么需要会话控制 ​

HTTP 协议是无状态的。站在服务器角度,它只关心“你请求了什么路径”,并不天然记得“这次请求和上次请求是不是同一个浏览器”。转写稿里反复强调:上一次你登录过,下一次请求时服务器并不知道,这样登录就失去意义。所以需要会话控制,把登录状态“记住”。

怎么想:会话控制的目标不是让服务器认识“人”,而是识别“浏览器客户端”。常见方案有 cookie、session、token;cookie/session 依赖浏览器,token 常被用于脱离浏览器的场景。今天重点用代码理解 cookie 与 session 的流程。

怎么做:先看 cookie 如何把状态存在浏览器,再看 session 如何把主要数据放服务器、只让浏览器保存一个 session ID。

易错点:不要把“会话”写成“绘画/互画”;不要以为服务器天然记得登录状态。

为什么需要:cookie 是最直观的方案——登录成功后,把状态记到浏览器上,下次请求自动带上。

怎么想:谁决定设置 cookie?服务器。谁真正操作?浏览器。服务器通过响应头 Set-Cookie 告诉浏览器怎么存;浏览器后续请求时通过请求头 Cookie 自动带回。

怎么做:转写稿在百度页面按 F12 打开“应用/Application”查看 Cookie,清空后刷新,登录状态就丢了;说明登录状态确实依赖浏览器里的 cookie。不同网站、不同浏览器之间 cookie 隔离;127.0.0.1 与 localhost 默认也不共享。

易错点:cookie 以域名隔离;默认有效期是浏览器关闭即失效;maxAge 在后端 JS 里是毫秒,浏览器调试面板常显示为秒/失效时间。

为什么需要:实际工作很少手写响应头,但必须理解 Set-Cookie 与 Cookie 的对应关系。

怎么想:用中间件给 req/res 增加 cookie 处理方法,设置时加到响应对象,读取时从请求对象取。

怎么做:安装 express 与 cookie-parser,写三个路由:设置、查看、删除。

js
const express = require('express');
const cookieParser = require('cookie-parser');
const app = express();
app.use(cookieParser());

app.get('/set', (req, res) => {
  res.cookie('username', 'admin', { maxAge: 3600 * 1000 });
  res.cookie('userId', '1001');
  res.cookie('address', '北京', { maxAge: 3600 * 1000 });
  res.send('cookie设置成功');
});

app.get('/get', (req, res) => {
  console.log(req.cookies);
  res.send(`用户名:${req.cookies.username},地址:${req.cookies.address}`);
});

app.get('/delete', (req, res) => {
  res.clearCookie('username');
  res.send('cookie删除成功');
});

app.listen(8080);

调试时看响应头里的 Set-Cookie,再看后续请求头里的 Cookie。删除 cookie 的原理是重新设置并让过期时间落到过去,浏览器便立即失效。

易错点:改代码后没重启服务;读取用 req.cookies 而不是 res;删除时路径/域名不匹配可能删不掉。

为什么需要 session:cookie 存在浏览器端,有数量和大小限制(转写稿说单站约 50 个、单个约 4KB),每次请求都携带会增加流量,且用户可删改,安全性有限。

怎么想:把主要数据放服务器,浏览器只保存一个唯一标识。服务器为每个会话创建 session 对象和唯一 session ID,再通过 Set-Cookie 把 session ID 存到浏览器;后续请求带上它,服务器据此找到对应 session。

怎么做:session 数据可存内存、文件或数据库;express-session 默认存内存,服务器重启后原 session 丢失,所以演示时重启后要重新设置。

易错点:session 仍要依赖 cookie 保存 session ID;不要以为 session 数据在浏览器里;resave 可理解为每次请求重新保存/延长有效期,转写稿中“receive”应纠正为 resave。

用 express-session 演示 ​

js
const session = require('express-session');
app.use(session({
  name: 'sess',
  secret: 'keyboard cat',
  resave: true,
  cookie: { maxAge: 3600 * 1000, httpOnly: true }
}));

app.get('/set', (req, res) => {
  req.session.username = '告料纯糖';
  req.session.userId = '1001';
  req.session.parents = 1;
  res.send('session设置成功');
});

app.get('/get', (req, res) => {
  res.send(`用户名:${req.session.username},用户ID:${req.session.userId}`);
});

app.get('/delete', (req, res) => {
  delete req.session.userId;
  res.send('删除单个session成功');
});

app.get('/destroy', (req, res) => {
  req.session.destroy(() => res.send('全部session删除成功'));
});

调试时观察:设置后响应头出现 Set-Cookie,值很长,是加密后的 session ID;请求 /get 时请求头自动带 cookie,服务器据此取到 req.session。删除单个属性用 delete req.session.userId,销毁当前客户端会话用 req.session.destroy。

易错点:httpOnly: true 时前端 JS 读不到该 cookie;maxAge 单位仍是毫秒;服务器重启会丢内存 session,需重新 /set。

本节回顾的边界说明 ​

先说明一个关键点:本节笔记的回顾清单是 MongoDB 安装启动、数据库/集合/文档、mongo 命令、JS 使用 mongoose 操作 MongoDB;但提供的转写稿实际讲的是 cookie/session 会话控制,与本节笔记主题不一致。因此下面只依据笔记中的回顾清单扩写,不把 cookie/session 的推导硬套到 MongoDB 上,避免编造。原笔记清单保留如下:

text
1. MongoDB 安装启动
   数据库 集合 文档
2. mongo 命令向MongoDB服务发送操作指令
3. JS 使用 mongoose 向MongoDB服务发送操作指令

MongoDB 安装与启动:先让服务跑起来 ​

为什么需要:MongoDB 不是一段只在前端运行的代码,而是一个需要先启动的数据库服务。后面的 mongo 命令、mongoose 连接,本质上都是客户端在向这个服务发送操作指令。服务没起来,后面所有连接都会失败。

怎么想:把 MongoDB 分成“服务端”和“客户端”两层。服务端负责真正存储数据、监听端口;客户端可以是 mongo shell,也可以是 Node.js 里的 mongoose。学习顺序应是:先启动服务,再连接服务,最后发指令。

怎么做:安装 MongoDB 后启动数据库服务,确认服务监听正常;再用 mongo/mongosh 这类命令行客户端连接。具体命令和端口以实际安装版本为准,不要死记某个版本。连接成功后再进入数据库操作。

易错点:服务未启动就连接;端口被占用或连接地址写错;把“安装完成”误认为“服务已启动”;不同版本客户端命令不同却混用。调试时先看服务是否运行,再看连接字符串。

数据库、集合、文档:先建立数据层级 ​

为什么需要:MongoDB 是文档型数据库,如果不先理解层级,就容易把关系型数据库的表、行概念直接套过来,导致建模混乱。

怎么想:可以按“数据库 → 集合 → 文档”理解。数据库是最大的数据容器;集合类似一组文档的容器;文档是具体的一条数据,通常以类似 JSON 的 BSON 结构保存。一个数据库里可以有多个集合,一个集合里有多条文档。

怎么做:先确定业务用哪个数据库,再确定数据放在哪个集合,最后设计文档字段。例如用户数据放用户集合,每条用户记录就是一个文档。字段设计要围绕查询和写入需求。

易错点:把集合当成“表”后强行要求固定列;文档字段随意增删导致结构不一致;数据库名、集合名拼写错误;以为集合必须先严格建表。MongoDB 灵活不等于可以乱,仍要约定字段结构。

用 mongo 命令向服务发送操作指令 ​

为什么需要:命令行客户端是最直接验证数据库服务是否正常、数据是否写入成功的方式。它能把“连接、选库、查集合、增删改查”拆开看清楚。

怎么想:mongo 命令不是数据库本身,而是客户端工具。你输入命令,它把操作发给 MongoDB 服务,服务执行后返回结果。学习时应先验证连接,再选数据库,再操作集合。

怎么做:连接服务后,切换到目标数据库,查看集合,执行插入和查询,确认数据存在。调试时先做最小插入,再做查询;如果查不到,检查是否选错数据库、集合名是否一致。

易错点:连接后没切数据库就操作;数据库名和集合名大小写或拼写不一致;插入后没查询验证;把命令行结果当成前端页面结果。排错顺序应是:服务 → 连接 → 数据库 → 集合 → 文档。

用 JS + mongoose 向 MongoDB 服务发送操作指令 ​

为什么需要:实际 Node.js 项目不会总靠命令行操作数据库,而是需要在代码里连接、定义数据结构、执行增删改查。mongoose 就是在 JS 中操作 MongoDB 的常用工具。

怎么想:mongoose 仍然是客户端,它最终也是向 MongoDB 服务发指令。代码层通常分三步:连接数据库;定义 Schema/Model 描述文档结构;通过 Model 调用查询、创建、更新、删除方法。

怎么做:先写连接字符串并连接;再定义模型;最后在接口或脚本中调用模型方法。调试时先确认连接成功,再做一次写入和查询。若查询为空,检查连接的是不是同一个数据库、模型对应集合是否正确、查询条件是否匹配。

易错点:服务没启动就连接;连接字符串中的数据库名写错;模型名与集合名对应关系不清;把 Schema 当成数据库本身;异步操作未等待结果就返回;命令行写入和代码连接的不是同一个库。记住:mongo 命令和 mongoose 只是两种客户端,核心都是向同一个 MongoDB 服务发送操作指令。

本节易错点汇总 ​

本节的主线不是“学几个命令”,而是建立一条链路:MongoDB 服务启动 → 数据库/集合/文档分层 → mongo 命令验证 → mongoose 在 JS 中操作。排错时不要一上来改代码,先确认服务是否运行、连接地址和数据库名是否一致,再确认集合和文档结构。只要这条链路清楚,命令行和 mongoose 的用法就只是同一原理的两种入口。

会话控制:无状态 HTTP 下的登录问题 ​

为什么需要:HTTP 是无状态协议,服务器无法区分多次请求是否来自同一客户端。用户第一次请求登录了,下一次请求时服务器并不知道他登录过,登录就失去意义。
怎么想:站在后端角度,请求来自一个个浏览器;它不关心操作者是人还是别的,只关心“这个浏览器之前来过没有”。因此需要额外机制记住登录状态。
怎么做:引入会话控制,常见方案有 cookie、session、token。
易错点:无状态不等于不能登录,而是要靠会话控制补状态;会话控制主要在后端实现,前端负责配合。

常见方案与 token 的定位 ​

为什么需要:cookie、session 都依赖浏览器,但前端开发还包括原生 iOS/Android、React Native、小程序等脱离浏览器的场景。
怎么想:token 不是 HTTP 协议自带的,而是开发者设计的机制,原理与 cookie/session 相似,但不完全依赖浏览器。
怎么做:浏览器场景常用 cookie/session;跨端或脱离浏览器时考虑 token。
易错点:不要以为 token 是 HTTP 协议规定;也不要以为前端只写页面就不用懂会话控制,实际开发必须按后端要求配合。

为什么需要:让浏览器记住登录状态,下次请求自动带上,后端据此判断是否登录过。
怎么想:设置与否由服务器决定,真正保存和执行的是浏览器。服务器通过响应头告诉浏览器怎么设置 cookie。
怎么做:登录成功后,服务器在响应头中返回 Set-Cookie;浏览器保存后,后续请求把 cookie 放进请求头 Cookie;后端从请求头读取。演示中可用 cookie-parser:

js
app.use(cookieParser());

res.cookie('username', 'admin', { maxAge: 3600 * 1000 });
res.cookie('userId', '1001');

// 读取
req.cookies

易错点:cookie 按域名隔离,127.0.0.1 设置的 cookie,换成 localhost 通常拿不到;换浏览器也不共享;同一浏览器同一网站只能存一套 cookie。默认 cookie 浏览器关闭即失效;maxAge 单位是毫秒,但响应头里常显示为秒,且 Expires 是零时区时间,别误判。删除 cookie 的原理是重新设置并让过期时间变成过去时间,浏览器会立即删掉。

调试与验证:从响应头到请求头 ​

为什么需要:确认 cookie 到底有没有设置、有没有随请求携带。
怎么想:设置看响应头 Set-Cookie,存储看浏览器“应用”面板,携带看网络请求的请求头 Cookie。
怎么做:F12 打开“应用”查看 cookie;刷新页面看响应标头;清空 cookie 后刷新,登录态丢失;换域名验证隔离;关闭整个浏览器验证会话 cookie 失效。
易错点:只关标签页不等于关浏览器;清缓存要勾选 Cookie;改代码后要重启服务,否则响应头不会变化;删除某个 cookie 后,同网站其他路径也读不到它。

为什么需要:理解为什么还要引入 session。
怎么想:cookie 存在客户端,有数量和大小限制;每次请求都携带,增加流量;用户还能删改。
怎么做:cookie 只存少量、非敏感信息,如用户名或标识。
易错点:一般每网站约 50 个、单个约 4KB;不要把敏感信息放 cookie;用户删了就重新登录,不能把 cookie 当唯一安全凭据。

为什么需要:解决 cookie 容量、流量和客户端可改的问题。
怎么想:把主要数据放服务器,为每个浏览器创建 session 对象和唯一 session ID;浏览器只保存这个 ID。
怎么做:登录验证通过后把用户信息写入 req.session;服务器通过响应头让浏览器保存 session ID;后续请求带 cookie,服务端按 ID 找到对应 session。演示中可用 express-session:

js
app.use(session({
  name: 'session',
  secret: '...',
  resave: true,
  saveUninitialized: false,
  cookie: { maxAge: 3600 * 1000, httpOnly: true }
}));

req.session.username = '张三';
req.session.userId = '1001';

// 读取
req.session.username;

// 删除单个
delete req.session.userId;

// 删除当前客户端全部
req.session.destroy(() => {});

易错点:session 可存内存、文件或 MongoDB,默认内存,服务重启会丢失;session ID 要加密签名(加盐);resave 可让活跃请求重新计时,saveUninitialized 一般设 false;destroy 只删当前客户端;session 依赖 cookie 传 ID,cookie 丢了就找不到;换域名或浏览器同样隔离。

课堂画面 ​

课堂画面 1

课堂画面 2

课堂画面 3

课堂画面 4

课堂画面 5

课堂画面 6

代码示例 ​

js
// cookie-demo/index.js
// 安装:npm i express cookie-parser
const express = require('express');
const cookieParser = require('cookie-parser');

const app = express();

// 1. 中间件:解析请求头里的 Cookie -> req.cookies
//    同时给响应对象加上 res.cookie / res.clearCookie 方法
app.use(cookieParser());

// 静态页面(把示例三的 index.html 放到 public 目录即可同源访问)
app.use(express.static('public'));

// 2. 设置 cookie
app.get('/set', (req, res) => {
  // res.cookie(名字, 值, 配置对象)  ==>  最终写进响应头 Set-Cookie
  res.cookie('username', 'admin');
  res.cookie('userId', '1001');
  res.cookie('address', '北京', {
    maxAge: 3600 * 1000, // 单位是毫秒,这里是 1 小时
    httpOnly: true,      // 前端 JS 读不到这个 cookie
    path: '/'            // 默认就是 /,整个网站都能带
  });
  res.send('cookie 设置成功');
});

// 3. 读取 cookie —— 用请求对象 req 取,不是 res
app.get('/get', (req, res) => {
  console.log('req.cookies =', req.cookies);
  res.send(`用户名:${req.cookies.username},地址:${req.cookies.address}`);
});

// 4. 删除 cookie
app.get('/delete', (req, res) => {
  // 原理:重新下发一次同名 cookie,把 expires 设成 1970-01-01
  res.clearCookie('username');
  res.send('cookie 删除成功');
});

app.listen(8080, () => {
  console.log('服务已启动:http://127.0.0.1:8080');
});

关键行与易错点

  • app.use(cookieParser()) 必须写在所有路由之前,否则 req.cookies 是 undefined,res.cookie 也不存在。
  • 设置 cookie 是后端决定、浏览器执行:后端只是往响应头写 Set-Cookie,浏览器收到后才真正存到本地。
  • 读取 cookie 用请求对象 req.cookies(数据在请求头里随请求带过来),不是响应对象。
  • maxAge 单位是毫秒,而浏览器 DevTools 的 Application 面板里显示的是秒;不写 maxAge 就是会话级 cookie,浏览器一关就没了。
  • httpOnly: true 后,浏览器里 document.cookie 读不到它,但请求时浏览器照样自动带上。
  • 删除 cookie 的本质是把过期时间改成 1970 年,不是真的“从磁盘删掉”。
  • 域名限制:用 127.0.0.1 设置的 cookie,换成 localhost 访问取不到,哪怕两者指向同一台服务器。
  • 中文、数组等值会被编码,DevTools 里看到的是编码后的字符串,别以为是乱码出错。

示例二:Express + express-session 实现 session 的设置、读取、删除 ​

js
// session-demo/index.js
// 安装:npm i express express-session
const express = require('express');
const session = require('express-session');

const app = express();

// 1. session 中间件(必须放在路由之前)
app.use(session({
  name: 'sid',                // 浏览器 cookie 里存 sessionID 的键名,默认 connect.sid
  secret: 'keyboard cat',     // 签名(加盐)密钥,防止前端伪造 sessionID
  resave: true,               // 每次请求都重新保存 session -> 重新计时
  saveUninitialized: false,   // 没有内容时不创建 session
  cookie: {
    maxAge: 3600 * 1000,      // cookie 有效期,毫秒,1 小时
    httpOnly: true            // 前端 document.cookie 读不到 sessionID
  }
}));

// 2. 设置 session:给 req.session 这个对象加属性即可
app.get('/set', (req, res) => {
  req.session.username = '张三';
  req.session.userId = '1001';
  req.session.address = '北京';
  res.send('session 设置成功');
});

// 3. 读取 session
app.get('/get', (req, res) => {
  console.log(req.session);
  res.send(`用户名:${req.session.username},用户ID:${req.session.userId}`);
});

// 4. 删除单个属性
app.get('/delete', (req, res) => {
  delete req.session.userId;
  res.send('session 删除成功');
});

// 5. 删除整个 session(只删当前这个浏览器的)
app.get('/destroy', (req, res) => {
  req.session.destroy(err => {
    if (err) return res.send('销毁失败');
    res.send('session 全部删除');
  });
});

app.listen(8080, () => {
  console.log('服务已启动:http://127.0.0.1:8080');
});

关键行与易错点

  • express-session 自动做两件事:服务端建 session 对象并生成唯一 sessionID;响应头里 Set-Cookie 只存这个 ID(还是加密签名后的),大量数据留在服务端。
  • secret 必填,用于签名(加盐);没有它前端就能伪造 sessionID,安全性直接归零。
  • resave: true 就是“重新计时”:只要用户还在请求,有效期就往后顺延;长时间不请求才会失效。
  • req.session 本身就是一个对象,赋值即保存,delete req.session.xxx 删单个属性,req.session.destroy() 删整个。
  • 默认 session 存在内存里:后端服务一重启,session 全部丢失;此时浏览器里的 cookie 还在,但服务端找不到对应记录,等于未登录——这是最容易“以为自己写错了”的坑。
  • 修改代码后 nodemon 自动重启,等于 session 被清空,必须重新请求 /set 再 /get。
  • cookie.maxAge 和上一节一样是毫秒;httpOnly: true 让前端 JS 拿不到 sessionID,这是常规做法。

html
<!-- cookie-demo/public/index.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>cookie / session 前端观察</title>
  <style>
    body { font-family: sans-serif; padding: 20px; }
    button { margin-right: 8px; padding: 6px 14px; cursor: pointer; }
    pre { background: #f5f5f5; padding: 12px; border-radius: 6px; min-height: 60px; }
  </style>
</head>
<body>
  <button data-url="/set">设置</button>
  <button data-url="/get">读取</button>
  <button data-url="/delete">删除</button>
  <pre id="out">点击按钮,同时打开 F12 的 Network 面板看请求头 / 响应头</pre>

  <script>
    const out = document.getElementById('out');

    document.querySelectorAll('button').forEach(btn => {
      btn.onclick = async () => {
        const res = await fetch(btn.dataset.url);   // 同源请求,自动带上 cookie
        const text = await res.text();
        out.textContent =
          '接口返回:' + text + '\n' +
          'document.cookie:' + (document.cookie || '(空)');
      };
    });
  </script>
</body>
</html>

关键行与易错点

  • document.cookie 只能读到非 httpOnly 的 cookie;address 设了 httpOnly,所以这里永远看不到它,但 Network 面板的请求头 Cookie 里一定带着它。
  • 页面必须和后端同源(同协议 + 同域名 + 同端口)才能共用一套 cookie;跨域 fetch 需要 credentials: 'include' 且后端配置 CORS。
  • 同一个网站只存“一套” cookie,登录第二个账号会挤掉第一个——想同时登两个账号只能开两个浏览器。
  • 验证流程固定是三步:点“设置” → 看 Network 的响应头 Set-Cookie → 点“读取”,看请求头 Cookie 是否带过去。

常见错误 ​

  • 现象:调用 res.cookie() 报 not a function。→ 原因:cookie-parser 未安装、未引入,或包名拼成 cookie-parser 少写 r。→ 修法:npm i cookie-parser,const cookieParser = require('cookie-parser'),并在路由前 app.use(cookieParser())。
  • 现象:req.cookies 是 undefined,或读取不到刚设置的 cookie。→ 原因:中间件未注册、注册在路由之后,或读取时误用 res.cookies。→ 修法:确保 app.use(cookieParser()) 在所有相关路由之前;读取统一用 req.cookies。
  • 现象:改了代码后浏览器仍不带 cookie / 看不到新响应头。→ 原因:Node 服务未重启,仍跑旧代码。→ 修法:开发用 nodemon,或每次改完手动重启;排查时先看 Network 的请求头/响应头是否真的变化。
  • 现象:maxAge 设成 3600,以为是一小时,结果很快失效或时间显示“对不上”。→ 原因:服务端 maxAge 单位是毫秒;DevTools 里常显示秒/UTC 时间。→ 修法:写 3600 * 1000;调试时同时看 Expires / Max-Age,不要按本地时区硬猜。
  • 现象:127.0.0.1 能拿到登录态,换 localhost 就没了。→ 原因:cookie 按主机/域名隔离,默认 domain 是设置时的访问主机。→ 修法:开发环境统一入口,固定用同一个 host + port;不要一会儿 127.0.0.1 一会儿 localhost。
  • 现象:前端 document.cookie 读不到 session cookie。→ 原因:配置了 httpOnly: true。→ 修法:登录态通常保持 httpOnly: true,前端不要依赖读取;确需前端读取时再显式设为 false,并评估 XSS 风险。
  • 现象:express-session 设置后能读到,服务重启后登录态全丢。→ 原因:默认 MemoryStore 存在内存中,重启即清空;也可能只访问了读取路由,没先执行写入 session 的路由。→ 修法:开发阶段接受重启失效,先访问写入路由再测读取;生产环境换持久化 store。
  • 现象:删除 cookie / 注销 session 后仍能读到旧状态。→ 原因:clearCookie 的名称、path、domain 与设置时不匹配;或只删了 session 字段,没有真正销毁会话。→ 修法:clearCookie 与设置时同名同 path/domain;注销用 req.session.destroy(cb),删单个字段用 delete req.session.xxx。
  • 现象:express-session 启动报 secret required 或配置不生效。→ 原因:初始化配置对象缺 secret,或 app.use(session(...)) 写法/位置错误。→ 修法:在路由前注册 app.use(session({ secret: '...', ... }));密钥用环境变量管理,不要提交真实密钥。

练习 ​

  1. 用 express + cookie-parser 实现 /set-cookie、/get-cookie、/del-cookie 三个路由:设置一个有效期 1 小时、httpOnly: true 的 cookie,并分别用 DevTools 的 Network 与 Application 验证设置、读取、删除结果。
  2. 给出一个最小复现:/get 路由里用 res.cookies 取不到值。请定位问题并改到可运行,要求说明中间件顺序、req/res 使用分别错在哪。
  3. 用 express-session 实现 /login、/me、/logout:/login 写入 req.session.user = { id, name },/me 读取并返回用户信息,未登录返回 401,/logout 销毁会话。要求重启服务前后各测一次并记录差异。
  4. 做一组对照实验:分别用 127.0.0.1 与 localhost 访问 /set-cookie 和 /get-cookie,记录 cookie 是否共享;再把 httpOnly 分别设为 true/false,观察 document.cookie 的结果,并写出开发环境统一访问入口的方案。
  5. 设计一份上线前检查清单:至少覆盖 cookie 的 httpOnly、secure、sameSite、maxAge,session store、secret 管理、注销逻辑与域名/路径一致性。

原视频对照 ​

#视频时长
1会话控制介绍06:14
2cookie实现原理16:18
3cookie使用演示37:38
4cookie的缺点02:37
5session介绍05:33
6session实现原理10:04
7session使用演示26:06

小结 ​

  • 会话控制排错先看“中间件是否注册、是否在路由前”,cookie-parser 和 express-session 的顺序错误是最常见根因。
  • 调 cookie 不要靠猜:同时看 Network 的请求头/响应头和 Application 面板;maxAge 的毫秒单位与 DevTools 显示时区要分清。
  • 开发环境必须统一 host + port,混用 127.0.0.1 与 localhost 会制造“登录态莫名丢失”的假故障。
  • httpOnly 应作为登录态 cookie 的安全默认;前端业务不要依赖 document.cookie 读取登录凭证。
  • express-session 默认 MemoryStore 只适合开发,重启即失效;生产必须换持久化 store 并规范管理 secret。
  • 删除/注销要成对检查名称、path、domain;注销会话用 destroy,删单个字段用 delete req.session.xxx。
  • 登录态判断以服务端 session/token 校验为准,前端路由守卫只负责体验层,不能替代后端鉴权。
  • 上线前按安全清单过一遍:httpOnly、secure、sameSite、有效期、store、密钥、注销与域名路径一致性。