前端开发中关于Token的那些事儿:深入浅出JWT签名验签
作为前端也要懂JWT,首先了解两个概念JWK
JWT
JWK
JWK(RSA JSON Web Key)是一种用于表示 RSA 公钥或私钥的 JSON 对象,JWK 是 JSON Web Token (JWT) 和 JSON Web Encryption (JWE) 等标准的一部分,常用于安全传输密钥信息。
用途
-
RSA JWK 主要用于:
-
密钥交换:在客户端和服务器间安全传输密钥。
-
数字签名:用于 JWT 签名和验证。
-
加密:用于 JWE 加密和解密。
RFC 7519 定义了 JSON Web Token (JWT) 的标准,JWT 是一种紧凑的、URL 安全的方式,用于在网络应用之间传递声明。
在线生成JWK https://mkjwk.org/
JWT(JSON Web Token)
JWT也就是我们平常说的Token,用来和后端接口交换数据的令牌。
JWT 结构
JWT 由三部分组成,用点号(.)分隔:
-
Header:包含令牌类型和签名算法。
-
Payload:包含声明(claims),即要传递的数据。
-
Signature:用于验证令牌的完整性和真实性。
JWT详细信息参考 https://jwt.io/
代码实践JWT签发、验证
新建一个JWT实践项目,安装依赖JWTjose
mkdir jwt-practice
cd jwt-practice
npm init
npm i jose
package.json
中修改包的加载方式,修改或添加字段:"type": "module",
新建index.js
文件,并复制下面代码内容
import { SignJWT, importJWK, jwtVerify } from 'jose'const alg = 'RS256'
const jwk = {kty: 'RSA',n: 'whYOFK2Ocbbpb_zVypi9SeKiNUqKQH0zTKN1-6fpCTu6ZalGI82s7XK3tan4dJt90ptUPKD2zvxqTzFNfx4HHHsrYCf2-FMLn1VTJfQazA2BvJqAwcpW1bqRUEty8tS_Yv4hRvWfQPcc2Gc3-_fQOOW57zVy-rNoJc744kb30NjQxdGp03J2S3GLQu7oKtSDDPooQHD38PEMNnITf0pj-KgDPjymkMGoJlO3aKppsjfbt_AH6GGdRghYRLOUwQU-h-ofWHR3lbYiKtXPn5dN24kiHy61e3VAQ9_YAZlwXC_99GGtw_NpghFAuM4P1JDn0DppJldy3PGFC0GfBCZASw',e: 'AQAB',d: 'VuVE_KEP6323WjpbBdAIv7HGahGrgGANvbxZsIhm34lsVOPK0XDegZkhAybMZHjRhp-gwVxX5ChC-J3cUpOBH5FNxElgW6HizD2Jcq6t6LoLYgPSrfEHm71iHg8JsgrqfUnGYFzMJmv88C6WdCtpgG_qJV1K00_Ly1G1QKoBffEs-v4fAMJrCbUdCz1qWto-PU-HLMEo-krfEpGgcmtZeRlDADh8cETMQlgQfQX2VWq_aAP4a1SXmo-j0cvRU4W5Fj0RVwNesIpetX2ZFz4p_JmB5sWFEj_fC7h5z2lq-6Bme2T3BHtXkIxoBW0_pYVnASC8P2puO5FnVxDmWuHDYQ',p: '07rgXd_tLUhVRF_g1OaqRZh5uZ8hiLWUSU0vu9coOaQcatSqjQlIwLW8UdKv_38GrmpIfgcEVQjzq6rFBowUm9zWBO9Eq6enpasYJBOeD8EMeDK-nsST57HjPVOCvoVC5ZX-cozPXna3iRNZ1TVYBY3smn0IaxysIK-zxESf4pM',q: '6qrE9TPhCS5iNR7QrKThunLu6t4H_8CkYRPLbvOIt2MgZyPLiZCsvdkTVSOX76QQEXt7Y0nTNua69q3K3Jhf-YOkPSJsWTxgrfOnjoDvRKzbW3OExIMm7D99fVBODuNWinjYgUwGSqGAsb_3TKhtI-Gr5ls3fn6B6oEjVL0dpmk',dp: 'mHqjrFdgelT2OyiFRS3dAAPf3cLxJoAGC4gP0UoQyPocEP-Y17sQ7t-ygIanguubBy65iDFLeGXa_g0cmSt2iAzRAHrDzI8P1-pQl2KdWSEg9ssspjBRh_F_AiJLLSPRWn_b3-jySkhawtfxwO8Kte1QsK1My765Y0zFvJnjPws',dq: 'KmjaV4YcsVAUp4z-IXVa5htHWmLuByaFjpXJOjABEUN0467wZdgjn9vPRp-8Ia8AyGgMkJES_uUL_PDDrMJM9gb4c6P4-NeUkVtreLGMjFjA-_IQmIMrUZ7XywHsWXx0c2oLlrJqoKo3W-hZhR0bPFTYgDUT_mRWjk7wV6wl46E',qi: 'iYltkV_4PmQDfZfGFpzn2UtYEKyhy-9t3Vy8Mw2VHLAADKGwJvVK5ficQAr2atIF1-agXY2bd6KV-w52zR8rmZfTr0gobzYIyqHczOm13t7uXJv2WygY7QEC2OGjdxa2Fr9RnvS99ozMa5nomZBqTqT7z5QV33czjPRCjvg6FcE',}const privateKey = await importJWK(jwk, alg)/*** 签发JWT*/
const jwt = await new SignJWT({ maizuo: 'xxst' })/*** 签名算法*/.setProtectedHeader({ alg }).setIssuedAt()/*** 发行人*/.setIssuer('myname:admin:issuer')/*** 观众/接收人*/.setAudience('myname:user:audience')/*** 设置过期时间*/.setExpirationTime('2h').sign(privateKey)/*** 验证JWT*/
const { payload, protectedHeader } = await jwtVerify(jwt, privateKey, {issuer: 'myname:admin:issuer',audience: 'myname:user:audience',
})
//打印签发、验证过程
console.log({ jwt, payload, protectedHeader })
nodejs中执行index.js
node -v
# v22.12.0 我的node版本
node index.js
打印执行结果的数据
{jwt: 'eyJhbGciOiJSUzI1NiJ9.eyJtYWl6dW8iOiJ4eHN0IiwiaWF0IjoxNzM5MjY5NzUwLCJpc3MiOiJteW5hbWU6YWRtaW46aXNzdWVyIiwiYXVkIjoibXluYW1lOnVzZXI6YXVkaWVuY2UiLCJleHAiOjE3MzkyNzY5NTB9.Vu91Rh8IiTSglhERK3MCT6VSIyTwoCB4lA-gdBjJI7_1IBpUjXsMNOD9SsRoKyWfph5xyVlLoEiLczIWid9VWp2_1PfLu3a2DXpVYMIvTwWg5-N0hQJZq4ytPYPUhoc7ROeuwGVGs-t6gIIciJCDtg4r7LWMGY9v0ekJxRRbv9EEpI0W5zrUEbemRAeq1o4QiZ1dpslko4WDZPEO0fUWqjm0k1NDBtuXKM8LelDUS_fpGsaqNNgSiL_XGGNvYMOWEFpmCHUMupQzbAnwuApd0TVVt4el9Rbek1m7rVHTAzaE0ynAbgJyBg6yPtkkw4snX-3sCYWmWGs15Kc4mRDRPQ',payload: {maizuo: 'xxst',iat: 1739269750,iss: 'myname:admin:issuer',aud: 'myname:user:audience',exp: 1739276950},protectedHeader: { alg: 'RS256' }
}