1、JWT 是什么
JWT = JSON Web Token 一段轻量、自包含、防篡改 的字符串,用于在前后端 / 服务之间安全传递用户身份信息。
核心特点
- 无状态、不用存 Session服务端不需要在内存 / Redis 存用户会话,所有信息都在 token 里。
- 跨域 / 分布式友好不依赖 Cookie,天然适合前后端分离、微服务、多域名场景。
- 前端存储,每次请求带上 一般存在 localStorage /sessionStorage/ Cookie,通过 axios 拦截器在请求头(如
Authorization: Bearer <token>)传给后端。 - 内容 Base64 编码,可解码,不加密
- 载荷(payload)任何人都能看见
- 不能存密码等敏感信息
- 签名防篡改服务端用密钥签名,只要密钥不泄露,token 就无法伪造 / 篡改。
前端在axios/index统一配置的请求拦截器:
javascript
service.interceptors.request.use(
function (config) {
console.log("axios拦截器发送请求")
let userStr = sessionStorage.getItem('cur_user');
if(userStr){
let user = JSON.parse(userStr);
config.headers.Authorization = `Bearer ` + user.token;
}
return config;
}
)
2. JWT 由三部分组成(固定结构)
bash
Header.Payload.Signature
① Header(头)
- 说明算法:
HS256 - 说明类型:
JWT - 公开、不加密
② Payload(载荷)
- 存用户信息:
_id、username、role、exp... - 公开、不加密
- ⚠ 不能放密码!
③ Signature(签名)
- 由
Header + Payload + 密钥加密生成 - 防篡改
- 后端靠它验证 token 是否有效
JSON Web Tokens - jwt.io
https://www.jwt.io/

3. JWT 编码 / 解码
编码(后端生成 token)
javascript
const token = jwt.sign(user, 'abc123', { expiresIn: '3000h' })
→ 把用户信息变成一段 token 字符串。
解码(后端验证 + 解析)
javascript
expressjwt({ secret: "abc123", algorithms: ['HS256'] })
→ 自动解析出用户信息 → 放到**req.auth(鉴权需用到)**
任何人都能解码看内容,但只有密钥能生成合法签名。
user.js中login接口生成jwt凭证
javascript
/*
制作JWT不可篡改凭证
*/
const token = jwt.sign(
user,//用户信息对象
'abc123',//密钥
{ expiresIn: '3000h' } //有效期
);
user.token = token;//把token字段加到user对象中
return res.json({
code: 2000,
msg: '登录成功',
data: user
});
app.js中的JWT 身份验证 + 全局异常统一处理中间件**:注意全局异常统一处理中间件要写在最后面,否则无法捕获异常。全局错误处理中间件,永远放在所有中间件的最后一个!**
javascript
//jwt验证
app.use(
expressjwt({
secret: "abc123",
algorithms: ['HS256'] // 指定签名算法
}).unless({
path: ["/login",
"/register"//restful风格的网址不是唯一的,要配合method一起明确
// {url:"/api/v1/users",methods:['POST']},
// {url:"/api/v1/reports/favcount",methods:['GET']},
] // 指定不需要验证的路由 登录和注册接口不需要验证
})
);
//统一处理异常的中间件
app.use((err, req, res, next) => {
console.log("err的内容",err)
if (err.name === 'UnauthorizedError') {
let message = '无效的令牌';
console.log("err.inner",err.inner.message)
// 检查内部错误信息(可选)
if (err.inner && err.inner.message === 'jwt expired') {
message = '令牌已过期';
} else if (err.inner && err.inner.message === 'invalid signature') {
message = '令牌签名无效';
}
// JWT 验证失败
return res.status(401).json({ code: 4001, msg: message,data:null });
} else {
// 其他错误
return res.status(500).json({ code: 5000, msg: '服务器内部错误:'+err.message,data:null });
}
});
4. JWT 核心:token 和 req.auth._id
① token = 身份凭证(进门卡)
-
前端存在
localStorage / sessionStorage -
请求时放在请求头:
Authorization: Bearer token -
作用:让后端知道你是登录用户
② req.auth._id = 当前登录用户 ID(钥匙)
- JWT 验证通过后自动挂载(只在后端可见,前端拿不到)
- 来源:token 里的 payload
- 作用:权限判断、数据归属
前端传来的 user._id 不可信,涉及权限与数据归属的操作,必须使用后端从 JWT 解析出的可信用户信息。
5.初学踩的坑
初学 JWT 时容易踩坑:误以为用户登录后获取 token,请求拦截器正常携带 token 并通过后端验证就足够安全。但实际上,token 仅用于身份校验,并不直接控制操作权限。如果后端直接信任前端传入的用户 ID,很容易出现被他人冒充身份、越权操作数据的安全问题。
我在 Postman 中使用张三的账号登录获取 token,然后在李书记的发布意见接口中,将请求头的 token 替换为张三的,并在请求体里把用户信息改成李书记的,最终成功发送了请求。可见确实有越权操作风险