各位观众老爷,晚上好!今天咱们聊聊 JWT,这玩意儿在 Web 安全里可是个香饽饽。咱们争取用最接地气的方式,把这 JWT 扒个底朝天,让各位都能用得溜溜的。
啥是 JWT?别跟我拽文,说人话!
JWT (JSON Web Token),你可以理解成一个加密过的身份证明。就像你出门要带身份证一样,你的程序要访问某些受保护的资源,也得带着 JWT。这玩意儿长得像一堆乱码,但里面包含了你的身份信息,以及一些其他的声明。服务器收到 JWT 之后,会验证这个 JWT 是不是它自己颁发的,有没有被篡改过,如果都没问题,那就允许你访问资源。
最关键的是,JWT 是无状态的。啥叫无状态?就是服务器不需要记住你的登录状态。传统的 Session 认证,服务器需要用一个 Session ID 来记住你,这玩意儿多了服务器就累死了。而 JWT 呢,服务器只需要验证一下你的 JWT 就行了,验证完了就忘了你,下次来还是一样验证,省心省力。
JWT 的结构:拆开看看里面有啥?
一个 JWT 实际上就是一个字符串,由三个部分组成,用点号 (.
) 分隔:
- Header (头部):描述 JWT 的元数据,比如用什么算法加密的。
- Payload (载荷):存放实际的数据,比如用户 ID、过期时间等等。
- Signature (签名):对 Header 和 Payload 进行加密,防止 JWT 被篡改。
这三个部分都是经过 Base64 编码的。Base64 编码不是加密,只是把二进制数据转换成文本数据,方便传输。
咱们来个例子:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
- Header:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
解码后是{"alg":"HS256","typ":"JWT"}
- Payload:
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
解码后是{"sub":"1234567890","name":"John Doe","iat":1516239022}
- Signature:
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Header:告诉服务器怎么加密的
Header 一般包含两个信息:
alg
(Algorithm):使用的加密算法,比如HS256
(HMAC SHA256),RS256
(RSA SHA256) 等等。typ
(Type):JWT 的类型,一般都是JWT
。
Payload:放点啥好呢?
Payload 里面可以放任何你想放的数据,但最好放一些和用户身份相关的信息,比如用户 ID、用户名、权限等等。JWT 标准也定义了一些常用的字段 (Claims):
iss
(Issuer):JWT 的签发者。sub
(Subject):JWT 的主题,一般是用户 ID。aud
(Audience):JWT 的接收者。exp
(Expiration Time):JWT 的过期时间。nbf
(Not Before):JWT 的生效时间。iat
(Issued At):JWT 的签发时间。jti
(JWT ID):JWT 的唯一标识符。
Signature:防止 JWT 被篡改
Signature 是 JWT 最重要的部分,它用来验证 JWT 的完整性。服务器会用一个密钥 (Secret Key) 和 Header 中指定的算法,对 Header 和 Payload 进行加密,生成 Signature。当客户端发送 JWT 给服务器时,服务器会用同样的密钥和算法,重新计算 Signature,然后和客户端发送的 Signature 进行比较,如果一样,就说明 JWT 没有被篡改过。
JWT 认证流程:一步一步来
- 用户登录: 用户输入用户名和密码,发送给服务器。
- 服务器验证: 服务器验证用户名和密码是否正确。
- 生成 JWT: 如果用户名和密码正确,服务器会生成一个 JWT,里面包含用户的身份信息。
- 返回 JWT: 服务器把 JWT 返回给客户端。
- 客户端保存 JWT: 客户端把 JWT 保存起来,比如保存在 localStorage 或 Cookie 中。
- 访问受保护的资源: 客户端每次访问受保护的资源时,都会在请求头中带上 JWT。
- 服务器验证 JWT: 服务器收到请求后,会验证 JWT 的有效性,如果有效,就允许客户端访问资源。
代码演示:用 Node.js 生成和验证 JWT
这里咱们用 Node.js 和 jsonwebtoken
库来演示 JWT 的生成和验证。
首先,安装 jsonwebtoken
库:
npm install jsonwebtoken
然后,创建一个 jwt.js
文件:
const jwt = require('jsonwebtoken');
// 密钥 (Secret Key),一定要保密!
const secretKey = 'your_secret_key';
// 生成 JWT
function generateToken(payload) {
const token = jwt.sign(payload, secretKey, { expiresIn: '1h' }); // 设置过期时间为 1 小时
return token;
}
// 验证 JWT
function verifyToken(token) {
try {
const decoded = jwt.verify(token, secretKey);
return decoded; // 返回解码后的 payload
} catch (err) {
return null; // JWT 无效
}
}
// 示例
const payload = {
userId: 123,
username: 'testUser',
role: 'admin'
};
const token = generateToken(payload);
console.log('Generated Token:', token);
const decoded = verifyToken(token);
if (decoded) {
console.log('Decoded Payload:', decoded);
} else {
console.log('Token is invalid.');
}
module.exports = {
generateToken,
verifyToken
};
这个代码做了以下几件事:
- 引入
jsonwebtoken
库。 - 定义了一个密钥
secretKey
,这个密钥一定要保密! generateToken
函数用来生成 JWT,接收一个 payload 参数,payload 就是你想放在 JWT 里面的数据。verifyToken
函数用来验证 JWT,接收一个 token 参数,如果 token 有效,就返回解码后的 payload,如果无效,就返回 null。- 最后,我们用一个例子来演示 JWT 的生成和验证。
重要的事情说三遍:密钥一定要保密!密钥一定要保密!密钥一定要保密!
JWT 的安全问题:别掉以轻心
虽然 JWT 很方便,但是也存在一些安全问题,需要注意:
- 密钥泄露: 如果密钥泄露了,任何人都可以伪造 JWT,冒充你的用户。所以,密钥一定要保密!不要把密钥放在代码里,最好放在环境变量或者配置文件里。
- 算法选择: 尽量使用非对称加密算法,比如 RS256。对称加密算法 (比如 HS256) 的密钥只有一份,一旦泄露,所有 JWT 都会失效。而非对称加密算法有公钥和私钥两把钥匙,私钥用来签名 JWT,公钥用来验证 JWT,即使公钥泄露了,也无法伪造 JWT。
- JWT 过期时间: JWT 的过期时间一定要设置,不然 JWT 就会一直有效,即使你的用户已经退出了登录。
- 防止重放攻击: 重放攻击是指攻击者截获到你的 JWT,然后重复发送这个 JWT,冒充你的用户。为了防止重放攻击,可以在 JWT 中添加一个
jti
(JWT ID) 字段,每次验证 JWT 时,都检查这个jti
是否已经使用过。 - 存储安全: 客户端存储 JWT 的地方也需要注意安全。如果保存在 localStorage 中,容易被 XSS 攻击窃取。如果保存在 Cookie 中,要注意设置
HttpOnly
和Secure
属性,防止 XSS 和 MITM 攻击。
最佳实践:用得漂亮点
- 使用 HTTPS: 所有的 API 请求都应该使用 HTTPS,防止 JWT 被窃听。
- 验证所有 Claims: 不仅仅要验证 Signature,还要验证
iss
、aud
、exp
等等 Claims,确保 JWT 是由你信任的签发者签发的,是给你的应用使用的,并且没有过期。 - 使用 Refresh Token: JWT 的过期时间不宜设置太长,但如果每次访问资源都要重新登录,用户体验会很差。可以使用 Refresh Token 来解决这个问题。Refresh Token 是一个长期有效的 Token,用来刷新 JWT。当 JWT 过期时,客户端可以用 Refresh Token 向服务器请求一个新的 JWT,而不需要重新登录。
- 黑名单机制: 有时候,你需要让一个 JWT 立即失效,比如用户注销登录或者修改了密码。可以使用黑名单机制来实现这个功能。黑名单是一个存储无效 JWT 的列表,每次验证 JWT 时,都检查这个 JWT 是否在黑名单中。
JWT vs Session:选哪个好?
特性 | JWT | Session |
---|---|---|
状态 | 无状态 | 有状态 |
服务器压力 | 低 | 高 |
可扩展性 | 高 | 低 |
安全性 | 需要注意密钥安全、防止 XSS 和 CSRF | 需要注意 Session ID 安全、防止 Session Hijacking 和 Fixation |
适用场景 | 分布式系统、API 认证 | 单体应用、需要存储更多用户信息的场景 |
总的来说,JWT 更适合分布式系统和 API 认证,Session 更适合单体应用和需要存储更多用户信息的场景。
总结:JWT 虽好,用起来要小心
JWT 是一种非常方便的认证方式,可以简化服务器的压力,提高系统的可扩展性。但是,JWT 也存在一些安全问题,需要注意。只有充分理解 JWT 的原理和安全风险,才能用得放心,用得舒心。
希望今天的讲解对大家有所帮助。如果有什么问题,欢迎提问! 下课!