JWT从入门到精通:一文解锁生成、验证与防篡改秘籍

JWT从入门到精通:一文解锁生成、验证与防篡改秘籍

JWT 是什么

JWT,即 JSON Web Token,是一种基于 JSON 的开放标准(RFC 7519),用于在网络应用间安全地传输信息 。简单来说,它就像是一把特殊的钥匙,这把钥匙里包含了用户的一些关键信息。当你登录一个应用时,服务器会生成这样一把 "钥匙" 给你,之后你拿着这把 "钥匙" 去访问应用的各种功能,服务器通过检查这把 "钥匙" 就能知道你是谁,是否有权限进行操作。

在实际应用中,JWT 最常见的场景就是身份验证和信息交换。比如,当你登录淘宝时,输入账号密码成功后,服务器会返回一个 JWT 给你的手机或电脑。之后你在浏览商品、添加购物车、下单等操作时,请求里都会带上这个 JWT,淘宝的服务器通过验证这个 JWT,就能确认是你在操作,而不是别人冒充你。又比如在一些微服务架构的系统中,各个服务之间需要传递用户信息来协同工作,JWT 就可以用来安全地传输这些信息,保证信息在传递过程中的完整性和真实性。

了解了 JWT 的基本概念和应用场景后,接下来我们就深入到 JWT 的内部,看看它是如何生成、验证以及防止被篡改的,这也是掌握 JWT 技术的关键所在。

JWT 的结构剖析

在深入了解 JWT 的生成、验证与防篡改之前,我们先来剖析一下 JWT 的内部结构。JWT 就像是一个特殊的包裹,它由三个部分组成,分别是 Header(头部)、Payload(载荷)和 Signature(签名) ,这三个部分通过点号(.)连接,形成一个完整的 JWT 字符串 ,就像这样:xxxxx.yyyyy.zzzzz 。接下来,我们就详细看看这三个部分各自的作用和奥秘。

Header(头部)

Header 是 JWT 的 "身份铭牌",主要描述 JWT 的元数据,其中最关键的是声明签名算法和令牌类型。比如下面这个典型的 Header:

json 复制代码
{
  "alg": "HS256",
  "typ": "JWT"
}

在这段 JSON 代码里,alg字段指定了签名使用的算法,这里的HS256表示使用 HMAC SHA-256 算法;typ字段则表明了令牌类型,JWT代表这是一个 JSON Web Token。当服务器生成 JWT 时,会先将这个 Header 对象进行 Base64Url 编码 ,Base64Url 编码是一种针对 URL 安全的 Base64 编码方式,编码后的结果就成为了 JWT 的第一部分。例如,上述 Header 经过 Base64Url 编码后,得到的字符串是:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 。需要注意的是,Base64Url 编码并不是加密,所以 Header 部分的内容任何人都可以解码查看,它只是以一种特定的格式来携带 JWT 的基本信息。

Payload(载荷)

Payload 是 JWT 的 "信息背包",是承载实际业务数据的部分,这些数据以 "声明(Claims)" 的形式存在。声明可以分为以下几类:

  • 标准声明(Registered Claims):这是一组预定义的声明,虽然不是强制使用,但推荐使用,它们为 JWT 提供了一些通用的、可互操作的信息。常见的标准声明有:

    • iss(issuer):签发者,标识 JWT 是由哪个实体生成的,比如auth.example.com ,表明这个 JWT 是由auth.example.com这个认证服务器签发的。

    • sub(subject):主题,通常用来标识 JWT 所面向的用户或实体,一般是用户的唯一 ID,例如123456 ,代表这个 JWT 是关于 ID 为123456的用户。

    • aud(audience):接收方,指定 JWT 的预期接收者,可以是一个或多个实体,比如["client1", "client2"] ,表示这个 JWT 可以被client1client2接收使用。

    • exp(expiration time):过期时间,用 Unix 时间戳表示,一旦当前时间超过这个时间戳,JWT 就会被视为过期,例如1616239022 ,对应的实际时间可能是某个具体的日期和时刻,超过这个时间后,该 JWT 就不再有效。

    • nbf(not before):生效时间,在此时间之前 JWT 无效,同样是 Unix 时间戳格式,比如1616238022 ,表示在这个时间点之前,即使 JWT 被收到,也不能被认为是有效的。

    • iat(issued at):签发时间,记录 JWT 的生成时间,也是 Unix 时间戳,如1616237022 ,可以用于追踪 JWT 的创建时刻。

  • 公共声明(Public Claims) :这是可以由应用程序自定义的声明,为了避免命名冲突,最好在 IANA JSON Web Token Registry 中定义它们,或者使用一个防冲突的命名空间,比如包含域名。例如,我们可以定义一个username声明来表示用户的用户名,或者定义一个role声明来表示用户的角色,如adminuser

  • 私有声明(Private Claims) :这是在 JWT 的提供者和消费者之间预先约定好的自定义声明,用于在双方之间共享特定的信息,既不属于标准声明也不属于公共声明。比如,应用程序内部可能约定一个userPreferences声明来存放用户的个性化设置信息。

一个包含业务数据的 Payload 示例如下:

json 复制代码
{
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022,
  "admin": true
}

这里sub表示用户 ID,name是用户姓名,iat记录了签发时间,admin则是一个自定义的私有声明,用于表示该用户是否是管理员。和 Header 一样,Payload 中的 JSON 对象也会通过 Base64Url 编码,成为 JWT 的第二部分。上述 Payload 经过 Base64Url 编码后得到:eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJhZG1pbiI6dHJ1ZX0 。同样要牢记,Payload 只是经过了编码,并非加密,所以不要在其中存放敏感信息,比如用户密码等,因为这些信息可以被轻易解码查看。

Signature(签名)

Signature 是 JWT 的 "安全锁",是保证 JWT 不被篡改的核心部分。签名的生成过程是这样的:首先,将编码后的 Header 和编码后的 Payload 用点号(.)连接起来,形成一个新的字符串,即base64UrlEncode(header) + "." + base64UrlEncode(payload) ;然后,使用在 Header 中指定的签名算法(如HS256),以及一个只有服务器知道的密钥(Secret) ,对这个连接后的字符串进行签名。以HS256算法为例,签名公式为:signature = HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret) 。最后,将签名结果(一个二进制哈希值)也进行 Base64Url 编码,形成 JWT 的第三部分。

假设我们的密钥是字符串"your-256-bit-secret" ,对eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJhZG1pbiI6dHJ1ZX0进行 HMAC - SHA256 签名并编码后,得到的第三部分可能是:SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c 。签名的关键作用在于,当服务器收到 JWT 后,会用同样的密钥和算法重新计算前两部分的签名,如果计算结果与 JWT 中的第三部分签名匹配,就说明 JWT 在传输过程中没有被篡改,是可信的;如果不匹配,那就意味着 JWT 可能被恶意修改过,服务器会立即拒绝这个 JWT,从而保证了数据的完整性和安全性。

JWT 的生成步骤

了解了 JWT 的结构之后,我们就可以开始学习如何生成 JWT 了。JWT 的生成过程就像是制作一把特殊的钥匙,每一个步骤都至关重要,下面我们就逐步来揭开它的神秘面纱 。

准备工作

在生成 JWT 之前,我们需要确定三个关键要素:

  • 算法 :选择一种签名算法,常见的有HS256(HMAC SHA-256)、RS256(RSA SHA-256)等 。HS256是一种对称加密算法,使用同一个密钥进行签名和验证,它的优点是简单高效,适合在客户端和服务器之间共享密钥的场景;RS256则是非对称加密算法,使用私钥进行签名,公钥进行验证,安全性更高,常用于需要更高级别安全保障的场景,比如金融系统等。

  • 密钥 :这是一个只有服务器知道的秘密字符串,就像是 JWT 的 "防伪印章",用于生成签名和验证签名的有效性。密钥的安全性至关重要,一定要妥善保管,不能泄露,否则 JWT 的安全性将无法保证。例如,我们可以设置一个密钥为"my-secret-key-12345"

  • Payload 业务信息 :确定需要包含在 JWT 中的业务数据,如用户 ID、用户名、角色、过期时间等 。这些信息将被编码到 JWT 的 Payload 部分。比如,我们有一个用户,其用户 ID 为123456,用户名为"JohnDoe",角色为"admin",我们希望 JWT 在 1 小时后过期,那么我们可以将这些信息整理成如下格式:

json 复制代码
{
  "userId": 123456,
  "username": "JohnDoe",
  "role": "admin",
  "exp": 1616239022 // 假设这是1小时后的Unix时间戳
}

构建 Header

Header 是 JWT 的 "规则说明" 部分,它是一个 JSON 对象,固定包含两个字段:

  • alg :表示签名算法,我们之前选择了HS256,所以这里填"HS256"

  • typ :表示 Token 类型,固定填"JWT" 。 构建好的 Header JSON 对象如下:

json 复制代码
{
  "alg": "HS256",
  "typ": "JWT"
}

然后,我们需要将这个 JSON 对象进行 Base64Url 编码 ,得到一个字符串。Base64Url 编码是一种针对 URL 安全的 Base64 编码方式,它将标准 Base64 编码中的+替换为-/替换为_,并移除填充字符= 。在 JavaScript 中,我们可以使用以下代码进行 Base64Url 编码:

javascript 复制代码
function base64UrlEncode(str) {
  return btoa(unescape(encodeURIComponent(str)))
   .replace(/\+/g, '-')
   .replace(/\//g, '_')
   .replace(/=/g, '');
}
const header = {
  "alg": "HS256",
  "typ": "JWT"
};
const encodedHeader = base64UrlEncode(JSON.stringify(header));
console.log(encodedHeader);

上述代码执行后,encodedHeader的值可能是eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 ,这就是编码后的 Header,它将成为 JWT 的第一部分。

构建 Payload

Payload 是承载业务数据的部分,我们将之前准备好的业务信息整理成 JSON 对象的形式,如下:

json 复制代码
{
  "userId": 123456,
  "username": "JohnDoe",
  "role": "admin",
  "exp": 1616239022
}

这里需要特别注意exp字段,它表示过期时间,是一个 Unix 时间戳,一旦当前时间超过这个时间戳,JWT 就会被视为过期 。同样,我们要对这个 Payload JSON 对象进行 Base64Url 编码,在 JavaScript 中,编码方式和 Header 类似:

javascript 复制代码
const payload = {
  "userId": 123456,
  "username": "JohnDoe",
  "role": "admin",
  "exp": 1616239022
};
const encodedPayload = base64UrlEncode(JSON.stringify(payload));
console.log(encodedPayload);

执行上述代码后,encodedPayload的值可能是eyJ1c2VySWQiOjEyMzQ1NiwidXNlcm5hbWUiOiJKb2huRG9lIiwicm9sZSI6ImFkbWluIiwiZXhwIjoxNjE2MjM5MDIyfQ ,这就是编码后的 Payload,将作为 JWT 的第二部分。

拼接 Header 和 Payload

将编码后的 Header 和 Payload 用点号(.)连接起来,形成一个待签名字符串 。在 JavaScript 中,可以这样实现:

javascript 复制代码
const signedString = encodedHeader + '.' + encodedPayload;
console.log(signedString);

得到的signedString可能是eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjEyMzQ1NiwidXNlcm5hbWUiOiJKb2huRG9lIiwicm9sZSI6ImFkbWluIiwiZXhwIjoxNjE2MjM5MDIyfQ ,这个字符串包含了 JWT 的头部和载荷信息,接下来就要对它进行签名,以保证信息的完整性和真实性。

生成 Signature

Signature 是 JWT 防篡改的核心部分,它的生成规则是:用服务器的密钥(比如"my-secret-key-12345")和选定的算法(这里是HS256),对 "待签名字符串" 进行哈希计算,得到二进制签名,然后再将二进制签名转成 Base64URL 编码 。在 JavaScript 中,可以使用crypto模块来实现HS256签名,示例代码如下:

javascript 复制代码
const crypto = require('crypto');
const secret = "my-secret-key-12345";
const hash = crypto.createHmac('sha256', secret)
  .update(signedString)
  .digest('base64');
const signature = hash
  .replace(/\+/g, '-')
  .replace(/\//g, '_')
  .replace(/=/g, '');
console.log(signature);

执行上述代码后,signature的值可能是SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c ,这就是生成的签名,它将成为 JWT 的第三部分。

组装完整 JWT Token

最后,将编码后的 Header、Payload 和 Signature 用点号(.)连接起来,就得到了完整的 JWT Token 。在 JavaScript 中:

javascript 复制代码
const jwtToken = encodedHeader + '.' + encodedPayload + '.' + signature;
console.log(jwtToken);

得到的jwtToken可能是eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjEyMzQ1NiwidXNlcm5hbWUiOiJKb2huRG9lIiwicm9sZSI6ImFkbWluIiwiZXhwIjoxNjE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c ,这个字符串就是我们最终生成的 JWT Token,服务器可以将它返回给客户端,客户端在后续的请求中携带这个 Token,服务器通过验证这个 Token 来确认请求的合法性和用户的身份。

JWT 的验证流程

当客户端拿着 JWT Token 来访问受保护资源时,服务器需要对这个 Token 进行验证,以确保请求的合法性和安全性。JWT 的验证流程就像是一个严谨的安检过程,每一个环节都不可或缺,下面我们就详细了解一下这个流程 。

接收 Token

客户端在向服务器发送请求时,会将 JWT Token 放在请求头(Header)的Authorization字段中 ,格式通常为Bearer <token> 。例如:

Plain 复制代码
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

服务器在接收到请求后,首先会从请求头中提取出这个Authorization字段,并从中解析出 JWT Token 。在大多数 Web 开发框架中,都有便捷的方法来获取请求头中的信息。比如在 Node.js 中使用 Express 框架,可以通过req.headers.authorization来获取Authorization字段的值,然后再通过字符串操作提取出真正的 Token 部分 。

拆分 Token

服务器拿到 JWT Token 后,会按点号(.)将其分割成三个部分,分别对应 JWT 的 Header、Payload 和 Signature 。在 JavaScript 中,可以使用split方法来实现:

javascript 复制代码
const token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c";
const parts = token.split('.');
const header = parts[0];
const payload = parts[1];
const signature = parts[2];
console.log(header, payload, signature);

通过上述代码,我们就得到了 JWT 的三个组成部分,接下来就可以分别对它们进行处理和验证 。

解析 Header

对分割得到的 Header 部分进行 Base64Url 解码 ,得到一个 JSON 字符串,再将其解析为 JSON 对象,这样就能获取到 JWT 的元数据信息,特别是签名算法 。在 JavaScript 中,可以使用以下代码实现:

javascript 复制代码
function base64UrlDecode(str) {
  return decodeURIComponent(
    atob(str)
     .split('')
     .map(c => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))
     .join('')
  );
}
const decodedHeader = base64UrlDecode(header);
const headerObj = JSON.parse(decodedHeader);
console.log(headerObj.alg); // 输出签名算法,如HS256

通过解析 Header,服务器就知道了应该使用哪种算法来验证签名,比如这里得到的algHS256,就表明要使用 HMAC SHA - 256 算法来进行后续的签名验证 。

解析 Payload 并检查过期时间

同样,对 Payload 部分进行 Base64Url 解码并解析为 JSON 对象 ,这样就能获取到其中包含的业务数据和声明信息 。在 JavaScript 中:

javascript 复制代码
const decodedPayload = base64UrlDecode(payload);
const payloadObj = JSON.parse(decodedPayload);
console.log(payloadObj);

解析出 Payload 后,重点要检查其中的过期时间(exp字段) 。获取当前时间的 Unix 时间戳,与exp字段的值进行对比 。如果当前时间大于exp,说明 JWT 已经过期,服务器应立即拒绝该请求 。例如:

javascript 复制代码
const currentTime = Math.floor(Date.now() / 1000);
if (payloadObj.exp && currentTime > payloadObj.exp) {
  throw new Error('JWT has expired');
}

通过检查过期时间,服务器可以确保只处理有效的 JWT,防止过期的 Token 被使用,保障系统的安全性 。

重新计算签名并对比

这是验证 JWT 是否被篡改的关键步骤 。服务器会使用之前从 Header 中获取的签名算法(如HS256),以及服务器端保存的密钥(和生成 JWT 时使用的密钥相同) ,对编码后的 Header 和 Payload 进行签名计算 。在 JavaScript 中,使用crypto模块来实现HS256签名计算:

javascript 复制代码
const crypto = require('crypto');
const secret = "my-secret-key-12345";
const data = header + '.' + payload;
const hash = crypto.createHmac('sha256', secret)
  .update(data)
  .digest('base64');
const calculatedSignature = hash
  .replace(/\+/g, '-')
  .replace(/\//g, '_')
  .replace(/=/g, '');

计算出签名后,将其与 JWT 中携带的签名(之前拆分得到的signature)进行对比 。如果两者一致,说明 JWT 在传输过程中没有被篡改,是可信的;如果不一致,那就意味着 JWT 可能被恶意修改过,服务器会拒绝这个请求 。例如:

javascript 复制代码
if (calculatedSignature!== signature) {
  throw new Error('JWT signature is invalid');
}

通过这一系列严格的验证步骤,服务器就能准确判断 JWT 的有效性和完整性,确保只有合法的请求才能访问受保护的资源 。

JWT 的防篡改原理

JWT 的防篡改能力是保障其在网络传输中数据安全的关键特性,而这一特性的核心就在于 Signature(签名)部分 。我们知道,JWT 由 Header(头部)、Payload(载荷)和 Signature 三部分组成,其中 Header 和 Payload 在生成 JWT 时都会进行 Base64Url 编码,然后通过点号(.)连接起来,形成一个待签名字符串 。而 Signature 就是使用在 Header 中指定的签名算法(如HS256RS256等 ),以及一个只有服务器知道的密钥(Secret) ,对待签名字符串进行签名计算得到的结果。

HS256算法为例,签名的生成公式为:signature = HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret) 。这个签名就像是 JWT 的 "数字指纹",它是根据 Header 和 Payload 的内容以及密钥计算出来的,具有唯一性和不可伪造性 。当服务器收到 JWT 后,会按照同样的算法和密钥,重新计算 Header 和 Payload 的签名 。如果重新计算得到的签名与 JWT 中携带的签名一致,那就说明 JWT 在传输过程中没有被篡改,因为如果有人试图修改 Header 或 Payload 中的任何内容,哪怕只是一个字符,重新计算出来的签名都会发生巨大的变化 。

例如,假设我们有一个原始的 JWT,其 Header 部分为{"alg": "HS256", "typ": "JWT"} ,经过 Base64Url 编码后得到eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 ;Payload 部分为{"sub": "123456", "name": "JohnDoe", "iat": 1616239022} ,编码后是eyJzdWIiOiIxMjM0NTYifQ 。服务器使用密钥"my-secret-key" ,通过HS256算法计算签名,得到签名部分xxxxxx ,最终完整的 JWT 为eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTYifQ.xxxxxx 。现在,假设黑客试图篡改 Payload 中的sub字段,将其改为"999999" ,然后重新编码 Payload 得到eyJzdWIiOiI5OTk5OTkiLCJuYW1lIjoiSm9obiBEb2UiLCJpYXQiOjE2MTYyMzkwMjJ9 ,并使用原来的签名xxxxxx 构造一个假的 JWT:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI5OTk5OTkiLCJuYW1lIjoiSm9obiBEb2UiLCJpYXQiOjE2MTYyMzkwMjJ9.xxxxxx 。当服务器收到这个假的 JWT 后,会用同样的密钥和算法重新计算签名,由于 Payload 已经被篡改,所以计算出来的新签名与原来的xxxxxx 肯定不一致,服务器就能立刻判断出这个 JWT 是被篡改过的,从而拒绝该请求 。

正是因为签名与 Header 和 Payload 的内容紧密相关,并且密钥只有服务器知晓,黑客即使获取到 JWT,也无法在不知道密钥的情况下伪造出合法的签名 ,所以 JWT 能够有效地防止被篡改,确保了数据在传输过程中的完整性和真实性 。

实际应用案例与代码示例

案例场景介绍

假设我们正在开发一个在线商城系统,用户在登录成功后,需要获取一个 JWT Token,用于后续访问商城的各种受保护资源,比如查看订单列表、管理收货地址等 。具体流程如下:

  1. 用户登录:用户在商城的登录页面输入用户名和密码,点击登录按钮,向服务器发送登录请求 。

  2. 服务器验证:服务器接收到登录请求后,验证用户名和密码是否正确 。如果验证通过,进入下一步;如果验证失败,返回错误信息,提示用户重新登录 。

  3. 生成 JWT Token :服务器验证用户身份成功后,根据用户的信息(如用户 ID、用户名、角色等 )生成一个 JWT Token 。例如,用户 ID 为123456,用户名为"JohnDoe",角色为"customer" ,服务器设置 JWT Token 的过期时间为 1 小时 。

  4. 返回 Token:服务器将生成的 JWT Token 返回给客户端,客户端可以将其存储在本地,比如存储在浏览器的 Local Storage 中,或者移动应用的本地存储中 。

  5. 访问受保护资源:当用户需要访问商城的受保护资源时,比如查看订单列表,客户端会在请求头中携带之前获取的 JWT Token,向服务器发送请求 。

  6. 服务器验证 Token:服务器接收到请求后,从请求头中提取 JWT Token,并按照之前介绍的验证流程进行验证 。如果验证通过,服务器允许用户访问受保护资源,返回订单列表数据;如果验证失败,服务器返回错误信息,提示用户重新登录或者 Token 无效 。

代码实现(以 Node.js 为例)

在 Node.js 中,我们可以使用jsonwebtoken库来方便地生成和验证 JWT 。首先,需要安装jsonwebtoken库,在项目目录下执行以下命令:

bash 复制代码
npm install jsonwebtoken

安装完成后,我们可以编写以下代码来实现上述案例场景:

javascript 复制代码
const express = require('express');
const jwt = require('jsonwebtoken');
const app = express();
app.use(express.json());

// 模拟用户数据库
const users = [
  { id: 123456, username: 'JohnDoe', password: 'password123', role: 'customer' }
];

// 登录路由,生成JWT
app.post('/login', (req, res) => {
  const { username, password } = req.body;
  // 查找用户
  const user = users.find(u => u.username === username && u.password === password);
  if (!user) {
    return res.status(401).json({ error: '用户名或密码错误' });
  }
  // 生成JWT Payload
  const payload = {
    userId: user.id,
    username: user.username,
    role: user.role
  };
  const secretKey = 'your-secret-key'; // 请替换为实际的密钥
  const options = { expiresIn: '1h' }; // 设置过期时间为1小时
  const token = jwt.sign(payload, secretKey, options);
  res.json({ token });
});

// 受保护的订单列表路由,需要JWT验证
app.get('/orders', (req, res, next) => {
  const token = req.headers.authorization;
  if (!token) {
    return res.status(401).send('请先登录');
  }
  const secretKey = 'your-secret-key';
  try {
    const decoded = jwt.verify(token, secretKey);
    // 这里可以根据decoded中的用户信息进行权限验证等操作
    // 假设验证通过,返回订单列表数据
    const orders = [
      { id: 1, product: '商品1', amount: 2 },
      { id: 2, product: '商品2', amount: 1 }
    ];
    res.json({ orders });
  } catch (error) {
    res.status(403).send('无效的Token');
  }
});

const port = 3000;
app.listen(port, () => {
  console.log(`服务器运行在端口 ${port}`);
});

在上述代码中:

  • /login路由用于处理用户登录请求,验证用户名和密码后,使用jwt.sign方法生成 JWT Token 并返回给客户端 。jwt.sign方法接收三个参数:payload(包含用户信息的对象)、secretKey(密钥)和options(可选参数,这里设置了过期时间) 。

  • /orders路由是受保护的资源路由,首先从请求头中获取Authorization字段,提取 JWT Token 。然后使用jwt.verify方法验证 Token 的有效性 。jwt.verify方法接收两个参数:token(要验证的 JWT Token)和secretKey(密钥) 。如果验证成功,decoded将包含 JWT Payload 中的用户信息,我们可以根据这些信息进行后续的业务处理,比如返回订单列表数据;如果验证失败,会抛出异常,捕获异常后返回错误信息 。

总结与拓展

通过前面的内容,我们已经全面掌握了 JWT 的生成、验证和防篡改机制。从生成过程来看,确定算法、密钥和 Payload 业务信息是基础,构建 Header 和 Payload 并进行 Base64Url 编码,再拼接两者生成待签名字符串,最后使用密钥和算法生成签名,组装成完整的 JWT Token,每一步都紧密相连,缺一不可。验证流程则是对生成过程的反向检查,通过拆分 Token、解析 Header 和 Payload,检查过期时间并重新计算签名进行对比,确保 JWT 的有效性和合法性。而 JWT 的防篡改原理,依赖于签名与 Header、Payload 以及密钥的紧密关联,只要密钥不泄露,签名就无法被伪造,保证了数据在传输过程中的完整性和真实性 。

在实际应用 JWT 时,还有一些注意事项。首先,密钥的管理至关重要,一定要妥善保管,避免泄露,建议通过环境变量、配置中心或密钥管理服务(KMS)来管理密钥。其次,JWT 的过期时间应设置得合理,不宜过长也不宜过短,过长会增加安全风险,过短则可能导致用户频繁登录,影响用户体验。对于需要长期保持登录的应用,可以配合刷新令牌(Refresh Token)机制来获取新的 JWT 。另外,由于 JWT 的 Payload 是 Base64Url 编码而非加密,所以不要在其中存放敏感信息,如用户密码等。

随着技术的不断发展,JWT 在未来的应用场景也会更加广泛和深入。在分布式系统和微服务架构中,JWT 的无状态特性将使其成为身份验证和授权的首选方案之一,能够更好地实现系统间的通信和协作。同时,JWT 也可能会与其他安全技术相结合,如多因素认证(MFA)等,进一步提升系统的安全性 。希望大家在实际项目中能够灵活运用 JWT,不断探索其更多的可能性,为应用的安全和稳定保驾护航。如果对 JWT 还有更多的疑问或者想要深入研究,可以查阅相关的文档和资料,进行更多的实践和尝试。

相关推荐
2401_895521342 小时前
SpringBoot Maven快速上手
spring boot·后端·maven
disgare3 小时前
关于 spring 工程中添加 traceID 实践
java·后端·spring
ictI CABL3 小时前
Spring Boot与MyBatis
spring boot·后端·mybatis
小江的记录本5 小时前
【Linux】《Linux常用命令汇总表》
linux·运维·服务器·前端·windows·后端·macos
yhole8 小时前
springboot三层架构详细讲解
spring boot·后端·架构
香香甜甜的辣椒炒肉8 小时前
Spring(1)基本概念+开发的基本步骤
java·后端·spring
白毛大侠9 小时前
Go Goroutine 与用户态是进程级
开发语言·后端·golang
ForteScarlet9 小时前
从 Kotlin 编译器 API 的变化开始: 2.3.20
android·开发语言·后端·ios·开源·kotlin
大阿明9 小时前
SpringBoot - Cookie & Session 用户登录及登录状态保持功能实现
java·spring boot·后端
Binary-Jeff9 小时前
Spring 创建 Bean 的关键流程
java·开发语言·前端·spring boot·后端·spring·学习方法