feng xiaohan

JWT

json web token。实现身份验证或授权,也就是鉴权。(常用于用户登录后需要存储用户信息)

有点类似 cookie 和 session。

原理

在服务端生成一个加密的令牌(token),将用户的信息封装到这个令牌中;前端每一次发送请求时去携带这个 token,服务端就可以将这个 token 进行解码,读取用户的信息,这样就达到了一个身份验证和鉴权。

组成

JWT 由 . 分割为三个部分:

  • 头部(Header):一般由令牌的类型和使用的签名算法组成(一般不用去管),是一个 json 对象。
  • 负载(Payload):包含需要传输的信息(用户身份、权限、令牌发布者等),也是一个 json 对象,使用 Base64 进行编码。
  • 验证签名(Verify Signature):通过指定算法对 Header 和 Payload 生成数字签名。

实现

后端(express)

  • 安装所需依赖
pnpm i express cors jsonwebtoken

cors:用于解决跨域。

import express from "express";
import jwt from "jsonwebtoken";
import cors from "cors";

let Key = "SVCSD"; // 加验私钥,一般藏在环境变量里
const app = express();
app.use(express.json()); // 中间件支持json
app.use(express.urlencoded({ extended: false }));
app.use(cors()); // 中间件解决跨域

let user = {
  name: "admin",
  password: "123456",
  id: 1,
};

// 1.登录返回前端token用于授权
app.post("/api/login", (req, res) => {
  if (req.body.name == user.name && req.body.password == user.password) {
    res.json({
      message: "登录成功",
      token: jwt.sign({ id: user.id }, Key, { expiresIn: "1h" }), // payload,私钥,token配置对象(如 token 过期时间)
    });
  } else {
    res.status(403).json({ message: "用户名或密码错误" });
  }
});

// 2.列表接口,只有授权状态才能访问,不然就会403
app.post("/api/list", (req, res) => {
  let token = req.headers.authorization; // 前端 token 存放在请求头的 authorization 里,规范
  jwt.verify(token, Key, (err, decode) => {
    // 使用 jwt 去验证 token 是否正确
    if (err) {
      // token 没有权限按规范返回403
      res.status(403).json({ message: "无权限" });
    } else {
      res.json({
        list: [
          { id: 1, title: "标题1" },
          { id: 2, title: "标题2" },
        ],
      });
    }
  });
});

app.listen(3000, () => {
  console.log("server started");
});

前端(fetch)

btn.onclick = () => {
  fetch("http://localhost:3000/api/login", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      name: name.value,
      password: password.value,
    }),
  })
    .then((res) => res.json())
    .then((res) => {
      cnosole.log(res);
      localStorage.setItem("token", res.token);
    });
};
fetch("http://localhost:3000/api/list", {
  headers: {
    Authorization: localStorage.getItem("token"),
    // "Authorization": `Bearer ${localStorage.getItem("token")}`
  },
})
  .then((res) => res.json())
  .then((res) => {
    cnosole.log(res);
  });

注意:Bearer 字段不是一定要加的,它是一个规范,只要看见 token 前面加了这个东西,就说明它使用了 OAuth 2.0 规范(jwt 也使用了该规范)。加了后端也需要去处理这个 Bearer