无感刷新 token 的秘密武器:兼顾安全性与用户体验
2023-12-10 05:57:17
无感刷新令牌:让身份验证变得无缝
前言
在现代网络应用程序中,令牌扮演着至关重要的角色。它们作为用户身份的凭证,在客户端和服务器之间传递,以验证用户的合法性。然而,令牌通常都有一个有效期,超过这个期限,令牌将失效,用户需要重新登录。
频繁的登录不仅会打断用户的使用体验,而且会增加用户忘记密码的风险。因此,无感刷新令牌 应运而生。它允许在令牌过期之前自动刷新,从而使用户无需重新登录即可继续使用应用程序。
无感刷新令牌的原理
无感刷新令牌的原理并不复杂,但它需要前端和后端的密切配合。
前端
前端负责发起刷新令牌的请求。当检测到令牌即将过期时,前端会向后端发送一个刷新令牌的请求。这个请求通常包含用户的登录信息和当前的令牌。
后端
后端负责验证刷新令牌的有效性,并返回一个新的令牌。如果刷新令牌有效,后端会生成一个新的令牌,并将其返回给前端。前端收到新的令牌后,会将其存储起来,以便在需要时使用。
无感刷新令牌的具体步骤
前端
- 在前端代码中,使用定时器或其他机制来检测令牌的过期时间。
- 当检测到令牌即将过期时,向后端发送一个刷新令牌的请求。
- 在请求中包含用户的登录信息和当前的令牌。
- 将收到的新的令牌存储起来,以便在需要时使用。
后端
- 验证刷新令牌的有效性。
- 如果刷新令牌有效,生成一个新的令牌,并将其返回给前端。
- 如果刷新令牌无效,返回一个错误信息。
如何在前端实现无感刷新令牌
在前端,可以使用请求拦截器来实现无感刷新令牌。请求拦截器是一个特殊的函数,它会在每次发送请求之前被调用。我们可以利用这个函数来检测请求的令牌是否即将过期,如果即将过期,则自动发送一个刷新令牌的请求。
以下是一个使用 Axios 库实现无感刷新令牌的示例:
// 在 Axios 实例中注册请求拦截器
axios.interceptors.request.use((config) => {
// 获取当前的令牌
const token = localStorage.getItem('token');
// 如果令牌即将过期,则发送一个刷新令牌的请求
if (isTokenExpiring(token)) {
// 发送刷新令牌的请求
const newToken = await refreshToken(token);
// 将新的令牌存储起来
localStorage.setItem('token', newToken);
// 将新的令牌添加到请求头中
config.headers['Authorization'] = `Bearer ${newToken}`;
}
// 返回请求配置
return config;
});
如何在后端实现无感刷新令牌
在后端,可以使用数据库或其他持久化存储机制来存储用户的刷新令牌。当用户登录时,后端会生成一个刷新令牌并将其存储起来。当用户发送刷新令牌的请求时,后端会验证刷新令牌的有效性,并返回一个新的令牌。
以下是一个使用 Node.js 和 MongoDB 实现无感刷新令牌的示例:
// 引入必要的库
const mongoose = require('mongoose');
const jwt = require('jsonwebtoken');
// 定义刷新令牌的模型
const RefreshToken = mongoose.model('RefreshToken', {
userId: String,
token: String,
createdAt: Date,
expiresAt: Date,
});
// 生成刷新令牌
const generateRefreshToken = (userId) => {
// 设置刷新令牌的有效期
const expiresIn = 60 * 60 * 24 * 7; // 7 天
// 创建刷新令牌
const token = jwt.sign({ userId }, process.env.REFRESH_TOKEN_SECRET, {
expiresIn,
});
// 返回刷新令牌
return token;
};
// 验证刷新令牌
const verifyRefreshToken = (token) => {
// 验证刷新令牌
const decoded = jwt.verify(token, process.env.REFRESH_TOKEN_SECRET);
// 返回解码后的数据
return decoded;
};
// 创建新的访问令牌
const generateAccessToken = (userId) => {
// 设置访问令牌的有效期
const expiresIn = 60 * 60; // 1 小时
// 创建访问令牌
const token = jwt.sign({ userId }, process.env.ACCESS_TOKEN_SECRET, {
expiresIn,
});
// 返回访问令牌
return token;
};
// 刷新令牌
const refreshToken = async (token) => {
// 验证刷新令牌
const decoded = verifyRefreshToken(token);
// 如果刷新令牌无效,则返回错误信息
if (!decoded) {
throw new Error('Invalid refresh token');
}
// 根据用户 ID 获取刷新令牌
const refreshToken = await RefreshToken.findOne({ userId: decoded.userId });
// 如果刷新令牌不存在,则返回错误信息
if (!refreshToken) {
throw new Error('Refresh token not found');
}
// 如果刷新令牌已过期,则返回错误信息
if (refreshToken.expiresAt < Date.now()) {
throw new Error('Refresh token expired');
}
// 创建新的访问令牌
const accessToken = generateAccessToken(decoded.userId);
// 返回新的访问令牌
return accessToken;
};
// 导出模块
module.exports = {
generateRefreshToken,
verifyRefreshToken,
generateAccessToken,
refreshToken,
};
总结
无感刷新令牌是一种非常有用的技术,它可以改善用户体验并提高安全性。通过在前端和后端实现无感刷新令牌,我们可以为用户提供更便捷、更安全的登录体验。
常见问题解答
- 无感刷新令牌和传统令牌之间有什么区别?
传统令牌需要用户在过期后重新登录,而无感刷新令牌会在过期之前自动刷新,无需用户干预。
- 如何保护刷新令牌免受盗用?
刷新令牌应存储在安全的位置,例如 HTTP 仅限 cookie 中。此外,刷新令牌应设置较短的有效期,以限制其被盗用后的影响。
- 什么时候应该使用无感刷新令牌?
当需要让用户保持登录状态时,应使用无感刷新令牌。这对于像社交媒体应用程序和电子商务网站这样的应用程序非常有用,用户经常在这些应用程序中执行任务。
- 无感刷新令牌的缺点是什么?
无感刷新令牌可能会增加安全风险,因为如果用户的设备被盗,则可以用来访问他们的帐户。此外,无感刷新令牌可能会影响应用程序的性能,因为它们需要在后台持续发送请求。
- 如何禁用无感刷新令牌?
如果应用程序不需要无感刷新令牌,可以通过在前端拦截刷新请求并返回错误响应来禁用它们。