1、JWT的理解
JWT 的组成部分: 分别是 Header(头部)、Payload(有效荷载)、Signature(签名) 三者之间使用英文的"."分隔,
Pyload 部分才是真正的用户信息,他是用户信息经过加密之后生成的字符串
Header 和 Signature 是 安全性相关的部分,只是为了保证 Token 的安全性
使用方式:
客户端收到服务器返回的 JWT 之后,通常会将它储存在 localStorage 或 sessionStorage 中。
此后,客户端每次与服务器通信,都要带上这个 JWT 的字符串,从而进行身份认证。推荐的做法是把 JWT 放在 HTTP 请求头的 Authorization 字段中,格式如下:
Authorization:Bearer <token>
2、安装
jsonwebtoken :生成JWT字符串
express-jwt :用于将JWT字符串解析还原成JSON对象
npm install jsonwebtoken express-jwt
3、使用
3.1导入
// 导入用于生成JWT字符串的包
const jwt = require("jsonwebtoken");
// 导入用户客户端发送过来的JWT字符串,解析还原成JSON对象的包
const { expressjwt } = require("express-jwt");
3.2定义密钥
为了保证 JWT 字符串的安全性,防止 JWT 字符串在网络传输过程中被别人破解,我们需要专门定义一个用于加密和解密
的 secret 密钥:
① 当生成 JWT 字符串的时候,需要使用 secret 密钥对用户的信息进行加密,最终得到加密好的 JWT 字符串
② 当把 JWT 字符串解析还原成 JSON 对象的时候,需要使用 secret 密钥进行解密
//定义secret密钥,建议将密钥命名为secretKey
const secretKey = "jwt NO1"; // 自定义
3.3 解析jwt 注册中间件
// 将JWT字符串还原为json对象
// expressjwt({ secret: secretKey, algorithms: ["HS256"] }) 用来解析token的中间件
// .unless({ path: [/^\/api\//] }) 用来指定哪些接口不需要访问权限
// 注意:只要配置成功了 express-jwt 这个中间件,就可以把解析出来的用户信息,挂在到req.auth上
app.use(
expressjwt({ secret: secretKey, algorithms: ["HS256"] }).unless({
path: [/^\/api\//],
})
);
3.3 使用
// 用户登陆成功之后,生成JWT字符串,通过token属性响应给客户端
// 调用jwt.sign() 生成jwt字符串,三个参数分别是:用户信息对象,加密密钥,配置对象
const tokenStr = jwt.sign({ username: userinfo.username }, secretKey, {
expiresIn: "30s",
});
4、捕获解析失败的错误
// 使用全局错误处理中间件,捕获解析JWT失败后产生的错误
app.use((err, req, res, next) => {
if (err.name === "UnauthorizedError") {
return res.send({
status: 401,
message: "无效的token",
});
}
res.send({
status: 500,
message: "未知的错误",
});
});
全部代码
const express = require("express");
// 导入用于生成JWT字符串的包
const jwt = require("jsonwebtoken");
// 导入用户客户端发送过来的JWT字符串,解析还原成JSON对象的包
const { expressjwt } = require("express-jwt");
const app = express();
const port = 3000;
//1、 解析post表单数据的中间件
app.use(express.urlencoded({ extended: false }));
app.use(express.json());
// 2、定义secret密钥,建议将密钥命名为secretKey
const secretKey = "jwt NO1"; // 自定义
// 3、将JWT字符串还原为json对象
// expressjwt({ secret: secretKey, algorithms: ["HS256"] }) 用来解析token的中间件
// .unless({ path: [/^\/api\//] }) 用来指定哪些接口不需要访问权限
// 注意:只要配置成功了 express-jwt 这个中间件,就可以把解析出来的用户信息,挂在到req.auth上
app.use(
expressjwt({ secret: secretKey, algorithms: ["HS256"] }).unless({
path: [/^\/api\//],
})
);
app.post("/api/login", (req, res) => {
const userinfo = req.body;
if (userinfo.username !== "admin" || userinfo.password != "000000") {
return res.send({
status: 400,
message: "登录失败",
});
}
// 用户登陆成功之后,生成JWT字符串,通过token属性响应给客户端
// 调用jwt.sign() 生成jwt字符串,三个参数分别是:用户信息对象,加密密钥,配置对象
// 注意:千万不要把密码写入token中
const tokenStr = jwt.sign({ username: userinfo.username }, secretKey, {
expiresIn: "120s",
});
res.send({
status: 200,
msg: "登录成功",
token: tokenStr,
});
});
// 前端请求需要在请求头带上Authorization:Bearer 获得的token
app.post("/getUserInfo", (req, res) => {
res.send({
status: 200,
data: req.auth,
});
});
// 使用全局错误处理中间件,捕获解析JWT失败后产生的错误
app.use((err, req, res, next) => {
if (err.name === "UnauthorizedError") {
return res.send({
status: 401,
message: "无效的token",
});
}
res.send({
status: 500,
message: "未知的错误",
});
});
app.listen(port, () => console.log(`Example app listening on port ${port}!`));