为了账号安全,请及时绑定邮箱和手机立即绑定

JWT课程:新手必学的JWT入门指南

标签:
架构 安全 API
概述

本文详细介绍了JWT的基本概念、工作原理、优势与应用场景,并深入讲解了JWT的生成、验证及安全措施,帮助读者全面理解JWT的使用方法。

JWT简介与基本概念

JWT(JSON Web Token)是一种开放标准(RFC 7519),用于在网络应用之间安全地传递信息。JWT通常用于身份验证和授权,它由三部分组成:头部、载荷和签名。JWT的关键在于其紧凑性、安全性以及易于解析的特点,使得它在现代Web应用中被广泛应用。

JWT采用一种自包含的、无状态的令牌格式,可以很容易地通过URL、POST请求参数或HTTP头字段等方式进行传输。这种格式不仅方便,而且可以减少服务器端的存储负担,因为JWT本身包含了所有必要的验证信息。

JWT的工作原理

JWT的工作原理可以概括为以下几个步骤:

  1. JWT生成:客户端请求服务器获取JWT,服务器验证客户端的身份(如用户名和密码),如果验证通过,生成一个包含用户信息的JWT。
  2. JWT传递:服务器将生成的JWT返回给客户端,客户端收到后可以存储在本地(如使用localStoragesessionStoragecookies)。
  3. JWT验证:当客户端需要与服务器进行交互时,将JWT通过请求头或其他方式发送给服务器。服务器接收到JWT后,对JWT进行验证,确保其有效性和安全性。
  4. JWT解析:如果JWT通过验证,服务器可以解析JWT中的载荷,获取用户信息并进行后续的操作,如查询用户信息、权限验证等。
  5. JWT刷新:由于JWT通常具有过期时间(如1小时),客户端在JWT即将过期时,可以再次请求服务器获取新的JWT,或使用刷新令牌进行刷新。

以下是JWT生成和验证的简单示例代码:

const jwt = require('jsonwebtoken');

const payload = {
    sub: '1234567890',
    name: 'John Doe',
    iat: Math.floor(Date.now() / 1000),
    exp: Math.floor(Date.now() / 1000) + (60 * 60)  // 1小时过期
};

const token = jwt.sign(payload, 'secret', { expiresIn: '1h' });
console.log('Generated JWT:', token);

try {
    const decoded = jwt.verify(token, 'secret');
    console.log('JWT is valid:', decoded);
} catch (error) {
    console.log('JWT is invalid:', error.name);
}
JWT的优势与应用场景

优势

  1. 紧凑性:JWT相对于Cookie更为轻量,适合在跨域访问时使用。
  2. 无状态:服务器不需要存储与JWT关联的任何数据,减轻了服务器的存储压力。
  3. 安全性:通过加密和签名机制,保证了传输数据的安全性。
  4. 跨域支持:JWT非常适合分布式系统中的身份验证和授权。

应用场景

  1. 身份验证:用户登录后,服务器生成JWT并返回给客户端,客户端在后续请求中携带JWT进行身份验证。
  2. 授权:通过JWT中的载荷,可以携带用户的权限信息,用于控制对资源的访问。
  3. 数据交换:JWT可以用于不同服务间的安全数据交换。
JWT的构成部分

JWT由三个部分组成:头部(Header)、载荷(Payload)和签名(Signature),三者通过.分隔,形成一个紧凑的字符串。

头部(Header)

头部部分包含JSON对象,通常包含两个字段:alg(算法)和typ(类型)。

{
    "alg": "HS256",  // 签名算法
    "typ": "JWT"     // 类型
}

头部经过Base64编码后,形成JWT的第一部分。

载荷(Payload)

载荷部分包含声明(Claim),声明是关于实体(人、地方、物体、事件等)的声明。JWT标准中定义了三个保留的声明字段:

  • iss: 发行人。
  • exp: 过期时间,表示从1970年1月1日以来的秒数。
  • sub: 主题,比如用户ID。
  • aud: 接收方,通常是一个字符串数组,表示接收JWT的系统或服务。
  • iat: 发行时间,表示从1970年1月1日以来的秒数。
{
    "sub": "1234567890",  // 用户ID
    "name": "John Doe",    // 用户名
    "iat": 1516239022,     // 发行时间
    "exp": 1516263022      // 过期时间
}

载荷经过Base64编码后,形成JWT的第二部分。

签名(Signature)

签名部分由头部和载荷的Base64编码后的字符串,以及一个密钥(secret)共同生成。签名的生成公式如下:

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

签名部分是JWT的最后一部分,用于验证JWT的完整性和真实性。签名的生成必须使用与头部指定的算法一致的算法(如HS256)。

如何生成JWT

JWT的生成过程包括准备阶段、使用库生成JWT和手动生成JWT的步骤。

准备工作

在生成JWT之前,需要做好以下准备工作:

  1. 选择合适的密钥:JWT的签名需要一个密钥(secret),该密钥通常是一个字符串,用于保证JWT的唯一性和安全性。
  2. 配置算法:根据项目需求选择合适的签名算法(如HS256、RS256等)。
  3. 设置过期时间:定义JWT的过期时间,通常以秒为单位,从1970年1月1日以来的秒数。
使用库生成JWT

为了简化JWT的生成过程,可以使用成熟的库来生成JWT。以下示例使用Node.js的jsonwebtoken库生成JWT:

const jwt = require('jsonwebtoken');

// 定义载荷
const payload = {
    sub: '1234567890',
    name: 'John Doe',
    iat: 1516239022,
    exp: 1516263022
};

// 生成JWT
const token = jwt.sign(payload, 'secret', { expiresIn: '1h' });

console.log(token);

以上代码会生成一个包含过期时间的JWT,并返回一个Base64编码后的字符串。

手动生成JWT

如果不想依赖库,可以手动实现JWT的生成。以下是一个简单的示例代码:

const crypto = require('crypto');

function base64UrlEncode(input) {
    return Buffer.from(input).toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
}

function generateJWT(payload, secret) {
    const header = {
        alg: 'HS256',
        typ: 'JWT'
    };

    const encodedHeader = base64UrlEncode(JSON.stringify(header));
    const encodedPayload = base64UrlEncode(JSON.stringify(payload));
    const signature = crypto
        .createHmac('sha256', secret)
        .update(encodedHeader + '.' + encodedPayload)
        .digest('base64')
        .replace(/\+/g, '-')
        .replace(/\//g, '_')
        .replace(/=/g, '');

    return `${encodedHeader}.${encodedPayload}.${signature}`;
}

const payload = {
    sub: '1234567890',
    name: 'John Doe',
    iat: Math.floor(Date.now() / 1000),
    exp: Math.floor(Date.now() / 1000) + (60 * 60)  // 1小时过期
};

const secret = 'secret';
console.log('Generated JWT:', generateJWT(payload, secret));

以上代码实现了JWT的生成,包括Base64编码和HMACSHA256签名。

如何验证JWT

验证JWT是确保JWT完整性和真实性的重要步骤,通常包括以下步骤:

  1. 分割JWT:将JWT的字符串分割成头部、载荷和签名三部分。
  2. 解析头部:从头部中获取签名算法。
  3. 重新计算签名:使用头部中的算法和密钥,重新计算JWT的签名。
  4. 对比签名:将重新计算的签名与JWT中的签名进行比较,判断JWT是否有效。
验证JWT的基本步骤

以下是一个简单的验证JWT的步骤:

  1. 分割JWT
    const [encodedHeader, encodedPayload, signature] = token.split('.');
  2. 解析头部
    const header = JSON.parse(Buffer.from(encodedHeader, 'base64').toString('utf8'));
  3. 重新计算签名
    const computedSignature = crypto
       .createHmac(header.alg, secret)
       .update(encodedHeader + '.' + encodedPayload)
       .digest('base64')
       .replace(/\+/g, '-')
       .replace(/\//g, '_')
       .replace(/=/g, '');
  4. 对比签名
    if (computedSignature === signature) {
       console.log('JWT is valid');
       const payload = JSON.parse(Buffer.from(encodedPayload, 'base64').toString('utf8'));
       console.log(payload);
    } else {
       console.log('JWT is invalid');
    }

以上代码验证了JWT的有效性,并解析了JWT中的载荷。

使用库验证JWT

使用库验证JWT可以更简洁地实现验证过程。以下示例使用Node.js的jsonwebtoken库验证JWT:

const jwt = require('jsonwebtoken');

const secret = 'secret';

try {
    const decoded = jwt.verify(token, secret);
    console.log('JWT is valid');
    console.log(decoded);
} catch (error) {
    console.log('JWT is invalid');
    console.log(error.name);
}

以上代码使用jwt.verify函数验证JWT的有效性,并解析JWT中的载荷。

JWT的应用实例

JWT在实际应用中可以用于实现用户认证和授权。以下是一个简单的用户认证示例。

实现用户认证

用户认证是JWT的一个典型应用,用户登录后返回一个JWT,后续的请求中携带JWT进行身份验证。

以下是一个简单的用户认证示例代码:

const jwt = require('jsonwebtoken');

const secret = 'secret';

// 用户登录
const login = (username, password) => {
    // 模拟用户验证过程
    if (username === 'john' && password === 'secret') {
        const payload = {
            sub: '1234567890',
            name: 'John Doe',
            iat: Math.floor(Date.now() / 1000),
            exp: Math.floor(Date.now() / 1000) + (60 * 60)  // 1小时过期
        };

        const token = jwt.sign(payload, secret, { expiresIn: '1h' });

        return token;
    }

    return null;
};

// 验证JWT
const verifyToken = (token) => {
    try {
        const decoded = jwt.verify(token, secret);
        console.log('JWT is valid');
        console.log(decoded);
        return decoded;
    } catch (error) {
        console.log('JWT is invalid');
        console.log(error.name);
        return null;
    }
};

// 用户登录
const token = login('john', 'secret');
if (token) {
    console.log('Login successful');
    console.log(`Token: ${token}`);
} else {
    console.log('Login failed');
}

// 验证JWT
const decoded = verifyToken(token);
if (decoded) {
    console.log('Access granted');
} else {
    console.log('Access denied');
}

以上代码实现了用户登录和JWT的生成,并验证了JWT的有效性。

JWT的安全性考虑

JWT虽然提供了许多便利,但也面临着一些潜在的安全风险。了解这些风险并采取措施提升安全性是非常重要的。

潜在的安全风险
  1. 密钥泄露:如果密钥泄露,攻击者可以伪造有效的JWT,导致身份验证失效。
  2. 过期时间设置不当:过长的过期时间会使JWT的安全性降低,过短则频繁请求JWT生成。
  3. 中间人攻击:在传输过程中,JWT可能被截获或篡改,导致攻击者冒充合法用户。
  4. CSRF攻击:如果JWT存储在Cookies中,且没有设置HttpOnly属性,攻击者可以通过CSRF攻击获取JWT。
  5. 跨域攻击:如果JWT通过前端JavaScript代码传递,可能存在跨域攻击的风险。
如何提升JWT的安全性
  1. 使用HTTPS:确保JWT在传输过程中通过HTTPS协议,以防止中间人攻击。
  2. 设置过期时间:合理设置JWT的过期时间,通常建议设置较短的过期时间(如1小时)。
  3. 使用强密钥:确保密钥足够复杂且不易被猜测,定期更换密钥。
  4. 不存储在Cookies中:如果必须存储JWT在Cookies中,确保设置HttpOnly属性,并使用SameSite属性防止CSRF攻击。
  5. 使用刷新令牌:在JWT即将过期时,使用刷新令牌获取新的JWT,避免频繁请求JWT生成。
  6. 限制JWT的使用范围:确保JWT仅被授权的服务使用,防止JWT被滥用。

以下是一些具体的代码示例来展示如何设置HttpOnly属性和SameSite属性,以及如何使用刷新令牌获取新的JWT:

const httpOnlyCookie = {
    httpOnly: true,
    sameSite: 'strict'
};

// 使用刷新令牌获取新的JWT的示例代码
const refreshJWT = (refreshToken, secret) => {
    try {
        const decoded = jwt.verify(refreshToken, secret);
        const payload = {
            sub: decoded.sub,
            name: decoded.name,
            iat: Math.floor(Date.now() / 1000),
            exp: Math.floor(Date.now() / 1000) + (60 * 60)  // 1小时过期
        };

        return jwt.sign(payload, secret, { expiresIn: '1h' });
    } catch (error) {
        return null;
    }
};
点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消