前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
  • 基础篇

    • 环境搭建
    • node部署
    • 基础应用
  • 内置模块

    • 1.0 本地路径处理 path
    • 2.0 文件系统操作 fs
    • 3.1 基础调试 console
    • 3.2 本地调试远程服务器上的Node代码
    • 4.1 网络服务 http
    • 4.2 网络服务 http res
    • 4.3 网络服务 http req
    • 4.4 网络服务 http server
    • 4.5 网络服务 http client
    • 4.6 网络服务 https
    • 4.7 网络TCP net
    • 4.8 网络UDP dgram
    • 4.9 域名解析 dns
    • 5.0 网络地址解析 url
    • 5.1 URL查询字符串 querystring
    • 6.1 流操作 stream
    • 6.2 逐行读取 readline
    • 7.1 进程相关 process
    • 7.2 子进程 child
    • 8.1 二进制数据 buffer
    • 8.2 二进制解码 string_decoder
    • 9.1 事件机制 events
    • 9.2 实用工具模块 util
    • 9.3 数据加密 crypto
    • 9.4 MD5入门介绍及crypto模块的应用
    • 9.4 资源压缩 zlib
    • 9.5 集群 cluster
    • 9.6 v8
  • 进阶篇

    • 5分钟入门非对称加密用法
    • cluster
    • cookie_parser深入
    • crypto模块之理论篇
    • express+cookie_parser签名机制深入剖析
    • express+session实现简易身份认证
    • https
    • log4js入门实例
    • node8_napi
    • 使用 async 控制并发
    • 使用 eventproxy 控制并发
    • 使用 superagent 与 cheerio 完成简单爬虫
    • 基于express+muter的文件上传
    • 将图片转成datauri嵌入到html
    • 常用中间件 body_parser 实现解析
    • 日志模块morgan
    • 服务端字符编解码&乱码处理 charset_enc_dec
    • 测试用例mocha,should,istanbul
    • 调试日志打印debug模块
  • Express

    • 1.1 Express概览篇
    • 1.2 Express
  • Koa2

    • 1.0 koa2概览篇
    • 1.1 快速开始
    • 1.2 async await使用
    • 1.3 koa2简析结构
    • 1.4 koa中间件开发与使用
    • 10.1 单元测试
    • 11.1 开发debug
    • 12.1 快速启动
    • 12.2 框架设计
    • 12.3 分层操作
    • 12.4 数据库设计
    • 12.5 路由设计
    • 12.6 webpack4环境搭建
    • 12.7 使用react
    • 12.8 登录注册功能实现
    • 12.9 session登录态判断处理
    • 13.1 import export使用
    • 2.1 原生koa2实现路由
    • 2.2 koa router中间
    • 3.1 GET请求数据获取
    • 3.2 POST请求数据获取
    • 3.3 koa bodyparser中间件
    • 4.1 原生koa2实现静态资源服务器
    • 4.2 koa static中间件
    • 5.1 koa2使用cookie
    • 5.2 koa2实现session
    • 6.1 koa2加载模板引擎
    • 6.2 ejs模板引擎
    • 7.1 busboy模块
    • 7.2 上传文件简单实现
    • 7.3 异步上传图片实现
    • 8.1 mysql模块
    • 8.2 async await封装使用mysql
    • 8.3 项目建表初始化
    • 9.1 原生koa2实现JSONP
    • 9.2 koa jsonp中间件
  • 其他

    • 操作数据库
    • Session 与 Token
    • Cookie、Session、Token、JWT
    • Socket

# koa2实现session

30 秒速记

  • Session = 浏览器只拿一个 sessionId,用户数据存在服务端,按 id 查
  • 存哪:单进程 demo 可以放内存,多实例 / 要扛重启就放 Redis / MySQL 这类共享存储
  • 登录成功后换一个新的 sessionId,防止会话固定攻击
  • 存储里的 TTL 和 Cookie 的过期时间对齐,滑动过期还是固定过期要想清楚
  • 存储挂了就当没登录,宁可让用户重新登录也不能放行

Session 就像存包处:浏览器只拿一张号码牌(sessionId),东西都存在服务端。 每次请求带着号码牌来,服务端凭号码去存储里取出用户信息。Koa 本身不带 session,要用中间件,比如官方的 koa-session 或者 koa-session-minimal。存储选型看部署:单进程 demo 放内存没问题,但进程一重启全员掉线,多实例时请求落到另一台机器也查不到,所以生产一般用 Redis。还有两个安全细节:登录成功要换新的 sessionId,存储读失败要按未登录处理。

理解「koa2实现session」时,要先明确输入、运行阶段和可观察输出。这里的交互实验会分别执行正常路径与失败路径,并把 Node.js 的关键状态变化和同屏代码对应起来;不能只记命令名称。

工程中还要验证退出码、资源清理和异常输入。属于 security 场景的配置应保持最小可复现,并在开发环境与 CI 中使用相同命令复跑,避免把平台差异或隐式默认值带到线上。

Session 和 Cookie 经常被一起问,其实是两个层面的东西:Cookie 是浏览器端的存储和自动携带机制,Session 是服务端维护的会话状态。最常见的组合是用 Cookie 运 sessionId,用服务端存储放数据。

用官方的 koa-session 接 Redis,核心代码就这么几行:

const Koa = require('koa')
const session = require('koa-session')
const Redis = require('ioredis')

const redis = new Redis()
const app = new Koa()
app.keys = [process.env.SESSION_SECRET]

app.use(session({
  key: 'sid',
  maxAge: 30 * 60 * 1000,   // 毫秒
  rolling: true,            // 每次请求都续期
  httpOnly: true,
  sameSite: 'lax',
  store: {                  // 只要实现 get / set / destroy 三个方法
    async get(key) { const v = await redis.get(key); return v && JSON.parse(v) },
    async set(key, sess, maxAge) { await redis.set(key, JSON.stringify(sess), 'PX', maxAge) },
    async destroy(key) { await redis.del(key) }
  }
}, app))

app.use(async (ctx) => {
  ctx.session.views = (ctx.session.views || 0) + 1
  ctx.body = `第 ${ctx.session.views} 次访问`
})
app.listen(3000)
@前端进阶之旅: 代码已经复制到剪贴板

注意 set 里用 PX 把 Redis 的过期时间和 maxAge 对齐,不然要么 Cookie 还在数据没了,要么数据永远堆在 Redis 里。退出登录写 ctx.session = null,中间件会调 destroy 把服务端记录也删掉。

面试官追问

追问 1服务部署了 4 个实例,用户刷新几下就随机掉登录,最可能是什么原因?
参考回答

session 存在各个进程的内存里,请求被负载均衡到别的实例就查不到。换成 Redis 这类共享存储;靠负载均衡做粘性会话只是绕开问题,实例重启照样丢。

追问 2什么是会话固定攻击,怎么防?
参考回答

攻击者先拿到一个合法的 sessionId,想办法让受害者用这个 id 登录,登录后攻击者手里的 id 就成了已登录状态。防法很简单:登录成功后生成新的 sessionId,旧的作废。

追问 3Session 设了 30 分钟过期,用户一直在操作也会被踢出,怎么改?
参考回答

那是固定过期。要「操作就续期」得开滑动过期,koa-session 里是 rolling: true 或 renew: true。安全要求高的系统会再加一个绝对上限,比如最多 12 小时必须重新登录。

追问 4Redis 抖了一下,读 session 报错,中间件该放行还是拒绝?
参考回答

拒绝,当成未登录处理,同时打日志和告警。读失败就放行等于把鉴权关了,这种漏洞比用户重新登录严重得多。

追问 5koa-session 不配 store,数据存在哪?
参考回答

默认存在 Cookie 里,把整个 session 对象序列化后签名写给浏览器,所以叫 Cookie Session。好处是服务端无状态,坏处是有 4KB 上限、内容用户可见,而且服务端没法主动让它失效。

# koa2实现session

# 前言

30 秒速记

  • Koa 只给了 ctx.cookies,session 要自己实现或用中间件
  • 数据量小、单进程:直接放进程内存,简单但重启就丢
  • 数据量大或多实例:放 MySQL / Redis 这类存储介质
  • Cookie 存用户信息 ≠ session,前者数据在客户端,后者在服务端

Koa 的核心很精简,只提供了读写 Cookie 的能力,session 得自己做或者找中间件。 方案按数据放哪分两类:数据少、就一个进程,可以直接存内存,一个 Map 就能跑;数据多或者要部署多台机器,就得放到 MySQL、Redis 这种外部存储里。内存方案的硬伤是进程重启全员掉线、多实例之间不共享,所以我只在本地 demo 里用。生产环境基本都是 Redis,读写快,还自带过期。

koa2原生功能只提供了cookie的操作,但是没有提供session操作。session就只用自己实现或者通过第三方中间件实现。在koa2中实现session的方案有一下几种

  • 如果session数据量很小,可以直接存在内存中
  • 如果session数据量很大,则需要存储介质存放session数据

面试官追问

追问 1登录后把用户信息 JSON 直接写进 Cookie,这算实现了 session 吗?
参考回答

不算,这是把数据存在客户端,用户能看能改,还受 4KB 限制。session 的意思是数据在服务端,Cookie 里只放一个查数据用的 id。

追问 2用一个 Map 存 session,跑了几天内存一直涨,为什么?
参考回答

过期的会话没人清理,每个新访客都塞一条进去。内存方案要自己定时扫过期项,这也是生产直接用 Redis 的原因之一,它的 TTL 到点自动删。

追问 3线上发版重启后所有用户都要重新登录,Cookie 明明还在,问题在哪?
参考回答

session 存在进程内存里,重启就清空了,浏览器带着旧 sessionId 来也查不到。换成外部存储就解决了。

追问 4session 存 MySQL 还是 Redis?
参考回答

一般选 Redis:每个请求都要读一次 session,Redis 读写是亚毫秒级,还有原生 TTL。MySQL 能用,但要自己清过期数据,而且会话读写会挤占业务库的连接。

# 数据库存储方案

30 秒速记

  • 流程:登录时生成 sessionId → 数据写进 MySQL → sessionId 写进 Cookie → 之后每个请求凭 Cookie 查库
  • koa-session-minimal 负责会话流程和 ctx.session,koa-mysql-session 负责具体的 MySQL 读写
  • 业务代码只读写 ctx.session,不碰 SQL
  • 好处是多实例共享、重启不丢;代价是每个请求多一次查库,库挂了全员掉登录
  • 过期数据要定期清理,量大时换 Redis 更合适

数据库方案就是把 session 数据存进 MySQL,浏览器的 Cookie 里只放 sessionId。 分工上 koa-session-minimal 管流程:读 Cookie 里的 id、在请求开始时把数据装到 ctx.session、结束时把改动写回去;koa-mysql-session 只负责实现存储接口,把数据存到表里。业务代码只需要操作 ctx.session,完全不用写 SQL。这样多台机器读同一个库,会话就共享了。代价是每个请求都多一次数据库查询,所以访问量大了我会换成 Redis。

  • 将session存放在MySQL数据库中
  • 需要用到中间件
    • koa-session-minimal 适用于koa2 的session中间件,提供存储介质的读写接口 。
    • koa-mysql-session 为koa-session-minimal中间件提供MySQL数据库的session数据读写操作。
    • 将sessionId和对应的数据存到数据库
  • 将数据库的存储的sessionId存到页面的cookie中
  • 根据cookie的sessionId去获取对于的session信息

面试官追问

追问 1Cookie 里有 sessionId
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
  • 基础篇

    • 环境搭建
    • node部署
    • 基础应用
  • 内置模块

    • 1.0 本地路径处理 path
    • 2.0 文件系统操作 fs
    • 3.1 基础调试 console
    • 3.2 本地调试远程服务器上的Node代码
    • 4.1 网络服务 http
    • 4.2 网络服务 http res
    • 4.3 网络服务 http req
    • 4.4 网络服务 http server
    • 4.5 网络服务 http client
    • 4.6 网络服务 https
    • 4.7 网络TCP net
    • 4.8 网络UDP dgram
    • 4.9 域名解析 dns
    • 5.0 网络地址解析 url
    • 5.1 URL查询字符串 querystring
    • 6.1 流操作 stream
    • 6.2 逐行读取 readline
    • 7.1 进程相关 process
    • 7.2 子进程 child
    • 8.1 二进制数据 buffer
    • 8.2 二进制解码 string_decoder
    • 9.1 事件机制 events
    • 9.2 实用工具模块 util
    • 9.3 数据加密 crypto
    • 9.4 MD5入门介绍及crypto模块的应用
    • 9.4 资源压缩 zlib
    • 9.5 集群 cluster
    • 9.6 v8
  • 进阶篇

    • 5分钟入门非对称加密用法
    • cluster
    • cookie_parser深入
    • crypto模块之理论篇
    • express+cookie_parser签名机制深入剖析
    • express+session实现简易身份认证
    • https
    • log4js入门实例
    • node8_napi
    • 使用 async 控制并发
    • 使用 eventproxy 控制并发
    • 使用 superagent 与 cheerio 完成简单爬虫
    • 基于express+muter的文件上传
    • 将图片转成datauri嵌入到html
    • 常用中间件 body_parser 实现解析
    • 日志模块morgan
    • 服务端字符编解码&乱码处理 charset_enc_dec
    • 测试用例mocha,should,istanbul
    • 调试日志打印debug模块
  • Express

    • 1.1 Express概览篇
    • 1.2 Express
  • Koa2

    • 1.0 koa2概览篇
    • 1.1 快速开始
    • 1.2 async await使用
    • 1.3 koa2简析结构
    • 1.4 koa中间件开发与使用
    • 10.1 单元测试
    • 11.1 开发debug
    • 12.1 快速启动
    • 12.2 框架设计
    • 12.3 分层操作
    • 12.4 数据库设计
    • 12.5 路由设计
    • 12.6 webpack4环境搭建
    • 12.7 使用react
    • 12.8 登录注册功能实现
    • 12.9 session登录态判断处理
    • 13.1 import export使用
    • 2.1 原生koa2实现路由
    • 2.2 koa router中间
    • 3.1 GET请求数据获取
    • 3.2 POST请求数据获取
    • 3.3 koa bodyparser中间件
    • 4.1 原生koa2实现静态资源服务器
    • 4.2 koa static中间件
    • 5.1 koa2使用cookie
    • 5.2 koa2实现session
    • 6.1 koa2加载模板引擎
    • 6.2 ejs模板引擎
    • 7.1 busboy模块
    • 7.2 上传文件简单实现
    • 7.3 异步上传图片实现
    • 8.1 mysql模块
    • 8.2 async await封装使用mysql
    • 8.3 项目建表初始化
    • 9.1 原生koa2实现JSONP
    • 9.2 koa jsonp中间件
  • 其他

    • 操作数据库
    • Session 与 Token
    • Cookie、Session、Token、JWT
    • Socket
完整面试题地址:
作者:程序员poetry
扫码关注作者公众号:「前端进阶之旅」 每天分享技术干货
前端进阶之旅公众号二维码

koa2实现session|Nodejs专题系列

# koa2实现session

30 秒速记

  • Session = 浏览器只拿一个 sessionId,用户数据存在服务端,按 id 查
  • 存哪:单进程 demo 可以放内存,多实例 / 要扛重启就放 Redis / MySQL 这类共享存储
  • 登录成功后换一个新的 sessionId,防止会话固定攻击
  • 存储里的 TTL 和 Cookie 的过期时间对齐,滑动过期还是固定过期要想清楚
  • 存储挂了就当没登录,宁可让用户重新登录也不能放行

Session 就像存包处:浏览器只拿一张号码牌(sessionId),东西都存在服务端。 每次请求带着号码牌来,服务端凭号码去存储里取出用户信息。Koa 本身不带 session,要用中间件,比如官方的 koa-session 或者 koa-session-minimal。存储选型看部署:单进程 demo 放内存没问题,但进程一重启全员掉线,多实例时请求落到另一台机器也查不到,所以生产一般用 Redis。还有两个安全细节:登录成功要换新的 sessionId,存储读失败要按未登录处理。

理解「koa2实现session」时,要先明确输入、运行阶段和可观察输出。这里的交互实验会分别执行正常路径与失败路径,并把 Node.js 的关键状态变化和同屏代码对应起来;不能只记命令名称。

工程中还要验证退出码、资源清理和异常输入。属于 security 场景的配置应保持最小可复现,并在开发环境与 CI 中使用相同命令复跑,避免把平台差异或隐式默认值带到线上。

Session 和 Cookie 经常被一起问,其实是两个层面的东西:Cookie 是浏览器端的存储和自动携带机制,Session 是服务端维护的会话状态。最常见的组合是用 Cookie 运 sessionId,用服务端存储放数据。

用官方的 koa-session 接 Redis,核心代码就这么几行:

const Koa = require('koa')
const session = require('koa-session')
const Redis = require('ioredis')

const redis = new Redis()
const app = new Koa()
app.keys = [process.env.SESSION_SECRET]

app.use(session({
  key: 'sid',
  maxAge: 30 * 60 * 1000,   // 毫秒
  rolling: true,            // 每次请求都续期
  httpOnly: true,
  sameSite: 'lax',
  store: {                  // 只要实现 get / set / destroy 三个方法
    async get(key) { const v = await redis.get(key); return v && JSON.parse(v) },
    async set(key, sess, maxAge) { await redis.set(key, JSON.stringify(sess), 'PX', maxAge) },
    async destroy(key) { await redis.del(key) }
  }
}, app))

app.use(async (ctx) => {
  ctx.session.views = (ctx.session.views || 0) + 1
  ctx.body = `第 ${ctx.session.views} 次访问`
})
app.listen(3000)
@前端进阶之旅: 代码已经复制到剪贴板

注意 set 里用 PX 把 Redis 的过期时间和 maxAge 对齐,不然要么 Cookie 还在数据没了,要么数据永远堆在 Redis 里。退出登录写 ctx.session = null,中间件会调 destroy 把服务端记录也删掉。

面试官追问

追问 1服务部署了 4 个实例,用户刷新几下就随机掉登录,最可能是什么原因?
参考回答

session 存在各个进程的内存里,请求被负载均衡到别的实例就查不到。换成 Redis 这类共享存储;靠负载均衡做粘性会话只是绕开问题,实例重启照样丢。

追问 2什么是会话固定攻击,怎么防?
参考回答

攻击者先拿到一个合法的 sessionId,想办法让受害者用这个 id 登录,登录后攻击者手里的 id 就成了已登录状态。防法很简单:登录成功后生成新的 sessionId,旧的作废。

追问 3Session 设了 30 分钟过期,用户一直在操作也会被踢出,怎么改?
参考回答

那是固定过期。要「操作就续期」得开滑动过期,koa-session 里是 rolling: true 或 renew: true。安全要求高的系统会再加一个绝对上限,比如最多 12 小时必须重新登录。

追问 4Redis 抖了一下,读 session 报错,中间件该放行还是拒绝?
参考回答

拒绝,当成未登录处理,同时打日志和告警。读失败就放行等于把鉴权关了,这种漏洞比用户重新登录严重得多。

追问 5koa-session 不配 store,数据存在哪?
参考回答

默认存在 Cookie 里,把整个 session 对象序列化后签名写给浏览器,所以叫 Cookie Session。好处是服务端无状态,坏处是有 4KB 上限、内容用户可见,而且服务端没法主动让它失效。

# koa2实现session

# 前言

30 秒速记

  • Koa 只给了 ctx.cookies,session 要自己实现或用中间件
  • 数据量小、单进程:直接放进程内存,简单但重启就丢
  • 数据量大或多实例:放 MySQL / Redis 这类存储介质
  • Cookie 存用户信息 ≠ session,前者数据在客户端,后者在服务端

Koa 的核心很精简,只提供了读写 Cookie 的能力,session 得自己做或者找中间件。 方案按数据放哪分两类:数据少、就一个进程,可以直接存内存,一个 Map 就能跑;数据多或者要部署多台机器,就得放到 MySQL、Redis 这种外部存储里。内存方案的硬伤是进程重启全员掉线、多实例之间不共享,所以我只在本地 demo 里用。生产环境基本都是 Redis,读写快,还自带过期。

koa2原生功能只提供了cookie的操作,但是没有提供session操作。session就只用自己实现或者通过第三方中间件实现。在koa2中实现session的方案有一下几种

  • 如果session数据量很小,可以直接存在内存中
  • 如果session数据量很大,则需要存储介质存放session数据

面试官追问

追问 1登录后把用户信息 JSON 直接写进 Cookie,这算实现了 session 吗?
参考回答

不算,这是把数据存在客户端,用户能看能改,还受 4KB 限制。session 的意思是数据在服务端,Cookie 里只放一个查数据用的 id。

追问 2用一个 Map 存 session,跑了几天内存一直涨,为什么?
参考回答

过期的会话没人清理,每个新访客都塞一条进去。内存方案要自己定时扫过期项,这也是生产直接用 Redis 的原因之一,它的 TTL 到点自动删。

追问 3线上发版重启后所有用户都要重新登录,Cookie 明明还在,问题在哪?
参考回答

session 存在进程内存里,重启就清空了,浏览器带着旧 sessionId 来也查不到。换成外部存储就解决了。

追问 4session 存 MySQL 还是 Redis?
参考回答

一般选 Redis:每个请求都要读一次 session,Redis 读写是亚毫秒级,还有原生 TTL。MySQL 能用,但要自己清过期数据,而且会话读写会挤占业务库的连接。

# 数据库存储方案

30 秒速记

  • 流程:登录时生成 sessionId → 数据写进 MySQL → sessionId 写进 Cookie → 之后每个请求凭 Cookie 查库
  • koa-session-minimal 负责会话流程和 ctx.session,koa-mysql-session 负责具体的 MySQL 读写
  • 业务代码只读写 ctx.session,不碰 SQL
  • 好处是多实例共享、重启不丢;代价是每个请求多一次查库,库挂了全员掉登录
  • 过期数据要定期清理,量大时换 Redis 更合适

数据库方案就是把 session 数据存进 MySQL,浏览器的 Cookie 里只放 sessionId。 分工上 koa-session-minimal 管流程:读 Cookie 里的 id、在请求开始时把数据装到 ctx.session、结束时把改动写回去;koa-mysql-session 只负责实现存储接口,把数据存到表里。业务代码只需要操作 ctx.session,完全不用写 SQL。这样多台机器读同一个库,会话就共享了。代价是每个请求都多一次数据库查询,所以访问量大了我会换成 Redis。

  • 将session存放在MySQL数据库中
  • 需要用到中间件
    • koa-session-minimal 适用于koa2 的session中间件,提供存储介质的读写接口 。
    • koa-mysql-session 为koa-session-minimal中间件提供MySQL数据库的session数据读写操作。
    • 将sessionId和对应的数据存到数据库
  • 将数据库的存储的sessionId存到页面的cookie中
  • 根据cookie的sessionId去获取对于的session信息

面试官追问

追问 1Cookie 里有 sessionId,但用户还是未登录状态,你怎么一段段查?
参考回答

先看请求头里带的 id 是不是你以为的那个,再去会话表里查这个 id 在不在、过没过期,最后看中间件连的是不是同一个库。三段任何一段断了都会表现成未登录。

追问 2能不能业务代码里自己写 SQL 去改会话表?
参考回答

不建议。会话数据的序列化格式和过期字段是 koa-mysql-session 定的,业务绕过它直接改,容易写坏格式。要改会话就改 ctx.session,要踢人就按 sessionId 调存储的删除接口。

追问 3会话表越来越大,查询变慢了,怎么办?
参考回答

先确认过期记录有没有被清理,没有就加定时任务按过期时间删。再看 id 字段有没有主键或索引。如果单纯是请求量大,把会话迁到 Redis。

追问 4想让用户在后台被管理员强制下线,这个方案好做吗?
参考回答

好做,这正是服务端会话的优势:删掉会话表里这个用户对应的记录,他下次请求就查不到了。前提是能按用户找到 sessionId,所以要额外维护一个「用户 → 会话」的索引。

# 快速使用

30 秒速记

  • 示例在 daily-code-practice 仓库的 demo/session/index.js,依赖 koa、koa-session-minimal、koa-mysql-session
  • 跑之前先准备好 MySQL,建好 koa_demo 库,账号密码按本机改
  • 验证顺序:先 /set 写入,再 / 读取,看 count 是否递增
  • 这是演示链路用的样例,Cookie 配置是空的,不能原样上生产

快速上手就是把仓库里的 session 示例拉下来跑一遍,亲眼看到「写入 → 带 Cookie 回来 → 读出」这条链路。 跑之前要先有一个能连的 MySQL,库名、账号密码和代码里对上,不然服务一启动连库就报错。验证时别直接打开首页,先访问 /set 创建会话,再访问 /,count 每刷新一次加一就说明会话保持住了。这个样例只是演示用,Cookie 选项全是空字符串,拿去上线前要补齐。

demo源码

https://github.com/poetries/daily-code-practice/tree/master/node/koa/koa2-demo/blob/master/demo/session/index.js

← 5.1 koa2使用cookie6.1 koa2加载模板引擎 →

fe