✍ 前情回顾
上回书我们说到,JWT 是一种现代身份认证利器,前端登录后拿到一个看似神秘的 token,后端只需通过解密它就能识别你是谁。
但有同学可能已经开始头秃了:
“老师,JWT 登录流程我大概懂了,但它到底怎么防止伪造?”
“那个 secret 是干嘛的?是不是要加密加盐啥的?”
别急,今天我们就来揭开 JWT 登录背后那些“不说你真不知道”的底层奥义。
🧭 JWT 登录鉴权流程全景图
我们先来回顾下典型的 JWT 登录认证流程,配合一点脑补画面更容易理解。
✅ 登录阶段(签发阶段)
-
前端发出登录请求(账号 + 密码):
POST /login body: { username: 'admin', password: '123456' } -
后端验证用户合法后,生成并返回 Token:
const token = jwt.sign({ userId: 42 }, SECRET_KEY, { expiresIn: '2h' })✅ 关键点:
jwt.sign()用SECRET_KEY进行签名(加密验证) -
前端收到 token 并保存(localStorage / cookie)
✅ 鉴权阶段(每次访问接口)
-
前端请求接口,附带 token:
Authorization: Bearer xxxxx.token.signature -
后端解析 token:
const decoded = jwt.verify(token, SECRET_KEY) -
若校验通过,继续执行接口逻辑;否则返回 401 Unauthorized
🍯 Token 放哪儿?就在 HTTP 请求头里!
很多同学问:
“我把 Token 放在 localStorage 了,那后端怎么知道我是谁?”
答案是:你要主动把它贴在请求头里!
GET /api/user
Authorization: Bearer <your-jwt-token>
✅ Authorization 是什么?
HTTP 请求头中的 Authorization 字段,是专门用来放身份凭证的标准位置。
对于 JWT,一般格式是:
Authorization: Bearer xxxxx.yyyyy.zzzzz
🧠 Bearer(持有者)表示你“持有这个 Token”,就等于持有了访问接口的权利。
🧪 示例代码:如何发送带 Token 的请求(前端角度)
以 fetch 举例:
const token = localStorage.getItem('token')
fetch('/api/protected', {
headers: {
'Authorization': 'Bearer ' + token
}
})
也可以封装 Axios 请求拦截器:
axios.interceptors.request.use((config) => {
const token = localStorage.getItem('token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
})
🔐 Secret Key 到底是什么?它凭什么能校验身份?
我们生成 JWT 的时候会写个 secret,比如这样:
jwt.sign(payload, 'my-secret', { expiresIn: '1h' })
它到底有什么用?答案是:
✅ 它是签名的核心,用于生成 Signature,也用于验证 token 是否被“伪造”或“篡改”。
🧪 举个例子:
JWT 的最后一段是签名部分,如下:
Header.Payload.Signature
Signature 就是这样生成的:
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret
)
如果有人想自己伪造一个 Token,他必须知道你的 secret,才能生成合法的签名,否则 jwt.verify() 立刻识破!
🧂 什么是“加盐”?它和 Secret 有关系吗?
“加盐”这个词原本来自密码学领域,意思是:
在明文或哈希前后加上一些不可预测的随机字符,使破解更难。
在 JWT 的上下文里,“加盐”主要体现在:
-
设置一个复杂、不可预测的 secret key
✅ 这就像是在签名过程中“加了盐”,避免别人穷举或猜测 -
不要用简单字符串当 secret!
// ❌ 不推荐 const SECRET_KEY = '123456' // ✅ 推荐 const SECRET_KEY = 'fU8Yw@#1DL9x!$as@_)&jdkla92@!KLSJD' -
可选:定期轮换 secret key 并让 token 快速过期
更加安全,但也更复杂(需要配合 refresh token)
💡 进阶一问:JWT 是不是“加密”的?
这个问题常被误解!
- ❌ JWT 并不是“加密”了用户信息
- ✅ 它只是“签名 + Base64 编码”
所以,JWT 的 Payload 是可以被任何人解码的:
const payload = JSON.parse(atob(token.split('.')[1]))
所以不要把敏感数据(如密码、银行卡号)放进 Payload!
🧪 示例代码:带复杂 secret 的 JWT 登录逻辑
const express = require('express')
const jwt = require('jsonwebtoken')
const app = express()
const SECRET_KEY = process.env.JWT_SECRET || 'fU8Yw@#1DL9x!$as@_)&jdkla92@!KLSJD'
// 登录接口
app.post('/login', (req, res) => {
const { username, password } = req.body
// 假装数据库验证通过
if (username === 'admin' && password === '123456') {
const token = jwt.sign({ username }, SECRET_KEY, { expiresIn: '2h' })
res.json({ token })
} else {
res.status(401).json({ msg: '用户名或密码错误' })
}
})
// 鉴权接口
app.get('/protected', (req, res) => {
const auth = req.headers.authorization
if (!auth) return res.status(401).json({ msg: '缺少 token' })
const token = auth.split(' ')[1]
try {
const decoded = jwt.verify(token, SECRET_KEY)
res.json({ msg: '通过认证', user: decoded })
} catch (e) {
res.status(403).json({ msg: '无效或过期 token' })
}
})
app.listen(3000, () => console.log('Server running...'))
🧠 总结:Token 是一把锁,Secret 是它的钥匙
JWT 是前端送给后端的一份自我介绍,贴在 Authorization 头上;而 Secret Key 则是后端验明真身的那把钥匙,“加盐”的目的,是让这把钥匙更坚固。它们各司其职,在请求的千军万马中,挑出你这个“持证者”,给你通行的绿灯,也拒绝假冒的黑客。“Token + Secret + Authorization” 这三者,构成了现代 Web 鉴权最坚实的铁三角。
如果你喜欢本系列文章,欢迎点赞收藏关注,你的三连就是我继续更新的最大动力 🧡