You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过REST API使用JWT保持用户登录状态

使用JWT实现REST API用户持久登录的无卡顿方案

要解决JWT过期导致的客户端卡顿问题,核心思路是提前刷新令牌,而非等令牌过期后再处理。以下是具体实现方案:

核心逻辑

  • 区分两类令牌:Access Token(短有效期,如15分钟)用于日常API请求,Refresh Token(长有效期,如7天)用于刷新Access Token。
  • 客户端每次发起API请求前,检查Access Token是否即将过期(比如提前1-2分钟),若即将过期则静默调用刷新接口获取新的Access Token,再用新令牌发起原请求。
  • 将刷新逻辑封装为请求拦截器,自动处理令牌更新,业务代码无需感知此过程。

客户端实现(以Axios为例)

通过拦截器封装刷新逻辑,同时处理并发请求的锁机制,避免重复刷新:

// 全局标记是否正在刷新令牌
window.isRefreshing = false;
// 存储等待刷新完成后执行的请求队列
window.requestQueue = [];

// Axios请求拦截器
axios.interceptors.request.use(async (config) => {
  const accessToken = localStorage.getItem('accessToken');
  const refreshToken = localStorage.getItem('refreshToken');

  if (!accessToken) {
    // 无有效令牌,跳转登录页
    window.location.href = '/login';
    return Promise.reject(new Error('无访问令牌'));
  }

  // 解码JWT获取过期时间(exp为秒级时间戳)
  const decodedPayload = JSON.parse(atob(accessToken.split('.')[1]));
  const now = Date.now() / 1000;
  const timeUntilExpire = decodedPayload.exp - now;

  // 提前1分钟刷新令牌
  if (timeUntilExpire < 60 && refreshToken) {
    if (!window.isRefreshing) {
      window.isRefreshing = true;
      try {
        // 调用刷新令牌接口
        const refreshRes = await axios.post('/api/refresh-token', { refreshToken });
        const newAccessToken = refreshRes.data.accessToken;
        const newRefreshToken = refreshRes.data.refreshToken;

        // 更新本地存储的令牌
        localStorage.setItem('accessToken', newAccessToken);
        localStorage.setItem('refreshToken', newRefreshToken);

        // 更新当前请求的Authorization头
        config.headers.Authorization = `Bearer ${newAccessToken}`;

        // 执行队列中等待的请求
        window.requestQueue.forEach(callback => callback(newAccessToken));
        window.requestQueue = [];
      } catch (err) {
        // 刷新失败,清空令牌并跳转登录
        localStorage.removeItem('accessToken');
        localStorage.removeItem('refreshToken');
        window.location.href = '/login';
        return Promise.reject(err);
      } finally {
        window.isRefreshing = false;
      }
    } else {
      // 正在刷新,将当前请求加入队列等待
      return new Promise((resolve) => {
        window.requestQueue.push((newToken) => {
          config.headers.Authorization = `Bearer ${newToken}`;
          resolve(config);
        });
      });
    }
  } else {
    // 令牌有效,直接设置Authorization头
    config.headers.Authorization = `Bearer ${accessToken}`;
  }

  return config;
});

服务端实现(以Node.js/Express为例)

提供专门的刷新令牌接口,验证Refresh Token有效性并生成新的Access Token(可选旋转Refresh Token):

const jwt = require('jsonwebtoken');
const User = require('./models/User');

// 刷新令牌接口
app.post('/api/refresh-token', async (req, res) => {
  const { refreshToken } = req.body;

  if (!refreshToken) {
    return res.status(401).json({ message: '缺少Refresh Token' });
  }

  try {
    // 验证Refresh Token签名和有效期
    const decoded = jwt.verify(refreshToken, process.env.REFRESH_TOKEN_SECRET);
    
    // 查询数据库验证Refresh Token是否属于当前用户(防止令牌被复用)
    const user = await User.findById(decoded.userId);
    if (!user || user.refreshToken !== refreshToken) {
      return res.status(403).json({ message: '无效的Refresh Token' });
    }

    // 生成新的Access Token(15分钟有效期)
    const newAccessToken = jwt.sign(
      { userId: user._id, username: user.username },
      process.env.ACCESS_TOKEN_SECRET,
      { expiresIn: '15m' }
    );

    // 可选:旋转Refresh Token,生成新的并更新数据库(提升安全性)
    const newRefreshToken = jwt.sign(
      { userId: user._id },
      process.env.REFRESH_TOKEN_SECRET,
      { expiresIn: '7d' }
    );
    user.refreshToken = newRefreshToken;
    await user.save();

    // 返回新令牌
    res.json({ accessToken: newAccessToken, refreshToken: newRefreshToken });
  } catch (err) {
    // Refresh Token过期或无效
    return res.status(403).json({ message: 'Refresh Token已过期或无效' });
  }
});

关键注意事项

  • Refresh Token存储安全:同域应用建议将Refresh Token存放在HttpOnly、Secure的Cookie中,避免XSS攻击;跨域场景存储在localStorage时,需做好XSS防护。
  • 强制启用HTTPS:所有令牌相关请求必须通过HTTPS传输,防止令牌被窃听。
  • Refresh Token失效机制:用户主动登出时,需将对应的Refresh Token从数据库删除或加入黑名单;检测到令牌泄露时,也要及时失效处理。
  • 避免敏感信息:不要将用户密码、银行卡号等敏感数据放入JWT的Payload中,因为JWT是Base64编码可解码的。
  • 平衡过期时间:Access Token设短有效期(15-30分钟),Refresh Token设长有效期(7-30天),在安全性和用户体验间找平衡。

内容的提问来源于stack exchange,提问作者Adjetey Octavius

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 14:31:01