一、什么是双 Token 机制?
双Token机制是通过两种令牌管理用户认证与授权的方案,核心令牌包括:
- Access Token(访问令牌)
- 用于身份验证和授权,每次请求时携带。
- 有效期短(如15分钟),降低泄漏风险。
- Refresh Token(刷新令牌)
- 用于在Access Token过期后获取新令牌。
- 有效期长(如7天),支持无缝续期。
二、JWT 与双 Token 机制的关系
1. JWT的结构
JWT是双Token机制中Access Token的常用格式,由三部分组成:
- Header(头部):指定令牌类型(JWT)和签名算法(如HMAC SHA256)。
- Payload(负载):包含用户标识(sub)、过期时间(exp)、权限等声明。
- Signature(签名):使用密钥对头部和负载签名,验证数据完整性。
2. 双Token的分工
- Access Token:采用JWT格式,携带用户认证信息。
- Refresh Token:独立令牌,用于刷新Access Token,通常存储于安全位置。
三、双 Token 的工作流程
-
用户登录
- 用户提交用户名/密码,服务器验证通过后返回Access Token(短时效)和Refresh Token(长时效)。
-
正常访问流程
- 客户端在请求头中携带Access Token(如
Authorization: Bearer <token>
)。 - 服务器验证Access Token有效后,返回请求资源。
- 客户端在请求头中携带Access Token(如
-
Access Token过期
- 客户端检测到Access Token过期,向服务器发送Refresh Token。
- 服务器验证Refresh Token有效,生成新的Access Token并返回。
-
Refresh Token过期
- 若Refresh Token失效,用户需重新登录以获取新的双Token。
四、双 Token 机制的优势
1. 提高安全性
- Access Token存储:存于内存(如JavaScript变量),页面刷新即清空,减少持久化泄漏风险。
- Refresh Token存储:通过
HttpOnly
Cookie存储,防止XSS攻击窃取令牌。
2. 无缝用户体验
- 自动刷新Access Token,避免频繁登录,适合长会话场景(如电商、社交应用)。
3. 无状态认证
- JWT自身携带用户信息,服务器无需存储会话状态,仅需验证Refresh Token(可选存储于数据库)。
五、如何实现双 Token 机制?
1. 生成令牌(代码示例)
const jwt = require('jsonwebtoken');
// 生成Access Token(15分钟过期)
function generateAccessToken(user) {
return jwt.sign(
{ sub: user.id, role: user.role }, // 负载信息
'access_secret_key', // 签名密钥
{ expiresIn: '15m' }
);
}
// 生成Refresh Token(7天过期)
function generateRefreshToken(user) {
return jwt.sign(
{ sub: user.id }, // 简化负载(仅需用户标识)
'refresh_secret_key',
{ expiresIn: '7d' }
);
}
2. 存储Refresh Token
- 服务器端:存入数据库,关联用户ID(适合高安全场景)。
- 客户端:通过
HttpOnly
Cookie存储(需配合Secure属性,避免明文传输)。
3. 验证Access Token
function verifyAccessToken(req, res, next) {
const token = req.headers.authorization?.split(' ')[1]; // 解析Bearer Token
if (!token) return res.status(401).json({ message: '缺少访问令牌' });
jwt.verify(token, 'access_secret_key', (err, decoded) => {
if (err) return res.status(403).json({ message: '令牌无效或过期' });
req.user = decoded; // 将用户信息附加到请求对象
next(); // 继续处理请求
});
}
4. 刷新Access Token
function refreshTokenHandler(req, res) {
const refreshToken = req.body.refreshToken; // 从请求体获取Refresh Token
// 验证Refresh Token
jwt.verify(refreshToken, 'refresh_secret_key', (err, user) => {
if (err) return res.status(403).json({ message: '刷新令牌无效' });
// 生成新的Access Token
const newAccessToken = generateAccessToken(user);
res.json({ accessToken: newAccessToken });
});
}
除非注明,否则均为李锋镝的博客原创文章,转载必须以链接形式标明本文链接
文章评论