【Node】Koa中通过JWT非对称加密生成Token

目录

一. JWT实现Token机制

1. header

2.  payload

3. signature

二. 非对称加密,生成私钥和公钥

三. Koa中通过JWT非对称加密生成Token


一. JWT实现Token机制

 JWT生成的Token由三部分组成

1. header

alg:采用的加密算法,默认是 HMAC SHA256(HS256),采用同一个密钥进行
加密和解密;

typ:JWT,固定值;

会通过base64Url算法进行编码。

2.  payload

携带的数据,比如我们可以将用户的id和name放到payload中;

默认也会携带iat(issued at),令牌的签发时间;

我们也可以设置过期时间:exp(expiration time);

会通过base64Url算法进行编码。

3. signature

设置一个secretKey,通过将前两个的结果合并后进行HMACSHA256的算法;

HMACSHA256(base64Url(header)+.+base64Url(payload), secretKey);

但是如果secretKey暴露是一件非常危险的事情,因为之后就可以模拟颁发token,
也可以解密token; 

二. 非对称加密,生成私钥和公钥

前面我们提到过,HS256加密算法单一密钥暴露就是非常危险的事情,这个时候我们可以使用非对称加密——RS256;

私钥(private key):用于发布令牌;

公钥(public key):用于验证令牌;

通过cmd以管理员身份运行,创建文件夹keys,cd到对应文件夹下,输入openssl,

genrsa -out private.key 1024

生成私钥保存在对应文件夹

rsa -in private.key -pubout -out public.key

 生成公钥保存在对应文件夹

三. Koa中通过JWT非对称加密生成Token

src/controller/user.controller.js

// src/controller/user.controller.js
const jwt = require("jsonwebtoken")
const {
  PRIVATE_KEY
} = require("../app/config")

class UserController {
  async login(ctx, next) {
    const userInfo = ctx.userInfo
    const {
      customer_number,
      customer_phone_number
    } = userInfo

    // 生成签名
    const token = jwt.sign({
      customer_number,
      customer_phone_number
    }, PRIVATE_KEY, {
      expiresIn: 60 * 60 * 24, // 过期时间(单位:s秒)
      algorithm: 'RS256' // 指定算法
    })

    ctx.body = {
      token: `Bearer ${token}`,
      userInfo
    }
  }

  test(ctx) {
    ctx.body = "test"
  }
}

module.exports = new UserController()

重点关注login方法即可~

ctx.userInfo是我在上一个中间件verifyLoginInfo中保存的用户信息,从用户信息中我取出了其中的customer_number用户编号与customer_phone_number用户手机号作为上面提到JWT中的payload;

PRIVATE_KEY是通过上述方法在本地生成的私钥;

expiresIn是过期时间;

algorithm是选择的加密算法;

生成token后保存在ctx.body中传出

以上完成了派发令牌的过程。

PRIVATE_KEY和PUBLIC_KEY获取的过程如下:

src/app/config.js

// src/app/config.js
const dotenv = require("dotenv")
const fs = require("fs")
const path = require("path")

dotenv.config()

const PRIVATE_KEY = fs.readFileSync(path.resolve(__dirname, './keys/private.key'));
const PUBLIC_KEY = fs.readFileSync(path.resolve(__dirname, './keys/public.key'));

module.exports = {
  APP_HOST,
  APP_PORT,
  MYSQL_HOST,
  MYSQL_PORT,
  MYSQL_DATABASE,
  MYSQL_USER,
  MYSQL_PASSWORD
} = process.env

module.exports.PRIVATE_KEY = PRIVATE_KEY
module.exports.PUBLIC_KEY = PUBLIC_KEY

src/middleware/user.middleware.js

// src/middleware/user.middleware.js
const errorType = require("../constants/error-types")
const userService = require("../service/user.service")
const jwt = require("jsonwebtoken")
const { PUBLIC_KEY } = require('../app/config')

const verifyLoginInfo = async(ctx, next) => {
  const loginInfo = ctx.request.body

  const result = await userService.login(loginInfo)
  console.log("verifyLoginInfo", result);

  if (result.length === 0) {
    const error = new Error(errorType.PHONE_OR_PASSWORD_IS_INCORRECT)
    return ctx.app.emit("error", error, ctx)
  }

  ctx.userInfo = result[0]

  next()
}

const verifyAuth = async (ctx, next) => {
  console.log("验证授权的middleware~");

  const authorization = ctx.header.authorization

  if (!authorization) {
    const error = new Error(errorType.NO_AUTHORIZATION)
    return ctx.app.emit("error", error, ctx)
  }
  const token = authorization.replace('Bearer ', '');

  // 倘若jwt的验证失败会自动抛出错误,因此需要通过try-catch包裹
  try {
    // 倘若jwt的验证成功,返回值为用户传入的payload+令牌派发时间+令牌过期时间
    const result = jwt.verify(token, PUBLIC_KEY, {
      algorithms: ["RS256"]
    })
    console.log("JWT payload:", result);
    await next();
  } catch(err) {
    const error = new Error(errorType.AUTHORIZATION_EXPIRED_OR_INCORRECT);
    ctx.app.emit('error', error, ctx);
  }
}

module.exports = {
  verifyLoginInfo,
  verifyAuth
}

重点关注verifyAuth函数即可~

从ctx.header.authorization中获取Authorization授权,再去除开头的"Bearer "即得到token;

传入token,PUBLIC_KEY,对应的加密算法名称进行jwt的验证;

需要注意的是:倘若jwt的验证失败会自动抛出错误,因此需要通过try-catch包裹;

倘若jwt的验证成功,返回值为用户传入的payload+令牌派发时间+令牌过期时间(以秒s为单位的时间戳);

以上完成了验证令牌的过程。

Postman测试

派发令牌

验证令牌

参考资料:深入Node.js技术栈-学习视频教程-腾讯课堂 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值