JWT的构成
JWT即JSON Web Token,由三部分组成:头部(Header)、载荷(Payload)和签名(Signature),它们用 . 分隔,形如eyhhhhhhh.eyppppp.ssssss
Header:包含令牌的类型(JWT)和签名算法
Payload:包含声明(用户id、角色等)和一些元数据(比如过期时间)
Signature:通过Header和Payload结合一个密钥(secret)生成的签名,用于验证令牌的完整性和真实性
JWT的鉴权流程
用户登陆时输入账号密码 => 服务端验证后生成JWT返回给客户端 => 客户端将JWT储存到本地(或cookie)中,在后续请求时将其加入到请求头(如Authorization: Bearer token
)=> 服务器在每次用户请求时验证JWT的有效性,如果有效则允许正常访问受保护的资源
常见用法
比如nodejs中jsonwebtoken
//npm install jsonwebtoken
import jwt from 'jsonwebtoken'
jwt.sign(payload, secretOrPrivateKey, [options, callback])
参数说明
- payload:要加密到 token 里的数据内容(负载)
String|buffer|object
- secretOrPrivateKey:用于签名的密钥(对称加密)或私钥(非对称加密)
String|buffer|object
- options:设置 token 的一些选项,比如过期时间、算法等。是一个对象如
{ expiresIn: '1h', algorithm: 'HS256' }
- callback:是一个函数,如果使用回调函数sign会变成异步,回调参数为
(err, token)
//同步用法
const token = jwt.sign({ userId: 123 }, 'mySecret', { expiresIn: '1h' });
//异步用法
jwt.sign({ userId: 123 }, 'mySecret', { expiresIn: '1h' }, function(err, token) {
// 处理 token 或错误
});
payload
, secretOrPrivateKey
是必须的参数。
简单例子
import express from 'express';
import jwt from 'jsonwebtoken'
const app = express();
app.use(express.json());
// 模拟用户数据库
const users = [
{ id: 1, username: 'admin', password: '123456' }
];
// JWT 密钥(生产环境中应使用环境变量存储)
const SECRET_KEY = 'my-secret-key';
// 登录路由 - 生成 JWT
app.post('/login', (req, res) => {
const { username, password } = req.body;
// 查找用户
const user = users.find(u => u.username === username && u.password === password);
if (!user) {
return res.status(401).json({ message: 'Invalid credentials' });
}
// 生成 JWT
const token = jwt.sign(
{ userId: user.id, username: user.username }, // Payload
SECRET_KEY, // 密钥
{ expiresIn: '1h' } // 令牌过期时间
);
res.json({ token });
});
// 中间件 - 验证 JWT
const authenticateToken = (req, res, next) => {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1]; // 提取 Bearer 后面的 token
if (!token) {
return res.status(401).json({ message: 'Authentication token missing' });
}
try {
// 验证 token
const decoded = jwt.verify(token, SECRET_KEY);
req.user = decoded; // 将解码后的用户信息附加到请求对象
next(); // 继续处理请求
} catch (error) {
return res.status(403).json({ message: 'Invalid or expired token' });
}
};
// 受保护的路由 - 需要 JWT 验证
app.get('/protected', authenticateToken, (req, res) => {
res.json({ message: 'Protected data', user: req.user });
});
// 启动服务器
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});
使用post方式在请求体中添加内容获取token
http://localhost:3000/login
{ "username": "admin", "password": "123456" }
- 将token添加到Header后使用get方法访问保护目录进行验证
http://localhost:3000/protected
{
"message": "Protected data",
"user": {
"userId": 1,
"username": "admin",
"iat": 1747386742,
"exp": 1747390342
}
}