NodeJS Koa 后端用户会话管理,JWT, Session,长短Token,本文一次性讲明白

#王者杯·14天创作挑战营·第2期#

前言

前几天,我写了一篇文章,《我设计的一个安全的 web 系统用户密码管理流程》。其中着重点是讲的如何利用非对称加密进行安全的设计,并在讲述了原理之后,又写了 《node 后端和浏览器前端,有关 RSA 非对称加密的完整实践, 前后端匹配的代码演示》 这篇文章,着重讲解了有关 RSA 非对称加密的完整实现。

但是,这些文章里并没有讲到用户登录管理的核心,也就是用户会话管理。这里面有很多概念,对于很多前端开发和很多初级后端开发来说,都是很模糊的。所以,今天,我想通过这一篇文章,把这里面的核心点全部讲明白。

基础概念

我们会听到一些词,大概是 SessionSession IDCookiesToken 等等,现在又有了新词儿,新的解决方案,如 JWT,长短 Token 等等。

我们一个一个来讲。这些词分别有对应的英文意思,但是每个词都有多个含义,并且在不同的环境下,还会有衍生不同的含义。因此,我这边只能从前后端开发的角度去解释这些名词,不能按照字典的含义去解释。

而且,由于网上言论众多,其中谬误的也好,佶屈聱牙的也罢,我都不去管它,我只按照我的理解来讲解,并且,我相信,我的理解是非常深入到位的。

Session

翻译——会话,引申为会话管理。那么,什么是会话呢?

首先,我们要理解一个基础概念——http 服务,是一种无状态服务。怎么理解无状态呢?一句话讲,就是——在默认情况下,后端程序根本不知道前端发过来的请求,谁是谁!

为了解决这个不知道谁是谁的问题,就引申出来解决方案——让后端程序知道,每一个请求,都是谁发出来的,好相应的把对应的资源发回去。

比如,张三请求 profile 接口,返回的内容应该 {name:"张三"},而不能是 {name:“李四”}。

那么,我们就可以理解,张三发出来的一系列的请求,应该归属于一个会话,李四发出来的一系列请求,应该归属于另一个会话。

所以,我们就需要把这一系列的会话,进行管理,这就是会话管理

换句话想,Session,并不是一个单纯的词,而是一个相对很大的概念,为了解决这个问题,不同的语言有不同的解决方案,这些各种不同的解决方案,都可以统称为 Session。

Session ID

理解了上面的 Session 的概念,这个概念就相对比较好理解了。每一个用户,都有一堆的资源,我们不能把这些资源都随时在程序里放着。因此,我们会把这些资源打包存着,然后整出一个 ID 来,方便我们来查询。这样,前端只要给我们这个 ID,我们就可以根据这个ID查找到对应的资源。

这里说的用户资源,是指 用户名,用户昵称,用户角色,过期时间等简短信息,只要方便后端程序快速的分清楚这货是谁,能干些啥事儿即可。不是说,要把所有的用户资源信息,全部存着。如果是那样的话,还要数据库干啥呢对吧。

Token

字面意思,凭证,令牌。是后端下发给前端的一段字符串(可长可短,你要愿意,给数字也不是不行,看你咋设计了。)

前端拿到这个 Token 以后,它每次发起请求时,都需要把这个 Token 给带上,后端根据这个 Token ,就可以去查找对应的资源了。

OK,注意到了吗?上面的 Session ID 是方便我们后端去查找资源的,这个 Token,也是后端去查找对应资源的。

所以—— Token = Session ID ?

答案是不一定。要看你咋设计。

一般而言,我是倾向于设计为 Token,就是 Session ID。当然,也可以不一样,前端给后端 Token,后端先根据 Token 去查找 SessionID,然后根据 ID 再查找对应资源,也是可以的对吧?

但我感觉这样设计有点缺心眼儿。

Cookies

字面意思是一种饼干,我不喜欢吃饼干,所以我不知道它具体是哪一种饼干。

Cookies 是用户浏览器的一种机制,它可以由我们的后端程序去设定。而且,在每次用户发起请求的时候,浏览器都会把 Cookies 自动带上。

换句话讲,如果我们使用 Cookies,可以大幅的减少前端开发的工作量,因为具体是咋实现的,不用管了,浏览器自动实现。

但问题是,现在的前端开发,往往不仅仅是运行在浏览器里面的网页,还有 APP、 小程序或者其他客户端程序,而这些里面,大多是没有 Cookies 的机制的。

因此,为了我们的后端服务可以同时服务多端,我建议就不要管这个玩意儿了。

不是不能用,顺手的时候,也可以兼容。

JWT

JSONWebToken 的缩写。在传统的会话管理里,都涉及一个存储的问题,就是,要把用户资源存着,然后去查询。你可以存在内存里,文件里,数据库里,等等,总之,你要存数据,并且方便查询。

JWT

假设,我们存在内存里,而我们的后端服务运行在多台负载均衡的服务器上,那么,当用户的 A 请求访问在甲服务器上,甲服务器在内存里存储了数据,B请求访问在乙服务器上,而乙服务器的内存中并没有用户数据,则会出现读取失败的问题。

怎么解决呢?聪明的你一定想到了解决方案——集中存储,比如单独搞一台 redis 服务器,大家都往这里存。挺好的。

那么假设你的这些服务器不在一个机房呢?也不是不能读是吧,就是慢点儿。

总之,传统的会话管理,有一个问题,就是 IO 问题,也就是存储和读取的问题。

JWT 方案是换了一个思路来解决,其核心原理是,直接将用户的资源信息加密,然后返回给客户端,客户端拿这个加密的长字符串直接作为 Token 来请求,后端程序只要解密字符串,就直接获得了用户的资源信息了。

也就是说, JWT 是用计算复杂度取代了存储复杂度,来解决上面说的问题。

总之,会话管理是一定要消耗资源的,要么是存储复杂度,要么是计算复杂度。


在很多文章和概念里,把 JWT 方案,和 Session 方案并列对比。可以这么比,但是我感觉概念不对。其实在我看来,JWT 方案,只是一种 Session 解决方案而已,两者是上下级的关系,而不是并列的关系。

我这边把传统的通过存储(你别管存哪儿,咋存)解决方案,称之为传统存储方案吧。

和传统存储方案相对比,优缺点如下

特点JWT存储
服务器状态无状态有状态
跨域支持天然支持需CORS配置
实时吊销默认不支持支持
传输效率大点儿,问题不大小,可忽略

其实总结下来,JWT最大的问题就是,一旦 Token 签发,除了超时失效,否则是没办法把用户登出的。

当然,聪明的你,肯定想到了黑名单机制,把登出的 Token 存下来,然后每次读取验证一下。那么我的问题是,那你为啥不用传统的存储会话管理,要用 JWT ?吃饱撑的?

长短 Token

好,通过上面的这些概念,我们知道,JWT 方案,不方便管理用户,传统存储解决方案,有IO性能问题,那么能不能两种方案结合结合,咱们取长补短!

可以,于是,有了长短 Token 的解决方案。

每次用户登录时,用 JWT 方式,签发一个长的 Token,这个 Token 的过期时间比较短,比如5分钟。5分钟后,它就失效了。

同时,用传统存储方案,签一个短的 Token。这个过期时间可以设置得比较长,比如一个月。

在正常业务时,后端直接读取用户传过来的 JWT 信息,就可以得到用户资源了。当前端发现 token 过期了,就拿 短 token 过来,再请求一个长 token,然后再继续使用。

这样,就解决了存储问题,又可以管理用户登出(直接在存储里删掉用户的短Token 即可)

代价是什么呢?代价是提高了各个前端项目的代码复杂度。你可以说,长短 Token 的解决方案是集合了两种解决方案的长处,也可以理解为,两者的短处也全粘上了。

传输数据变大,还是得存储用户信息,还消耗CPU计算资源等等。

小结

综上所述,我们应该对用户会话管理这块的概念都有了一个清晰的理解了。那么我们在开发项目时应该如何选择呢?

首先,无论是哪种解决方案,你都得会!没有什么方案是一招鲜,吃遍天的。

例如,传统存储方案中的所谓 IO 问题,对于绝大多数项目来说,这就是一个不存在的问题。直接往内存一存,有啥性能瓶颈?拢共就跑在一台服务器上,哪里来的什么速度或者进程共享的问题?在我看来,绝大多数项目,连 redis 都是多余。

再说,JWT 方案,有啥登出管理问题?一个小系统,一天登录拢共就几个人,你设置为有效期1天,我也不认为有多大的安全隐患,如果真遇到了什么大问题,直接把 JWT 的签发加密词一改,所有签发的TOKEN,全部失效。

最后说说长短 TOKEN,如果你团队有足够多的人手,项目也确实有很高的安全性,可以考虑使用。否则,是徒增麻烦。

所以,我推荐你根据你自己的项目选择合适的解决方案,我只说我的观点:

  1. 大多数项目都可以使用 JWT 方案。
  2. 你有切实的要迫使用户登出的项目,比如论坛等等,可以用传统的存储方案。
  3. 你团队实力足够,甲方是安全洁癖,项目也确实有高等级的要求,或者产品或老板想装逼忽悠客户,长短TOKEN你值得拥有!

本来,我是想在这篇文章中,引用一些代码,方便各位看官理解的,没想到,光讲概念就讲了这么多了。没办法,周末抽空,我会把每种方案的 NodeJS + Koa 的实战完整代码给写成文章分享给大家。


2025年06月10日 补充:

晚了几天,终于,我还是把所有的代码都写出来,并写成了文章,给大家参考:

《NodeJS Koa 后端用户会话管理,JWT 和 Session 两种方案完整代码演示》
《NodeJS Koa 后端用户会话管理 长短 Token 方案完整代码演示》

同时,为了大家可以实际体验,我还写了一个演示项目,供大家学习参考,仓库地址为:https://github.com/fengcms/session-study-demo

如果你点过去看了我的仓库,给我的项目点一个 Star 哈,谢谢大家!


最近我在参加 CSDN 的一个创作比赛,各位看官在留言区留言可以给我加分。所以希望各位看官帮帮忙,点评一下哦!

当然,我们程序开发都是很忙滴,没时间点评没关系哈,点个赞,收个藏,也不是不可以哈!耽误不了您的事儿,还能让我继续保持创作热情,小可在这边谢谢各位看官了哈!

<think>好的,用户想了解Node.js后端开发的使用教程和最佳实践。我需要根据用户提供的引用内容来整理回答。首先,用户提供的引用1到4提到了Node.js的高性能、非阻塞I/O、模块系统、性能优化、中间件、RESTful API设计、测试调试、监控以及CI/CD等关键点。我需要把这些内容结构化,分步骤解。 首先,应该从Node.js的基础开始,比如安装和环境配置,然后介绍核心特性,如异步编程和模块系统。接下来,框架选择方面,Express是最常用的,引用4也提到了。然后,项目结构的最佳实践,比如MVC模式,引用4中的模板可能涉及代码组织,需要强调可维护性。 路由和中间件的部分,引用1和4都提到了中间件和路由管理,需要详细说明如何使用Express中间件来处理请求。数据库交互方面,引用3提到了数据库交互,可能需要介绍ORM工具如Sequelize或Mongoose。 性能优化是关键,引用1和3都强调了这点,应该包括集群模式、缓存、压缩等策略。认证和授权部分,引用3和4提到,需要讨论JWT或OAuth的实现。错误处理和日志记录也是必要内容,引用3和4有相关部分,比如统一错误响应和日志系统。 测试和调试部分,引用1和4提到了测试框架如Mocha和Jest,以及调试工具。性能监控和CI/CD是部署的重要环节,引用1和4讨论了监控工具和持续集成部署流程。 最后,结合用户提供的引用内容,需要确保每个部分都有对应的引用标注。同时,生成的相关问题要围绕教程中的关键点,比如性能优化、框架选择、项目结构等。检查是否有遗漏的信息,比如非阻塞I/O、事件循环模型,这些在引用2中也有提到,需要整合进去。 现在需要确保回答结构清晰,分步骤说明,并且自然地插入引用标记。比如在异步编程时,引用2提到非阻塞I/O,可以标注[^2]。在Express框架时,引用4提到项目结构,标注[^4]。性能优化部分引用1和3的相关内容。最后的问题要覆盖教程中的各个主要部分,帮助用户深入探索。</think>### Node.js 后端开发教程与最佳实践 #### 一、基础准备 1. **安装与环境配置** - 安装Node.js(推荐使用LTS版本)并配置npm/yarn包管理器。 - 使用`nvm`(Node Version Manager)管理多版本环境[^4]。 2. **核心特性** - **异步非阻塞I/O**:通过事件循环处理高并发请求,避免线程阻塞[^2][^3]。 - **模块系统**:使用CommonJS或ES模块化规范组织代码[^1]。 #### 二、框架选择与项目结构 1. **主流框架** - **Express**:轻量灵活,适合快速构建RESTful API。 - **Koa/Nest.js**:提供更现代的中间件机制或面向企业级的TypeScript支持[^2]。 2. **项目结构规范** - 按功能分层(如MVC模式): ``` src/ ├── controllers/ # 路由处理逻辑 ├── models/ # 数据库模型 ├── routes/ # 路由定义 ├── middleware/ # 自定义中间件 └── utils/ # 工具函数 ``` - 遵循模块化原则,提高代码可维护性。 #### 三、核心开发实践 1. **路由与中间件** - 使用Express定义路由: ```javascript const express = require('express'); const app = express(); app.get('/api/users', (req, res) => { /* 处理逻辑 */ }); ``` - 中间件应用:日志记录、请求解析(如`body-parser`)、跨域处理(CORS)。 2. **数据库交互** - **ORM/ODM工具**:Sequelize(SQL)、Mongoose(MongoDB)简化数据库操作。 - 连接池配置优化查询性能。 3. **认证与授权** - 使用JWT(JSON Web Token)实现无状态认证。 - 结合Passport.js支持多策略(如OAuth2.0)。 #### 四、性能优化策略 1. **代码层面** - 避免同步操作(如`fs.readFileSync`),优先使用异步/Promise。 - 使用缓存(Redis)减少重复计算或数据库查询[^1]。 2. **部署优化** - **集群模式**:利用多核CPU启动多个进程(`cluster`模块)。 - **反向代理**:通过Nginx负载均衡和静态资源托管。 #### 五、测试与监控 1. **单元测试** - 使用Mocha/Jest编写测试用例,确保核心逻辑正确性。 - 示例: ```javascript test('用户注册接口', async () => { const response = await request(app).post('/api/register').send({username: 'test'}); expect(response.statusCode).toBe(201); }); ``` 2. **性能监控** - 集成PM2监控进程状态和内存占用。 - 使用New Relic或Prometheus收集性能指标[^1]。 #### 六、持续集成与部署(CI/CD) 1. 配置GitHub Actions或Jenkins自动化流程: - 代码提交后触发测试。 - 通过Docker容器化部署至云服务器(如AWS/Heroku)[^1]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值