认证、授权、JWT、密码哈希:Node.js 鉴权到底在做什么

认证、授权、JWT、密码哈希:Node.js 鉴权到底在做什么

很多人第一次做登录系统时,都会遇到同一个问题:

"我已经拿到用户名和密码了,接下来到底该怎么设计?"

如果把这件事说清楚,其实就四步:

  1. 用户注册时,把密码安全地存起来
  2. 用户登录时,校验密码是否正确
  3. 登录成功后,发一个身份凭证给客户端
  4. 后续请求里,检查这个凭证是否有效

这篇先不急着写代码,先把整个链路讲明白。因为只要概念清楚了,后面用 bcryptjsjose 写实现就会很顺。


1. 先分清三个词:认证、授权、鉴权

这三个词经常被混着说,但它们不是一回事。

认证(Authentication)

回答的是:"你是谁?"

比如:

  • 用户输入账号密码登录
  • 服务端判断这个人是不是这个账号的主人

授权(Authorization)

回答的是:"你能做什么?"

比如:

  • 普通用户只能看自己的订单
  • 管理员才能删除用户

鉴权

中文里很多项目会把"认证 + 授权"统称为"鉴权"。

所以你看到"鉴权系统",通常不是单纯验密码,而是一整套登录和权限控制机制。


2. 一个登录系统最小闭环长什么样

最基本的流程是这样的:

注册

用户输入用户名和密码。

服务端不能直接把密码明文存数据库,而是要做哈希处理后再存。

登录

用户再次输入用户名和密码。

服务端拿用户输入的密码,和数据库里保存的哈希值做比对。

比对通过,说明密码正确。

签发身份凭证

登录成功后,服务端给客户端发一个 token。

这个 token 之后会被客户端带着去访问受保护接口。

访问受保护接口

客户端请求时,把 token 带上。

服务端检查 token 是否有效、是否过期、是否被篡改。

通过后,才允许访问。

这就是一个最小闭环。


3. 为什么密码不能明文存储

这是最重要的一点之一。

如果数据库里直接存的是明文密码,一旦数据库泄漏,后果非常严重。

因为很多人会在多个网站重复使用同一个密码。

所以正确做法不是"加密密码",而是"哈希密码"。

哈希不是加密

两者差别很大:

  • 加密:可以解密回来
  • 哈希:通常不能反推出原文

密码场景里更适合哈希,因为系统根本不需要知道你的原密码是什么,只需要在登录时验证"你输入的密码,和当初那个密码是不是同一个"。

为什么不能只用普通哈希函数

md5sha1 这类算法已经不适合拿来存密码了。

原因很简单:

  • 速度太快,暴力破解成本低
  • 容易被彩虹表攻击
  • 对密码场景缺少足够的抗暴力破解能力

所以密码哈希通常要用专门的慢哈希算法,比如 bcrypt


4. bcryptjs 是做什么的

bcryptjs 的作用很直接:

  • 注册时:把明文密码变成哈希
  • 登录时:把用户输入的密码和哈希结果比对

它的核心价值有两个:

1)加入 salt

salt 可以理解为给每个密码加一段随机扰动。

同样的密码,每次生成的哈希结果也会不同。

这能避免很多常见攻击方式。

2)增加计算成本

bcrypt 是慢哈希。

它故意让计算变慢,目的是提高暴力破解的成本。

这不是缺点,而是设计目标。


5. JWT 是什么

JWT 是 "JSON Web Token"。

它常被用来做登录后的身份凭证。

你可以把它理解成一张"电子通行证":

  • 登录成功后,服务端签发给你
  • 之后你访问受保护接口时,带着它
  • 服务端检查它是不是自己签的、有没有过期

JWT 不是数据库

JWT 本身不是一个会话记录表。

它通常是无状态的:服务端不一定需要保存会话,就能验证这个 token。

JWT 也不是加密串

很多人以为 JWT 是加密后的数据,其实不准确。

JWT 里 payload 部分只是编码过,不是保密的。

任何人都可以把 token 拿去解码看到里面的内容。

真正保证可信的是"签名"。

所以 JWT 的重点是:

  • 你能看见内容
  • 但你不能篡改内容

6. JWT 的结构

一个 JWT 通常由三段组成:

text 复制代码
header.payload.signature

说明算法、类型等信息。

Payload

放业务数据,比如:

  • 用户 id
  • 角色
  • 过期时间相关字段

Signature

签名部分,用来防篡改。

如果有人改了 payload 里的内容,签名就对不上,服务端会直接判定 token 无效。


7. jose 是做什么的

jose 是一个处理 JWT、JWS、JWE 的库。

在常见登录场景里,你最常用到的是:

  • 签发 JWT
  • 校验 JWT

也就是说:

  • bcryptjs 负责"密码"
  • jose 负责"token"

这两者分工非常明确。


8. 一次完整请求链路长什么样

可以把整个过程想成下面这个流程:

第一步:注册

用户提交密码。

服务端:

  • 校验参数
  • bcryptjs 哈希密码
  • 把哈希值存入数据库

第二步:登录

用户提交账号和密码。

服务端:

  • 根据账号查用户
  • 取出数据库里的密码哈希
  • bcryptjs.compare() 比对
  • 通过后,用 jose 签发 JWT

第三步:访问接口

客户端请求接口时,把 JWT 带上。

服务端:

  • 取出 token
  • jose 验签
  • 检查是否过期
  • 通过后把用户信息挂到请求上下文里

第四步:做权限判断

比如:

  • 普通用户只能看自己的资料
  • 管理员才能进后台

这一步才是真正的"授权"。


9. 常见误区

误区 1:JWT 就是安全

不对。JWT 只是一个身份凭证格式。

它是否安全,取决于:

  • 密钥是否足够强
  • 是否正确验签
  • 是否设置过期时间
  • 是否做好密钥管理

误区 2:JWT 是加密的

不对。JWT 默认不是加密,只是签名。

payload 能被解码看到。

误区 3:有了 JWT 就不用管权限

不对。

JWT 只能证明"这个请求带着一个合法 token"。

至于"这个人能不能删用户、能不能进后台",还得单独做权限判断。

误区 4:密码用 md5 就够了

不够。

密码哈希要选适合密码场景的方案,bcrypt 就是常见选择之一。


10. 这套方案里两个库各管什么

可以直接记成一句话:

  • bcryptjs 管密码
  • jose 管 token

再展开一点:

bcryptjs

  • 注册时哈希密码
  • 登录时校验密码

jose

  • 登录成功后签发 JWT
  • 后续请求时验证 JWT

11. 这一篇你应该建立的核心认知

如果只记住三件事,就够了:

  1. 密码不能明文存,必须哈希
  2. JWT 是登录后的身份凭证,不是数据库,不是加密数据
  3. bcryptjsjose 分别解决的是两类不同问题

12. 下一篇该讲什么

接下来最适合单独展开的,是密码哈希这部分。

因为一旦注册和登录的密码处理讲透了,后面再看 JWT 和权限控制就会很自然。

下一篇就可以专门讲:

  • 为什么 bcrypt 适合密码
  • salt 和 cost factor 是什么
  • 注册和登录时分别怎么写
  • 常见坑有哪些

如果你后面要继续,我就按这个系列往下写第二篇。

后记

2026年5月7日于上海,在claude opus 4.6辅助下完成。

相关推荐
~|Bernard|26 分钟前
二.go语言中map的底层原理(2026-5-8)
算法·golang·哈希算法
mask哥38 分钟前
力扣算法java实现汇总整理(下)
java·算法·leetcode
样例过了就是过了1 小时前
LeetCode热题100 编辑距离
数据结构·c++·算法·leetcode·动态规划
wearegogog1231 小时前
MATLAB椭圆参数检测算法实现
数据库·算法·matlab
secondyoung1 小时前
Markdown数学公式语法速查手册
算法·编辑器·markdown·latex
君义_noip1 小时前
CSP-S 2025 提高级 第一轮(初赛) 阅读程序(1)
算法·深度优先·信息学奥赛·初赛
小O的算法实验室1 小时前
2026年IEEE TEVC,知识引导的竞争进化算法用于多解传感器-武器-目标分配问题,深度解析+性能实测
算法·论文复现·智能算法·智能算法改进
khalil10202 小时前
代码随想录算法训练营Day-46 动态规划13 | 647. 回文子串、516.最长回文子序列、动态规划总结
数据结构·c++·算法·leetcode·动态规划·回文子串·回文子序列
学习3人组2 小时前
柔性排产时序算法+中间过程+阶段目标 细化表格
算法·mes
he___H2 小时前
算法快与慢--哈希+双指针
算法·leetcode·哈希算法