认证、授权、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辅助下完成。

相关推荐
xieliyu.5 小时前
Java算法精讲:双指针(二)
java·开发语言·算法
wayz115 小时前
Momentum:PSL(心理线指标)技术指标详解
算法·金融·数据分析·量化交易·特征工程
8Qi86 小时前
LeetCode 213:打家劫舍 II(House Robber II)—— 题解 ✅
算法·leetcode·职场和发展·动态规划
三品吉他手会点灯6 小时前
C语言学习笔记 - 44.运算符和表达式 - 运算符2 - 除法与取余运算符
c语言·开发语言·笔记·算法
乐迪信息7 小时前
乐迪信息:AI算法盒子实时识别船舶烟雾与火焰异常
大数据·人工智能·算法·安全·目标跟踪
J-Tony117 小时前
【JVM】根可达算法
jvm·算法
艾iYYY7 小时前
string 类的模拟实现
android·服务器·c语言·c++·算法
Lsk_Smion7 小时前
力扣实训 _ [75].颜色分类 _ 杨辉三角
数据结构·算法·leetcode
jidaowansui8 小时前
P11375 [GESP202412 六级] 树上游走
数据结构·算法