JWT学习是一个全面的指南,涵盖了JWT的基本概念、工作原理及其组成部分。本文详细介绍了JWT在用户认证、信息交换和API访问控制等场景中的应用,并提供了JWT创建、解码和验证的基础操作示例。
JWT学习:从入门到实践的全面指南 1. JWT简介1.1 什么是JWT
JWT(JSON Web Token)是一种开放的标准(RFC 7519),用于在网络应用环境间安全地将信息从客户端传递到服务器端。JWT基于一种紧凑、自包含的、可传递的格式,通常用于身份验证(登录)、信息交换或访问控制等场景。
1.2 JWT的工作原理
JWT的工作原理可以概括为以下三个步骤:
- 生成JWT:服务器生成一个安全的token,包括头信息和载荷,然后用密钥签名。
- 传递JWT:客户端接收JWT,并将其保存在本地。通常保存在浏览器的
localStorage
中或者sessionStorage
中,或者是作为HTTP请求的头信息传递。 - 验证JWT:每次客户端向服务器发送请求时,都需要携带JWT。服务器收到请求后,会验证这个JWT是否有效。这包括检查签名是否正确以及载荷是否有效。
以下是JWT生成、传递及验证的代码示例:
JWT生成示例
const jwt = require('jsonwebtoken');
const secret = 'yourSecretKey';
// 生成JWT
const token = jwt.sign(
{
user: 'John Doe',
id: 12345
},
secret,
{
expiresIn: '1h'
}
);
console.log(token);
JWT验证示例
const jwt = require('jsonwebtoken');
const token = 'yourTokenString';
// 验证JWT
jwt.verify(token, secret, (err, decoded) => {
if (err) {
console.log('Invalid token');
} else {
console.log('Token is valid', decoded);
}
});
1.3 JWT的三个部分介绍
JWT由三部分组成,中间由“.”分隔:
-
Header(头部):包含令牌的类型(总是为
JWT
)和所使用的签名算法(例如HMAC SHA256
或RSA
),通常由base64
编码。示例代码:
{ "alg": "HS256", "typ": "JWT" }
经过
base64
编码后的结果为:eyJsYWciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9
-
Payload(载荷):载荷可以用来存放声明,JWT的标准声明可参考JWT标准文档。载荷部分也经过了
base64
编码。例如,载荷可以包含用户的身份信息,如用户ID、用户名、过期时间等。示例代码:
{ "sub": "1234567890", "name": "John Doe", "iat": 1516239022 }
经过
base64
编码后的结果为:eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
-
Signature(签名):签名确保了JWT的完整性和真实性。它使用头部中指定的算法,通过特定密钥签名载荷部分。签名也经过了
base64
编码。示例代码:
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
例如,如果使用
HS256
算法,签名如下:eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ==.dGJjZmJjZmI=
2.1 用户认证
JWT非常适合用于用户认证。认证时,服务器验证JWT的签名,并将JWT附加到返回的响应中,客户端则在后续的请求中将JWT作为认证信息包含在请求头中,这样可以减少数据库查询次数,提高系统性能和安全性。
JWT用户认证示例
const jwt = require('jsonwebtoken');
const secret = 'yourSecretKey';
app.post('/login', (req, res) => {
const user = req.body; // 假设这里是从数据库获取的用户信息
const token = jwt.sign(user, secret, { expiresIn: '1h' });
res.json({ token });
});
app.use((req, res, next) => {
const token = req.headers['authorization'];
if (token) {
jwt.verify(token, secret, (err, decoded) => {
if (err) {
res.status(403).json({ message: 'Invalid token' });
} else {
req.decoded = decoded;
next();
}
});
} else {
res.status(403).json({ message: 'No token provided' });
}
});
app.get('/protected', (req, res) => {
res.json({ message: 'Protected content', user: req.decoded });
});
2.2 信息交换
JWT可以用来安全交换信息。例如,一个API可以将用户信息作为JWT发送给另一个API,接收方可以验证JWT并了解发送方的身份和信息。
JWT信息交换示例
const jwt = require('jsonwebtoken');
const secret = 'yourSecretKey';
// 发送JWT
const token = jwt.sign(
{
user: 'John Doe',
id: 12345
},
secret,
{
expiresIn: '1h'
}
);
// 接收和验证JWT
jwt.verify(token, secret, (err, decoded) => {
if (err) {
console.log('Invalid token');
} else {
console.log('Token is valid', decoded);
}
});
2.3 API访问控制
JWT也可以用于API访问控制。通过验证JWT,服务器可以确定请求方是否有权限访问特定资源或执行特定操作。
JWT API访问控制示例
const jwt = require('jsonwebtoken');
const secret = 'yourSecretKey';
app.get('/api/resource', (req, res) => {
const token = req.headers['authorization'];
if (token) {
jwt.verify(token, secret, (err, decoded) => {
if (err) {
res.status(403).json({ message: 'Invalid token' });
} else {
res.json({ message: 'Access granted', user: decoded });
}
});
} else {
res.status(403).json({ message: 'No token provided' });
}
});
3. JWT基础操作
3.1 创建JWT
创建JWT包括生成头部、载荷然后签名。例如,使用Node.js创建JWT,可以使用jsonwebtoken
库。
const jwt = require('jsonwebtoken');
const token = jwt.sign(
{
user: 'John Doe',
id: 12345
},
'secret',
{
expiresIn: '1h'
}
);
console.log(token);
3.2 解码JWT
解码JWT不需要验证其签名,只需要将JWT字符串拆分成头部、载荷和签名三部分,然后使用base64
解码头部和载荷部分。
示例代码:
const jwt = require('jsonwebtoken');
const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxhPQpJ4dGOWD3YoKwFf0C1Z4CpSb4KZx168DR4';
const decoded = jwt.decode(token, { complete: true });
console.log(decoded);
3.3 验证JWT
验证JWT涉及检查签名是否正确以及过期时间等。例如,使用Node.js验证JWT。
示例代码:
const jwt = require('jsonwebtoken');
const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxhPQpJ4dGOWD3YoKwFf0C1Z4CpSb4KZx168DR4';
try {
const decoded = jwt.verify(token, 'secret');
console.log('Token is valid', decoded);
} catch (error) {
console.log('Token is not valid', error);
}
4. 使用JWT的注意事项
4.1 签名算法选择
选择合适的安全算法对于JWT的安全性至关重要。最常用的算法包括HS256
(使用共享密钥的HMAC算法)和RS256
(使用公钥和私钥的RSA算法)。选择算法时要考虑到安全性和复杂性。
4.2 令牌过期策略
设定合理的过期时间可以限制JWT的有效期,减少JWT被滥用的风险。过期时间可以设置为5分钟
、1小时
或更长,具体取决于应用需求。
4.3 令牌刷新机制
为了避免用户频繁重新登录,可以实现JWT的刷新机制。例如,可以在JWT过期前发起刷新请求,服务器返回新的JWT。
5. 实战演练5.1 使用Node.js实现JWT认证
以一个简单的Node.js应用程序为例,演示如何使用JWT进行用户认证。首先安装必要的库:
npm install express jsonwebtoken
示例代码:
const express = require('express');
const jwt = require('jsonwebtoken');
const app = express();
const secret = 'yoursecret';
app.post('/login', (req, res) => {
const user = req.body; // 假设这里是从数据库获取的用户信息
const token = jwt.sign(user, secret, { expiresIn: '1h' });
res.json({ token });
});
app.use((req, res, next) => {
const token = req.headers['authorization'];
if (token) {
jwt.verify(token, secret, (err, decoded) => {
if (err) {
res.status(403).json({ message: 'Invalid token' });
} else {
req.decoded = decoded;
next();
}
});
} else {
res.status(403).json({ message: 'No token provided' });
}
});
app.get('/protected', (req, res) => {
res.json({ message: 'Protected content', user: req.decoded });
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
5.2 使用Python实现JWT认证
以一个简单的Python Flask应用程序为例,演示如何使用JWT进行用户认证。首先安装必要的库:
pip install Flask
pip install Flask-JWT-Extended
示例代码:
from flask import Flask, request, jsonify
from flask_jwt_extended import JWTManager, jwt_required, create_access_token
app = Flask(__name__)
app.config['JWT_SECRET_KEY'] = 'yoursecret'
jwt = JWTManager(app)
@app.route('/login', methods=['POST'])
def login():
user = request.json.get('user', None)
if user:
token = create_access_token(identity=user, expires_delta=False)
return jsonify(token=token)
return jsonify({'msg': 'Missing user parameter'}), 400
@app.route('/protected', methods=['GET'])
@jwt_required()
def protected():
return jsonify({'msg': 'Access protected content', 'user': get_jwt_identity()})
if __name__ == '__main__':
app.run()
6. 常见问题解答
6.1 JWT安全性如何保证
JWT的安全性依赖于签名算法和密钥的安全性。密钥应该保密,只有服务器知道。JWT应通过HTTPS传输,以防止中间人攻击。同时,确保载荷中的数据不包含敏感信息。
6.2 如何处理过期的JWT
过期的JWT可以使用刷新机制来处理。当JWT即将过期时,客户端可以发起刷新请求,服务器验证身份后返回新的JWT。
6.3 JWT与Cookie的区别
- 存储:JWT存储在前端,可以存储在本地存储或HTTP头中;Cookie存储在服务器端,由浏览器自动管理。
- 安全性:JWT不依赖于服务器状态,可以通过HTTPS传输保证安全;Cookie在未加密的HTTP连接中容易被窃取。
- 跨域:JWT可以在任何站点间传递,而Cookie只能传递到同源站点。
- 大小限制:JWT的大小限制较小,适合存储少量信息;Cookie的大小限制较大,可以存储更多数据。
JWT适用于不需要服务器状态保持的场景,而Cookie适合需要服务器状态保持的场景。