登录续约

482 阅读4分钟

登录续约接口的调用频率取决于几个因素,包括应用的安全需求、用户体验要求以及具体的实现方式。以下是一些常见的做法:

  1. Token 有效期:通常,登录续约接口(refresh token)的调用频率取决于访问令牌(access token)的有效期。如果 access token 有效期为 1 小时,那么在 1 小时后需要调用续约接口以获取新的 access token。

  2. 用户活动:有些应用会基于用户的活动来决定何时调用续约接口。例如,如果用户在一段时间内没有任何操作,应用可能会提示用户重新登录或自动调用续约接口。

  3. 前端实现

    • 定时器:前端可以使用定时器(如 setTimeout)在 access token 即将过期前几分钟调用续约接口。
    • 拦截器:在请求发送之前拦截请求并检查 token 是否有效,如果即将过期则先调用续约接口。
  4. 安全策略:某些高安全性要求的应用可能会要求更频繁的 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。

详细解释

  1. Access Token:通常有效期较短(如 1 小时)。用于快速验证用户身份,减少对服务器的负担。

  2. Refresh Token:有效期较长(如 30 天)。用于在 access token 过期后,获取新的 access token。

示例场景

假设你的应用设置如下:

  • Access Token 有效期:1 小时
  • Refresh Token 有效期:30 天

流程

  1. 用户首次登录时,获取一个有效期为 1 小时的 access token 和一个有效期为 30 天的 refresh token。
  2. 用户使用应用时,每小时需要用 refresh token 续约获取新的 access token。
  3. 如果用户在第 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 限制、设备管理、多因素认证等。