• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
jwcat313
博客园    首页    新随笔    联系   管理    订阅  订阅
postman实现自动生成JWT

背景

相信很多人在使用 postman 调用接口时都遇到了这样的问题,就是请求的网站需要验证 JWT,而我们虽然可以一键生成,但每次生成后都要重新粘到 postman 的请求头中才会生效,这不免带来许多麻烦,更头疼的是,大部分 JWT 的有效时间只有 10 分钟,当我们进行其他工作,再回过神来调用接口时,又会抛出令人讨厌的 403 错误,迫使我们再次手动生成,我们需要解决这个问题。

什么是 JWT

JWT 全称 Json Web Token,字面意思就是 Json 对象在网络中传输用到的令牌,而令牌的作用就是确保传输过程中的安全性。

授权:这是使用 JWT 的最常见场景。用户登录后,每个后续请求都将包含 JWT,允许用户访问该令牌允许的路由、服务和资源。单点登录是当今广泛使用 JWT 的一项功能,因为它的开销小,并且能够轻松地跨不同域使用。
信息交换:JSON Web 令牌是在各方之间安全地传输信息的好方法。由于 JWT 可以签名(例如,使用公钥/私钥对),因此您可以确保发件人是他们所声称的身份。此外,由于签名是使用标头和有效负载计算的,因此您还可以验证内容是否未被篡改。
可以使用密钥(使用 HMAC 算法)或使用 RSA 或 ECDSA 的公钥/私钥对对 JWT 进行签名。
官网地址:https://jwt.io/introduction

JWT 结构

JWT 一般由三个部分组成:

  • header
  • payload
  • signature
    而由他们三个生成的JWT格式一般是xxxxx.yyyyy.zzzzz,中间由 . 进行分隔,下面是一个真实的 JWT:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhY2Nlc3NLZXlJZCI6ImlkYWFzbW5nXzAzMWYxMDc2OGE2ZjRhNGE5M2FiZWU2MDViYjgzNzg1IiwiZXhwIjoxNzMzNzkyMDkwfQ.AD_cguC8JPS9HIMHz0y0yWU--UYpgKnKRkd_SDpDSYU

header

header通常由两部分组成:令牌的类型(JWT)和正在使用的签名算法,例如 HMAC SHA256 或 RSA。

例如:

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

然后,此 JSON 经过 Base64Url 编码以形成 JWT 的第一部分。也就是xxxxx这一部分。
通常这一部分是保持不变的,因为这一部分信息并不涉及什么重要的安全信息,只是告知后台应当采用什么算法进行加密验签。

payload

JWT 的第二部分是payload,其中包含声明。声明是关于实体(通常是用户)和其他数据的声明。 有三种类型的声明:已注册、公共和私有声明。

  • 已注册的声明 Register Claims:这些是一组预定义的声明,不是强制性的,但建议使用,以提供一组有用的、可互操作的声明。其中一些是:iss(颁发者)、exp(过期时间)、sub(主题)、aud(受众)等。

请注意,声明名称只有三个字符长,因为 JWT 是紧凑的。
我们一般会携带 exp,因为 exp 的值是当前时间加上有效期的一个时间类型值,所以每次经过Base64加密生成的结果都不一样。

  • 公共声明 Public Claims:这些声明可以由使用 JWT 的用户随意定义。但为避免冲突,应在 IANA JSON Web 令牌注册表中定义它们,或将其定义为包含抗冲突命名空间的 URI。

  • 私有声明 Private Claims:这些是自定义声明,用于在同意使用它们的各方之间共享信息,既不是注册声明,也不是公开声明。

一个有效的payload示例:

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}

然后,对该 JSON 进行 Base64Url 编码,以形成 JWT 的第二部分。也就是yyyyy这一部分。

请注意,对于签名令牌,此信息虽然可以防止篡改,但任何人都可以读取。除非 JWT 已加密,否则不要将机密信息放在 JWT 的 payload 或 header 元素中。

signature

要创建signature部分,您必须获取编码的header、编码的payload、密钥、标头中指定的算法,并对其进行签名。

例如,如果您想使用 HMAC SHA256 算法,将按以下方式创建signature:

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

最后,再次对HMAC SHA256 算法加密后得到的byte数组进行base64加密,就得到了 JWT 的第三部分。也就是zzzzz这一部分。
signature用于验证消息在整个过程中没有被更改,并且在使用私钥secret签名的令牌的情况下,它还可以验证 JWT 的发件人是否是它所声称的身份。

我们要怎么做

postman 中有这样一个选项 Scripts ,可以在里面编写 JavaScript 代码,左边的 pre-request 和 post-response 则是指定在请求前的操作和接收到请求结果后的操作。( PS:旧版 postman 的 Scripts 应该是 pre-request )

我们要做的,就是在这里面生成所需的 JWT ,并且放入 postman 的环境变量中,这样,我们的接口在调用时,只需要使用双大括号{{}}的的方式,就可以自动从环境变量取到所需的 JWT 。

还记得上文提到的一个真实的 JWT 吗?让我们来分析一下。

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhY2Nlc3NLZXlJZCI6ImlkYWFzbW5nXzAzMWYxMDc2OGE2ZjRhNGE5M2FiZWU2MDViYjgzNzg1IiwiZXhwIjoxNzMzNzkyMDkwfQ.AD_cguC8JPS9HIMHz0y0yWU--UYpgKnKRkd_SDpDSYU

第三部分AD_cguC8JPS9HIMHz0y0yWU--UYpgKnKRkd_SDpDSYU好像和前两个部分不太一样。
它携带了下划线 _ 和减号 - 这些字符,这是因为先经过HMAC SHA256加密再经过 base64 编码后可能会产生加号 + ,等于号 = ,斜杠 / 这些不安全符号,经过转义后就会产生第三部分的结果。

现在我们已经知道了原理,进入编码部分。

首先,引入我们所需的类。其中 btoa 是一个安全的 base64 算法。

const CryptoJS = require('crypto-js')
const btoa = require('btoa')

完成 JWT 第一部分xxxxx,得到eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9

let headerJson = JSON.stringify({
    "alg": "HS256",
    "typ": "JWT"
})
let header = btoa(headerJson)

注意:JSON 内属性的顺序改变也会改变header

完成 JWT 第二部分yyyyy,得到eyJhY2Nlc3NLZXlJZCI6ImlkYWFzbW5nXzAzMWYxMDc2OGE2ZjRhNGE5M2FiZWU2MDViYjgzNzg1IiwiZXhwIjoxNzMzNzkyMDkwfQ

// 超时时间为10分钟
let exp = new Date().getTime() + 10*60*1000

let ak = pm.environment.get("ak")
let payloadJson = JSON.stringify({
    "accessKeyId": ak,
    "exp": exp,
})

let payload = btoa(payloadJson);

这边我使用到了aksk,其中公钥ak放在payload内,私钥sk放在第三部分用于HMAC SHA256加密

完成 JWT 第三部分zzzzz,得到AD_cguC8JPS9HIMHz0y0yWU--UYpgKnKRkd_SDpDSYU

function safeEncode(header,payload,sk) {
    // HmacSHA256加密
    let hash = CryptoJS.HmacSHA256(header+"."+payload,sk)

    // base64加密
	let base64 = CryptoJS.enc.Base64.stringify(hash);
	let reg = new RegExp("/", "g");

    // 手动替换为可以被校验的符号
	base64 = base64.replace(/=+/,"").replace(/\+/g,"-").replace(reg,"_");
	return base64;
}
let signature=safeEncode(header,payload,sk)

最后的最后,就是拼接起来,并放到环境变量中,就得到了每次都是随机生成的 JWT,大功告成!

let jwt=header + "." + payload+"."+signature
pm.environment.set("SSO-JWT-Authorization",jwt)

完整代码如下:

const CryptoJS = require('crypto-js')
const btoa = require('btoa') // 安全的base64加密算法

// 通过header,payload,sk生成签名signature
function safeEncode(header,payload,sk) {
    // HmacSHA256加密
    let hash = CryptoJS.HmacSHA256(header+"."+payload,sk)

    // base64加密
	let base64 = CryptoJS.enc.Base64.stringify(hash);
	let reg = new RegExp("/", "g");

    // 手动替换为可以被校验的符号,
	base64 = base64.replace(/=+/,"").replace(/\+/g,"-").replace(reg,"_");
	return base64;
}

function generateToken() {
    let ak=pm.environment.get("ak")// 放在aksk环境变量里,可以自行调整
    let sk=pm.environment.get("sk")

    let iat = new Date().getTime()
    // 超时时间为10分钟
    let exp = iat + 10*60*1000

    let headerJson = JSON.stringify({
        "typ": "JWT",
        "alg": "HS256"
    })

    let payloadJson=JSON.stringify({
        "accessKeyId": ak,
        "exp": exp,
	})
 
    let header=btoa(headerJson)
    let payload=btoa(payloadJson)
    let signature=safeEncode(header,payload,sk)

    // 拼接得到jwt
    let jwt=header + "." + payload+"."+signature
    pm.environment.set("SSO-JWT-Authorization",jwt)
    console.log(jwt)
}
			
generateToken()
posted on 2024-12-10 09:27  九尾猫313  阅读(519)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3