侧边栏壁纸
博主头像
ERSHI的个人网站

记录一下

  • 累计撰写 4 篇文章
  • 累计创建 5 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

Nodejs+Express中JWT的实现

ERSHI
2025-05-30 / 0 评论 / 0 点赞 / 4 阅读 / 0 字

要实现刷新 Token(Refresh Token)机制,通常的做法是为用户提供两个 Token:Access TokenRefresh Token。以下是实现步骤和关键点:


1. Access Token 和 Refresh Token 的区别

  • Access Token: 短生命周期的 Token,用于访问受保护资源。
  • Refresh Token: 长生命周期的 Token,用于获取新的 Access Token,而无需重新登录。

2. 修改 .env 文件

.env 中添加 Refresh Token 的密钥和过期时间:

PORT=3000
JWT_SECRET=your_jwt_secret_key
JWT_EXPIRATION=1h
REFRESH_TOKEN_SECRET=your_refresh_token_secret_key
REFRESH_TOKEN_EXPIRATION=7d

3. 登录时生成 Access Token 和 Refresh Token

auth.js/login 路由中,修改代码以同时生成 Access Token 和 Refresh Token:

// 登录路由
router.post('/login', async (req, res) => {
  try {
    const { username, password } = req.body;

    // 校验输入
    if (!username || !password) {
      return res.status(400).json({ message: 'Username and password are required' });
    }

    // 查找用户
    const user = users.find(user => user.username === username);
    if (!user) {
      return res.status(400).json({ message: 'Invalid credentials' });
    }

    // 验证密码
    const isMatch = await bcrypt.compare(password, user.password);
    if (!isMatch) {
      return res.status(400).json({ message: 'Invalid credentials' });
    }

    // 生成 Access Token
    const accessToken = jwt.sign({ userId: user.id }, process.env.JWT_SECRET, { expiresIn: process.env.JWT_EXPIRATION });

    // 生成 Refresh Token
    const refreshToken = jwt.sign({ userId: user.id }, process.env.REFRESH_TOKEN_SECRET, { expiresIn: process.env.REFRESH_TOKEN_EXPIRATION });

    // 将 Refresh Token 存储到用户对象中(或数据库中)
    user.refreshToken = refreshToken;
    saveUsers(); // 如果使用内存存储,则需要手动保存

    res.status(200).json({ message: 'Login successful', accessToken, refreshToken });
  } catch (error) {
    res.status(500).json({ message: 'Internal server error' });
  }
});

4. 实现 Refresh Token 路由

创建一个 /refresh-token 路由,允许用户通过有效的 Refresh Token 获取新的 Access Token。

// Refresh Token 路由
router.post('/refresh-token', (req, res) => {
  try {
    const { refreshToken } = req.body;

    if (!refreshToken) {
      return res.status(400).json({ message: 'Refresh token is required' });
    }

    // 验证 Refresh Token
    jwt.verify(refreshToken, process.env.REFRESH_TOKEN_SECRET, (err, decoded) => {
      if (err) {
        return res.status(403).json({ message: 'Invalid refresh token' });
      }

      // 查找用户并验证 Refresh Token 是否匹配
      const user = users.find(user => user.id === decoded.userId && user.refreshToken === refreshToken);
      if (!user) {
        return res.status(403).json({ message: 'Refresh token does not match' });
      }

      // 生成新的 Access Token
      const newAccessToken = jwt.sign({ userId: user.id }, process.env.JWT_SECRET, { expiresIn: process.env.JWT_EXPIRATION });

      res.status(200).json({ accessToken: newAccessToken });
    });
  } catch (error) {
    res.status(500).json({ message: 'Internal server error' });
  }
});

5. 存储 Refresh Token

为了安全起见,建议将 Refresh Token 存储在服务器端(如数据库)。如果继续使用内存存储,可以修改 users 数组来保存每个用户的 Refresh Token。

例如,在注册或登录时更新用户对象:

const newUser = { id: Date.now(), username, password: hashedPassword, refreshToken: '' };
users.push(newUser);

6. 注销功能(可选)

为了提高安全性,可以提供注销功能,使 Refresh Token 失效。

// 注销路由
router.post('/logout', authenticateToken, (req, res) => {
  try {
    const { userId } = req.user;

    // 找到用户并清除 Refresh Token
    const user = users.find(user => user.id === userId);
    if (user) {
      user.refreshToken = '';
      saveUsers(); // 如果使用内存存储,则需要手动保存
    }

    res.status(200).json({ message: 'Logout successful' });
  } catch (error) {
    res.status(500).json({ message: 'Internal server error' });
  }
});

7. 安全性注意事项

  • Refresh Token 的存储: 始终将 Refresh Token 存储在服务器端(如数据库),而不是客户端 Cookie 或 LocalStorage。
  • Token 黑名单: 可以维护一个黑名单,记录已失效的 Refresh Token。
  • 定期轮换 Refresh Token: 每次使用 Refresh Token 时,生成一个新的 Refresh Token 并替换旧的。
  • HTTPS: 确保应用运行在 HTTPS 上,防止 Token 在传输过程中被窃取。

8. 示例测试

登录:

发送 POST 请求到 /api/auth/login,携带用户名和密码,返回 Access Token 和 Refresh Token。

刷新 Token:

发送 POST 请求到 /api/auth/refresh-token,携带 Refresh Token,返回新的 Access Token。

注销:

发送 POST 请求到 /api/auth/logout,使 Refresh Token 失效。


通过以上步骤,实现了一个带有 Refresh Token 的 JWT 认证系统!

注:本章涉及到的Express 、dotenv、jsonwebtoken框架

0
  1. 支付宝打赏

    qrcode alipay
  2. 微信打赏

    qrcode weixin

评论区