JWT笔记

5 阅读3分钟
  • JSON Wen token,用于身份验证和身份交换
  • 三部分组成:
    • Header (头部)

      • 头部通常有两部分组成:令牌的类型(JWT)和 所使用签名的算法(如 HMAC SHA256 或 RSA)。
      {
        "alg": "HS256",
        "typ": "JWT"
      }
      
      
    • Payload (有效载荷)

      • 有效载荷部分包含声明(claims),即存储在令牌中的信息。声明可以是注册的(如 issexpsub 等)、公共的或私有的。
      {
        "sub": "1234567890",
        "name": "John Doe",
        "admin": true,
        "iat": 1516239022
      }
      
    • Signature (签名)

      • 为了保证令牌未被篡改,需要使用头部中指定的算法对编码后的头部和有效载荷进行签名。使用的密钥是由服务端保管的
      HMACSHA256(
        base64UrlEncode(header) + "." +
        base64UrlEncode(payload),
        your-256-bit-secret)
      
  • JWT的工作流程
    1. 用户登录:用户通过登录表单提交验证(如用户名和密码)
    2. 生产JWT: 服务端言验证用户凭证后生成JWT,并将其发送给客户端(token)
    3. 客户端存储JWT : 客户端可以将JWT存储在本地存储或者Cookie中
    4. 请求保护资源:随后的请求中,客户端将JWT发送到服务器(通常在HTTP头的Authorization中)
    5. 服务端验证JWT: 服务器验证JWT有效性

JWT 的优点

  • 自包含:JWT 包含了所有用户信息,避免了多次查询数据库。
  • 跨域:JWT 可以在不同的域间使用,非常适合微服务架构。
  • 无状态:服务器不需要保存会话信息,减轻了服务器的负担。

JWT 的缺点

  • 安全性:如果密钥泄露,攻击者可以伪造有效的 JWT。使用 HTTPS 保护传输非常重要。
  • 有效性:一旦生成,JWT 在到期前无法被撤销,因此需要合理设置有效期。

———————————————————————————————————————

代码例子:

// ***************生成token****************************
// 【实际上就是对uid进行加密】
const generateToken = function (uid, scope) {
  // scope: 用户权限
  const secretKey = global.config.security.secretKey
  const expiresIn = global.config.security.expiresIn
  //   接收三个参数:payload,secret,options
  //   payload:要加密的数据
  //   secret:密钥
  //   options:配置项(密钥过期时间等)
  const token = jwt.sign(
    {
      // 要加密的数据:用户id,权限
      uid,
      scope,
    },
    secretKey,
    {
      expiresIn,
    },
  )

  return token
}
    
// ***********************解析token ***********************
const basicAuth = require('basic-auth') // 用于处理HTTP Basic认证,从请求中提取用户名和密码
const jwt = require('jsonwebtoken')
class Auth {
  constructor(level) {
    // level是接口的层级
    this.level = level || 1
    // 用户权限,分级
    Auth.USER = 8
    Auth.ADMIN = 16
    Auth.SUPER_ADMIN = 32
  }
  get m() {
    return async (ctx, next) => {
      const errMsg = 'token 不合法'
      const userToken = basicAuth(ctx.req) // 获取用户名和密码
      /**
      * basicAuth(ctx.req)用于解析 HTTP Basic Authentication 的凭据。在 HTTP Basic Authentication 中,客户端在请求头中发送的凭据格式如下:
      Authorization:Basic <base64(username:passsword)>
      当你调用 `basicAuth(ctx.req)` 时,它会从请求的头部提取 `Authorization` 字段,并解析出用户名和密码。
      在 `Auth` 类中,`userToken` 会被解析为:
        {
          name: 'username', // 解码后的用户名
          pass: 'password'  // 解码后的密码
        }

      */
      if (!userToken || !userToken.name) {
        throw new global.errs.Forbidden(errMsg)
      }
      try {
        var decode = jwt.verify(
          userToken.name,
          global.config.security.secretKey,
        )
      } catch (err) {
        // token 不合法
        // token 过期
        if (err.name == 'TokenExpiredError') {
          throw new global.errs.Forbidden('token 已过期')
        }
        throw new global.errs.Forbidden(errMsg)
      }
      if (decode.scope < this.level) {
        throw new global.errs.Forbidden('权限不足')
      }
      // token 合法
      ctx.auth = { ...decode }
      await next()
    }
  }
  static verifyToken(token) {
    try {
      jwt.verify(token, global.config.security.secretKey)
      return true
    } catch (err) {
      return false
    }
  }
}
module.exports = {
  Auth,
}


// 客户端传入的时候:
  _encode(){
    const token = wx.getStorageSync('token')
    // 对token:进行base64, 这表明用户只需提供 token 作为身份凭证,而没有设置密码。
    const base64 = Base64.encode(`${token}:`)
    
    return 'Basic '+base64
  },
  -----
   header:{
        Authorization:this._encode()
   },