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

MERN项目PC重启后JWT令牌丢失求助:需实现持久化登录至主动退出

解决MERN项目中JWT令牌PC重启后丢失的问题

你的核心问题是当前使用的是会话Cookie,浏览器关闭(包括PC重启)后会自动清除,同时JWT本身有过期时间,无法永久有效。要实现“主动退出前保持登录”,需要结合持久化Cookie和刷新令牌机制,具体步骤如下:

1. 将Cookie设置为持久化而非会话Cookie

登录/注册成功后,给Cookie添加maxAge或expires属性,让Cookie在浏览器关闭后依然保留。注意不要把访问令牌的有效期设得太长(安全风险),一般15-30分钟即可,搭配刷新令牌使用。

后端(Express)示例代码:

// 生成访问令牌(短有效期)
const accessToken = jwt.sign(
  { userId: user._id, email: user.email },
  process.env.ACCESS_TOKEN_SECRET,
  { expiresIn: '15m' }
);

// 生成刷新令牌(长有效期)
const refreshToken = jwt.sign(
  { userId: user._id },
  process.env.REFRESH_TOKEN_SECRET,
  { expiresIn: '7d' }
);

// 设置访问令牌Cookie(15分钟过期)
res.cookie('accessToken', accessToken, {
  httpOnly: true,          // 防止前端JS读取,提升安全性
  secure: process.env.NODE_ENV === 'production', // 生产环境强制HTTPS
  sameSite: 'strict',      // 防止CSRF攻击
  maxAge: 15 * 60 * 1000   // 15分钟,毫秒单位
});

// 设置刷新令牌Cookie(7天过期)
res.cookie('refreshToken', refreshToken, {
  httpOnly: true,
  secure: process.env.NODE_ENV === 'production',
  sameSite: 'strict',
  maxAge: 7 * 24 * 60 * 60 * 1000 // 7天
});

2. 实现刷新令牌接口

当访问令牌过期时,用刷新令牌请求新的访问令牌,避免用户重新登录。后端需要验证刷新令牌的有效性,并且最好将刷新令牌存储在数据库中(用户退出时可删除,防止滥用)。

后端刷新接口示例:

app.post('/api/refresh-token', async (req, res) => {
  const { refreshToken } = req.cookies;
  if (!refreshToken) return res.sendStatus(401);

  // 验证刷新令牌(同时建议从数据库查询该令牌是否存在)
  jwt.verify(refreshToken, process.env.REFRESH_TOKEN_SECRET, (err, decoded) => {
    if (err) return res.sendStatus(403); // 令牌无效或已过期

    // 生成新的访问令牌
    const newAccessToken = jwt.sign(
      { userId: decoded.userId, email: decoded.email },
      process.env.ACCESS_TOKEN_SECRET,
      { expiresIn: '15m' }
    );

    // 更新访问令牌Cookie
    res.cookie('accessToken', newAccessToken, {
      httpOnly: true,
      secure: process.env.NODE_ENV === 'production',
      sameSite: 'strict',
      maxAge: 15 * 60 * 1000
    });

    res.json({ success: true });
  });
});

3. 前端自动刷新令牌逻辑

在应用初始化时(比如App组件的useEffect),检查当前访问令牌是否有效,若无效则调用刷新接口获取新令牌;另外可以在请求拦截器中处理401错误,自动触发刷新。

前端示例代码:

import { useEffect, useState } from 'react';

function App() {
  const [isAuthenticated, setIsAuthenticated] = useState(false);

  useEffect(() => {
    const initAuth = async () => {
      try {
        const response = await fetch('/api/refresh-token', {
          credentials: 'include' // 携带Cookie发送请求
        });
        if (response.ok) {
          setIsAuthenticated(true);
        } else {
          // 刷新失败,跳转到登录页
          window.location.href = '/login';
        }
      } catch (err) {
        console.error('Auth init failed:', err);
        window.location.href = '/login';
      }
    };

    initAuth();
  }, []);

  // 请求拦截器示例(用Axios的话)
  // axios.interceptors.response.use(
  //   (response) => response,
  //   async (error) => {
  //     if (error.response.status === 401) {
  //       const refreshRes = await axios.post('/api/refresh-token', {}, { withCredentials: true });
  //       if (refreshRes.data.success) {
  //         // 重新发送原请求
  //         error.config.headers['Authorization'] = `Bearer ${refreshRes.data.accessToken}`;
  //         return axios(error.config);
  //       } else {
  //         window.location.href = '/login';
  //       }
  //     }
  //     return Promise.reject(error);
  //   }
  // );

  return (
    // 你的应用内容
  );
}

export default App;

4. 退出登录逻辑

用户主动退出时,需要清除Cookie中的令牌,并从数据库删除对应的刷新令牌:

// 后端退出接口
app.post('/api/logout', (req, res) => {
  // 从数据库删除当前用户的刷新令牌(如果之前存储了)
  // await User.findByIdAndUpdate(req.user.id, { $pull: { refreshTokens: req.cookies.refreshToken } });

  // 清除Cookie
  res.clearCookie('accessToken');
  res.clearCookie('refreshToken');
  res.sendStatus(200);
});

安全注意事项

  • 生产环境必须使用HTTPS,否则httpOnly Cookie可能被窃取
  • 刷新令牌要存储在数据库中,用户修改密码或退出时及时删除
  • 避免将敏感信息存入JWT payload,因为JWT是可解码的

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 16:35:21