JWT 认证
基于令牌的安全信息交换标准。
JWT 认证
这一课你将能做到
- 理解 JWT 的结构和签名原理
- 用 Node.js 实现登录签发 JWT、接口鉴权
- 处理 Token 过期、刷新、黑名单
为什么要学
HTTP 是无状态的,服务器不知道每次请求是不是同一个人。传统方案用 Session + Cookie,但分布式部署时 Session 同步麻烦。JWT(JSON Web Token)把用户信息加密后放 Token 里,服务器验证签名即可,不用查数据库,天然支持分布式。
人话:JWT 是「电子门票」,用户登录后拿到,之后每次请求出示门票,服务器验票放行。
先建立图像
JWT 结构(三部分,用 . 分隔):
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjEsImlhdCI6MTY0MjAwMDAwMCwiZXhwIjoxNjQyMDAzNjAwfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Header.Payload.Signature
Header: {"alg":"HS256","typ":"JWT"} → Base64 编码
Payload: {"userId":1,"iat":1642000000,"exp":1642003600} → Base64 编码
Signature: HMACSHA256(Header.Payload, secret) → 签名
跟着做
步骤 1:安装依赖
npm install jsonwebtoken bcrypt
步骤 2:用户注册与登录
const jwt = require('jsonwebtoken');
const bcrypt = require('bcrypt');
const SECRET = process.env.JWT_SECRET || 'your-secret-key';
// 注册
app.post('/api/register', async (req, res) => {
const { username, password } = req.body;
// 密码哈希(不要明文存储)
const passwordHash = await bcrypt.hash(password, 10);
const result = await pool.query(
'INSERT INTO users (username, password_hash) VALUES ($1, $2) RETURNING id, username',
[username, passwordHash]
);
res.status(201).json({ code: 0, data: result.rows[0] });
});
// 登录
app.post('/api/login', async (req, res) => {
const { username, password } = req.body;
// 查用户
const result = await pool.query(
'SELECT * FROM users WHERE username = $1', [username]
);
const user = result.rows[0];
if (!user) {
return res.status(401).json({ code: 401, msg: '用户名或密码错误' });
}
// 校验密码
const match = await bcrypt.compare(password, user.password_hash);
if (!match) {
return res.status(401).json({ code: 401, msg: '用户名或密码错误' });
}
// 签发 JWT
const token = jwt.sign(
{ userId: user.id, username: user.username },
SECRET,
{ expiresIn: '2h' } // 2 小时过期
);
res.json({ code: 0, data: { token, user: { id: user.id, username: user.username } } });
});
步骤 3:鉴权中间件
// 中间件:验证 Token
function authMiddleware(req, res, next) {
// 从 Header 取 Token
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return res.status(401).json({ code: 401, msg: '未登录' });
}
const token = authHeader.substring(7); // 去掉 "Bearer "
try {
const payload = jwt.verify(token, SECRET);
req.user = payload; // 把用户信息挂到 req 上
next();
} catch (err) {
if (err.name === 'TokenExpiredError') {
return res.status(401).json({ code: 401, msg: 'Token 已过期' });
}
return res.status(401).json({ code: 401, msg: 'Token 无效' });
}
}
// 使用中间件
app.get('/api/profile', authMiddleware, async (req, res) => {
// req.user 就是 JWT 的 payload
const { userId } = req.user;
const result = await pool.query(
'SELECT id, username, email, avatar FROM users WHERE id = $1',
[userId]
);
res.json({ code: 0, data: result.rows[0] });
});