登录续约接口的调用频率取决于几个因素,包括应用的安全需求、用户体验要求以及具体的实现方式。以下是一些常见的做法:
-
Token 有效期:通常,登录续约接口(refresh token)的调用频率取决于访问令牌(access token)的有效期。如果 access token 有效期为 1 小时,那么在 1 小时后需要调用续约接口以获取新的 access token。
-
用户活动:有些应用会基于用户的活动来决定何时调用续约接口。例如,如果用户在一段时间内没有任何操作,应用可能会提示用户重新登录或自动调用续约接口。
-
前端实现:
- 定时器:前端可以使用定时器(如
setTimeout)在 access token 即将过期前几分钟调用续约接口。 - 拦截器:在请求发送之前拦截请求并检查 token 是否有效,如果即将过期则先调用续约接口。
- 定时器:前端可以使用定时器(如
-
安全策略:某些高安全性要求的应用可能会要求更频繁的 token 刷新,以减少 token 被盗用的风险。
示例
假设 access token 的有效期是 1 小时,前端可以在 token 有效期剩余 5 分钟时调用续约接口:
const refreshToken = async () => {
try {
const response = await fetch('/api/refresh-token', {
method: 'POST',
credentials: 'include', // 包含 cookies
});
const data = await response.json();
// 保存新的 token
localStorage.setItem('access_token', data.access_token);
} catch (error) {
console.error('Error refreshing token:', error);
}
};
// 假设 access token 有效期为 1 小时,在55分钟时调用续约接口
setTimeout(refreshToken, 55 * 60 * 1000);
后端实现
后端在处理续约接口时,通常会验证 refresh token 是否有效,并生成新的 access token 和 refresh token:
app.post('/api/refresh-token', (req, res) => {
const { refreshToken } = req.body;
if (!isValidRefreshToken(refreshToken)) {
return res.status(401).json({ message: 'Invalid refresh token' });
}
const newAccessToken = generateAccessToken();
const newRefreshToken = generateRefreshToken();
res.json({
access_token: newAccessToken,
refresh_token: newRefreshToken,
});
});
确保根据应用的需求和安全策略来调整续约接口的调用频率。
设置 refresh token 有效期为 30 天,意味着在这 30 天内,只要 refresh token 没有过期,用户就可以通过 refresh token 自动续约获取新的 access token,而无需再次手动登录。这通常用于提升用户体验,使用户在长时间内保持登录状态。但这并不意味着用户在 30 天内不需要登录,而是可以自动续约 access token。
详细解释
-
Access Token:通常有效期较短(如 1 小时)。用于快速验证用户身份,减少对服务器的负担。
-
Refresh Token:有效期较长(如 30 天)。用于在 access token 过期后,获取新的 access token。
示例场景
假设你的应用设置如下:
- Access Token 有效期:1 小时
- Refresh Token 有效期:30 天
流程:
- 用户首次登录时,获取一个有效期为 1 小时的 access token 和一个有效期为 30 天的 refresh token。
- 用户使用应用时,每小时需要用 refresh token 续约获取新的 access token。
- 如果用户在第 29 天登录并获取新的 access token 和 refresh token,那么新的 refresh token 有效期仍然是 30 天。
示例代码
生成和续约 Token 的后端代码
const jwt = require('jsonwebtoken');
// 生成 access token,有效期 1 小时
const generateAccessToken = (user) => {
return jwt.sign({ id: user.id }, 'access_secret', { expiresIn: '1h' });
};
// 生成 refresh token,有效期 30 天
const generateRefreshToken = (user) => {
return jwt.sign({ id: user.id }, 'refresh_secret', { expiresIn: '30d' });
};
// 处理 token 续约的 API
app.post('/api/refresh-token', (req, res) => {
const { refreshToken } = req.body;
if (!refreshToken) {
return res.status(400).json({ message: 'Refresh token is required' });
}
jwt.verify(refreshToken, 'refresh_secret', (err, user) => {
if (err) {
return res.status(403).json({ message: 'Invalid refresh token' });
}
const newAccessToken = generateAccessToken(user);
const newRefreshToken = generateRefreshToken(user); // 可选择每次续约时生成新的 refresh token
res.json({
accessToken: newAccessToken,
refreshToken: newRefreshToken,
});
});
});
前端自动续约的代码
const refreshToken = async () => {
const refreshToken = localStorage.getItem('refresh_token');
if (!refreshToken) {
return;
}
try {
const response = await fetch('/api/refresh-token', {
method: 'POST',
credentials: 'include',
body: JSON.stringify({ refreshToken }),
headers: {
'Content-Type': 'application/json',
},
});
const data = await response.json();
if (data.accessToken) {
localStorage.setItem('access_token', data.accessToken);
localStorage.setItem('refresh_token', data.refreshToken); // 可选择每次续约时更新 refresh token
}
} catch (error) {
console.error('Error refreshing token:', error);
}
};
// 定时器,每小时续约一次
setInterval(refreshToken, 55 * 60 * 1000); // 55 分钟后续约,确保 access token 不会过期
通过这种方式,即使用户长时间不手动登录,只要 refresh token 仍然有效,他们的 access token 就会自动续约,保持登录状态。注意,长时间有效的 refresh token 可能会增加安全风险,需要权衡用户体验和安全性,并考虑使用其他安全措施,如 IP 限制、设备管理、多因素认证等。