验证流程
用户向服务器发送用户名和密码。
服务器验证通过后,生成一个令牌(token)。里面存放着相关数据,比如用户角色、登录时间等。
服务器向用户返回这个令牌(token)。需要用户自己选择某种方式保存起来,一般可以使用
localStorage、sessionStorage、Cookie 等。
用户随后的每一次请求,需要将令牌(token)通过请求头携带到服务器。请求头字段名由后端指定,一般叫 Authorization。
服务器收到令牌(token),通过解析就能得知用户的身份。
JWT的组成
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.
TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
一个JWT实际上就是一个字符串,它由三部分组成:
头部(Header)
载荷(Payload)
签名(signature).
把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用"点"(.)分隔,就可以返回给用户。
JWT的特点
JWT 默认是不加密,但也是可以加密的。生成原始 Token 以后,可以用密钥再加密一次。
JWT 不加密的情况下,不能将秘密数据写入 JWT。
JWT 不仅可以用于认证,也可以用于交换信息。有效使用 JWT,可以降低服务器查询数据库的次数。
JWT 的最大缺点是,由于服务器不保存 session 状态,因此无法在使用过程中废止某个 token,或者更改 token 的权限。也就是说,一旦 JWT 签发了,在到期之前就会始终有效,除非服务器部署额外的逻辑。
JWT 本身包含了认证信息,一旦泄露,任何人都可以获得该令牌的所有权限。为了减少盗用,JWT 的有效期应该设置得比较短。对于一些比较重要的权限,使用时应该再次对用户进行认证。
为了减少盗用,JWT 不应该使用 HTTP 协议明码传输,要使用 HTTPS 协议传输。
示例(使用postman)
准备
项目结构如下:
my_project
│
├── server.js # Node.js 服务器代码
├── package.json # 项目配置文件,包含依赖和脚本
└── node_modules/ # npm 安装的包文件夹
在目标项目下,运行如下命令
# 初始化新的node.js项目
npm init -y
# 安装Express和JSON Web Token (JWT)库
npm install express body-parser jsonwebtoken
创建服务器 server.js文件,运行
node server.js
const express = require('express');
const jwt = require('jsonwebtoken');
const bodyParser = require('body-parser');
const app = express();
const secretKey = 'your_secret_key';
app.use(bodyParser.json());
// 登录路由
app.post('/api/login', (req, res) => {
const { username, password } = req.body;
// 验证用户名和密码(这里简化为直接通过)
if (username === 'admin' && password === 'password') {
const token = jwt.sign({ username, role: 'admin' }, secretKey, { expiresIn: '1h' });
res.json({ token });
} else {
res.status(401).send('Invalid credentials');
}
});
// 数据获取路由
app.get('/api/data', (req, res) => {
const token = req.headers['authorization'].split(' ')[1];
if (!token) {
return res.status(401).send('No token provided');
}
jwt.verify(token, secretKey, (err, decoded) => {
if (err) {
return res.status(401).send('Invalid token');
}
// 返回一些数据
res.json({ message: 'This is protected data', user: decoded });
});
});
app.listen(3000, () => {
console.log('Server running on port 3000');
});
postman测试
打开Postman,选择POST请求。
输入URL:http://localhost:3000/api/login
在Body选项卡中,选择raw并选择JSON格式。
输入如下:
{
"username": "admin",
"password": "password"
}
点击“Send”按钮发送请求。
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwicm9sZSI6ImFkbWluIiwiaWF0IjoxNzE4NTA4Mjc5LCJleHAiOjE3MTg1MTE4Nzl9.IIpxMPo4FtQOwpRXXOOKYUoza6u9vKWEFPGC5HvBgJI"
}
方法:GET
URL: http://localhost:3000/api/data
Headers:
Key: Authorization
Value: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwicm9sZSI6ImFkbWluIiwiaWF0IjoxNzE4NTA4Mjc5LCJleHAiOjE3MTg1MTE4Nzl9.IIpxMPo4FtQOwpRXXOOKYUoza6u9vKWEFPGC5HvBgJI(将其替换为实际令牌)
{
"message": "This is protected data",
"user": {
"username": "admin",
"role": "admin",
"iat": 1718508279,
"exp": 1718511879
}
}