linwu-hi / code-interview

前端面试小册,包含Vue面试题,React面试题,JS面试题,HTTP面试题,工程化面试题,CSS面试题,算法面试题,大厂面试题,高频面试题
194 stars 23 forks source link

面试官:如何实现jwt鉴权机制?说说你的思路 #163

Open linwu-hi opened 1 year ago

linwu-hi commented 1 year ago

面试官:如何实现jwt鉴权机制?说说你的思路

一、JWT简介

JSON Web Token(JWT)是一种用于在用户和服务器之间传递安全可靠信息的字符串书写规范。它由三部分组成:头部(Header)、载荷(Payload)、签名(Signature),并以.进行拼接。JWT在前后端分离的开发中常用于身份验证。

头部(Header)

头部指定使用的加密算法,一般使用HMAC SHA256。头部信息经过Base64编码,例如:

{ "alg": "HS256", "typ": "JWT" }

编码后为:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

载荷(Payload)

载荷用于存放实际的内容,例如用户的ID和名称,还可以设置过期时间等信息。载荷信息也经过Base64编码,例如:

{
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022
}

编码后为:

eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ

签名(Signature)

签名是对头部和载荷进行签名,使用秘钥对数据进行加密。签名可以确保数据在传输过程中没有被篡改。

二、实现JWT鉴权机制

1. 生成Token

使用第三方库jsonwebtoken来生成JWT Token。在用户登录成功后,颁发Token作为后续接口访问的凭证。

const crypto = require("crypto");
const jwt = require("jsonwebtoken");

// 用户列表(应该使用数据库存储,这里仅作演示)
let userList = [];

class UserController {
  static async login(ctx) {
    const data = ctx.request.body;
    if (!data.name || !data.password) {
      return (ctx.body = {
        code: "000002",
        message: "参数不合法",
      });
    }
    const result = userList.find(
      (item) =>
        item.name === data.name &&
        item.password === crypto.createHash("md5").update(data.password).digest("hex")
    );
    if (result) {
      const token = jwt.sign(
        {
          name: result.name,
        },
        "test_token", // 密钥
        { expiresIn: 60 * 60 } // 过期时间:60 * 60秒
      );
      return (ctx.body = {
        code: "0",
        message: "登录成功",
        data: {
          token,
        },
      });
    } else {
      return (ctx.body = {
        code: "000002",
        message: "用户名或密码错误",
      });
    }
  }
}

前端接收到Token后,通常会使用localStorage进行缓存,并将Token放在HTTP请求头的Authorization字段中。注意在Authorization字段中加上Bearer前缀,后面跟一个空格。

axios.interceptors.request.use((config) => {
  const token = localStorage.getItem("token");
  config.headers.common["Authorization"] = "Bearer " + token; // 注意这里的Authorization
  return config;
});

2. 校验Token

使用koa-jwt中间件进行Token验证,配置白名单(接口白名单不需要校验)。

const koajwt = require("koa-jwt");

app.use(
  koajwt({
    secret: "test_token", // 密钥,与签发时保持一致
  }).unless({
    path: [/\/api\/register/, /\/api\/login/], // 接口白名单
  })
);

通过中间件校验后,可以在后续路由中获取用户信息。

router.get("/api/userInfo", async (ctx, next) => {
  const authorization = ctx.header.authorization;
  const token = authorization.replace("Bearer ", "");
  const result = jwt.verify(token, "test_token");
  ctx.body = result;
});

三、优缺点

优点:

缺点:

参考文献