JWT(JSON Web Token)是一种用于在各方之间安全地传输信息的紧凑且自包含的令牌。它通常用于身份验证和信息交换。JWT 由三部分组成:头部(Header)、载荷(Payload)和签名(Signature)。
JWT 的结构
一个 JWT 令牌通常由三个部分组成,每部分之间用点(.)分隔:
header.payload.signature
1. 头部(Header)
头部通常包含两部分:令牌类型(即 JWT)和所使用的签名算法(如 HMAC SHA256 或 RSA)。
示例:
{
"alg": "HS256",
"typ": "JWT"
}
然后将头部进行 Base64Url 编码,得到 JWT 的第一部分。
2. 载荷(Payload)
载荷部分包含声明(claims),声明是关于实体(通常是用户)和其他数据的声明。声明有三种类型:注册声明、公共声明和私有声明。
示例:
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
然后将载荷进行 Base64Url 编码,得到 JWT 的第二部分。
3. 签名(Signature)
签名部分是为了确保令牌在传输过程中不被篡改。首先将头部和载荷进行 Base64Url 编码,然后使用指定的签名算法和一个密钥对它们进行签名。
示例(使用 HMAC SHA256 算法):
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret
)
完整的 JWT 示例
假设我们有以下头部和载荷:
头部:
{
"alg": "HS256",
"typ": "JWT"
}
载荷:
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
使用密钥 your-256-bit-secret 进行签名,生成的 JWT 可能如下所示:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
JWT 的使用场景
- 身份验证:JWT 最常见的使用场景是身份验证。服务器在用户登录时生成一个 JWT,并将其返回给客户端。客户端在后续请求中将 JWT 附加到请求头中,服务器通过验证 JWT 来确认用户身份。
- 信息交换:JWT 可以安全地传输信息,因为它们可以被签名。例如,使用公钥/私钥对 JWT 进行签名,接收方可以验证 JWT 的真实性。
示例:使用 Node.js 和 Express 生成和验证 JWT
以下是一个简单的示例,展示如何使用 Node.js 和 Express 生成和验证 JWT:
const express = require('express');
const jwt = require('jsonwebtoken');
const bodyParser = require('body-parser');
const app = express();
const PORT = 3000;
const SECRET_KEY = 'your_secret_key';
app.use(bodyParser.json());
// Mock user data
const users = {
user1: 'password1',
user2: 'password2'
};
// Login route
app.post('/login', (req, res) => {
const { username, password } = req.body;
if (users[username] && users[username] === password) {
const token = jwt.sign({ username }, SECRET_KEY, { expiresIn: '1h' });
res.json({ token });
} else {
res.status(401).send('Invalid credentials');
}
});
// Protected route
app.get('/protected', (req, res) => {
const token = req.headers['authorization'];
if (!token) {
return res.status(403).send('Token is required');
}
try {
const decoded = jwt.verify(token, SECRET_KEY);
res.json({ message: 'This is a protected resource', user: decoded.username });
} catch (err) {
res.status(401).send('Invalid token');
}
});
app.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}`);
});
这个示例展示了如何生成和验证 JWT。用户通过 /login 路由进行认证,成功后获得一个 JWT。访问受保护资源时,用户需要在请求头中包含该 JWT,服务器验证令牌后授予访问权限。
验证与失效的边界
前文的三段式结构是常见 JWS 紧凑表示,不能据此概括所有 JWT 形式;加密 JWT 还涉及 JWE。Base64URL 编码不是加密,签名 JWT 的载荷通常可被持有者读取,因此不应放入不应泄露的秘密。
验证时应显式约束允许的算法,并按协议校验签发者、受众、有效期及令牌用途,不能仅凭解码成功或签名通过就授予任意资源权限。
自包含令牌不会因为前端删除副本就自动撤销。短有效期、服务端撤销记录或其他会话机制各有代价,应按实际退出与权限更新要求选择。
前面的 Express 代码用于说明流程;硬编码密钥、明文示例密码和直接读取整个 authorization 头不能作为完整生产认证方案。实际方案还需安全存储凭据、按所用认证方案解析请求头,并与资源级授权配合。