Node.js实现JWT认证的流程步骤

 更新时间:2025年06月24日 09:19:28   作者:盛夏绽放  
JSON Web Tokens (JWT) 是现代 Web 开发中广泛使用的身份验证机制,本文将用生动的方式带你全面了解 JWT 在 Node.js 中的实现,包括生成、验证和各种相关方法,需要的朋友可以参考下

一、JWT 是什么?为什么需要它?

想象一下你去参加一个音乐会,入场时需要出示门票。这张门票包含你的座位信息,并有防伪标识。JWT 就像这张数字门票:

  • 包含信息:存储用户身份数据(如用户ID)
  • 防伪标识:通过签名防止篡改
  • 有效期:像门票一样有使用期限

传统 session 与 JWT 对比

特性SessionJWT
存储位置服务器内存/数据库客户端
扩展性跨服务器共享困难天然支持分布式
跨域支持需要额外配置原生支持
移动端友好度一般非常好
安全性依赖 Cookie 安全依赖 Token 存储方式

二、JWT 的结构解析

一个 JWT 看起来像这样:
xxxxx.yyyyy.zzzzz

它由三部分组成,用点(.)分隔:

Header (头部) - xxxxx

{
  "alg": "HS256",  // 签名算法
  "typ": "JWT"     // 令牌类型
}

Payload (负载) - yyyyy

{
  "sub": "1234567890",  // 主题(用户ID)
  "name": "John Doe",    // 自定义数据
  "iat": 1516239022     // 签发时间
}

Signature (签名) - zzzzz

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret
)

可视化流程

[Header JSON] → Base64编码 → xxxxx
[Payload JSON] → Base64编码 → yyyyy
[xxxxx.yyyyy + 密钥] → 签名算法 → zzzzz
最终令牌:xxxxx.yyyyy.zzzzz

三、Node.js 中实现 JWT

1. 安装 jsonwebtoken 包

const jwt = require('jsonwebtoken');
const secret = 'your-secret-key'; // 应该存储在环境变量中

// 用户登录成功后生成token
function generateToken(user) {
  return jwt.sign(
    {
      userId: user.id,
      username: user.username,
      role: user.role
    },
    secret,
    {
      expiresIn: '1h', // 1小时后过期
      issuer: 'your-company', // 签发者
      audience: 'your-app-name' // 接收方
    }
  );
}

参数详解表

参数类型必填说明
payloadObject/String要编码的数据
secretString签名密钥
optionsObject配置选项

常用 options

选项示例值说明
expiresIn‘1h’/‘15m’/‘7d’有效期
algorithm‘HS256’签名算法
issuer‘your-app’签发者
audience‘client-app’接收方
subject‘user-auth’主题

3. 验证 JWT

function verifyToken(token) {
  try {
    return jwt.verify(token, secret, {
      issuer: 'your-company',
      audience: 'your-app-name'
    });
  } catch (err) {
    console.error('Token验证失败:', err.message);
    return null;
  }
}

// 使用示例
const token = generateToken({id: 1, username: 'john', role: 'admin'});
const decoded = verifyToken(token);

验证流程示意图

客户端请求 → [携带Token] → 服务器
       ↓
[提取Authorization头]
       ↓
[拆分Bearer和Token]
       ↓
[jwt.verify()验证]
       ↓
[有效] → 继续处理请求
       ↓
[无效] → 返回401错误

4. 错误处理大全

JWT 验证可能抛出以下错误:

错误类型触发条件处理建议
JsonWebTokenError无效token返回401
TokenExpiredErrortoken过期返回401,提示刷新
NotBeforeError未到生效时间等待或返回403
SyntaxErrortoken格式错误返回400

错误处理增强版

function handleTokenError(err) {
  switch(err.name) {
    case 'JsonWebTokenError':
      return { status: 401, message: '无效令牌' };
    case 'TokenExpiredError':
      return { status: 401, message: '令牌已过期,请重新登录' };
    case 'NotBeforeError':
      return { status: 403, message: '令牌尚未生效' };
    default:
      return { status: 400, message: '令牌处理错误' };
  }
}

四、高级应用场景

1. 刷新令牌机制

[登录成功]
  ↓
[发放 access_token (短有效期) + refresh_token (长有效期)]
  ↓
[access_token过期] → [用refresh_token获取新access_token]
  ↓
[refresh_token过期] → [要求重新登录]

实现代码:

// 生成令牌对
function generateTokenPair(user) {
  const accessToken = jwt.sign(
    { userId: user.id }, 
    secret, 
    { expiresIn: '15m' }
  );
  
  const refreshToken = jwt.sign(
    { userId: user.id, tokenType: 'refresh' },
    secret,
    { expiresIn: '7d' }
  );
  
  return { accessToken, refreshToken };
}

// 刷新access token
function refreshAccessToken(refreshToken) {
  const decoded = jwt.verify(refreshToken, secret);
  if (decoded.tokenType !== 'refresh') {
    throw new Error('非法的refresh token');
  }
  return jwt.sign({ userId: decoded.userId }, secret, { expiresIn: '15m' });
}

2. 在不同路由中的验证中间件

// 基础验证中间件
function authenticateJWT(req, res, next) {
  const authHeader = req.headers.authorization;
  
  if (authHeader) {
    const token = authHeader.split(' ')[1];
    
    jwt.verify(token, secret, (err, user) => {
      if (err) {
        const error = handleTokenError(err);
        return res.status(error.status).json(error);
      }
      
      req.user = user;
      next();
    });
  } else {
    res.sendStatus(401);
  }
}

// 角色检查中间件
function requireRole(role) {
  return (req, res, next) => {
    if (req.user?.role !== role) {
      return res.status(403).json({ message: '权限不足' });
    }
    next();
  };
}

// 使用示例
router.get('/admin', authenticateJWT, requireRole('admin'), (req, res) => {
  res.json({ message: '欢迎管理员' });
});

五、安全最佳实践

密钥管理

  • 永远不要将密钥硬编码在代码中
  • 使用环境变量或密钥管理服务
  • 定期轮换密钥

Token 存储

  • 前端:使用 HttpOnly + Secure 的 Cookie 比 localStorage 更安全
  • 避免在 URL 中传递 token

额外安全措施

// 示例:增加IP绑定
function generateToken(user, ip) {
  return jwt.sign({
    userId: user.id,
    ip: ip // 绑定用户当前IP
  }, secret, { expiresIn: '1h' });
}

function verifyToken(token, ip) {
  const decoded = jwt.verify(token, secret);
  if (decoded.ip !== ip) {
    throw new Error('IP地址不匹配');
  }
  return decoded;
}

六、常见问题解答

Q: JWT 和 Session Cookie 哪个更好?

A: 没有绝对的好坏,取决于场景:

  • 需要分布式/无状态 → JWT
  • 需要即时撤销 → Session
  • 移动端应用 → JWT
  • 传统Web应用 → 两者皆可

Q: JWT 过期后如何处理?

A: 两种方案:

  • 让用户重新登录
  • 使用refresh token机制自动获取新token

Q: 如何实现强制下线?

A: JWT 本身难以实现,可以考虑:

  • 使用短有效期 + refresh token
  • 维护一个黑名单(部分牺牲无状态特性)
  • 在token中存储版本号,修改版本号使旧token失效

七、完整示例代码

const express = require('express');
const jwt = require('jsonwebtoken');
const app = express();
require('dotenv').config();

const SECRET = process.env.JWT_SECRET || 'fallback-secret';
const PORT = process.env.PORT || 3000;

// 模拟用户数据库
const users = [
  { id: 1, username: 'admin', password: 'admin123', role: 'admin' },
  { id: 2, username: 'user', password: 'user123', role: 'user' }
];

app.use(express.json());

// 登录路由
app.post('/login', (req, res) => {
  const { username, password } = req.body;
  const user = users.find(u => u.username === username && u.password === password);
  
  if (!user) {
    return res.status(401).json({ message: '用户名或密码错误' });
  }
  
  const token = jwt.sign(
    { userId: user.id, role: user.role },
    SECRET,
    { expiresIn: '15m' }
  );
  
  res.json({ token });
});

// 受保护路由
app.get('/profile', authenticateJWT, (req, res) => {
  const user = users.find(u => u.id === req.user.userId);
  res.json({ 
    id: user.id,
    username: user.username,
    role: user.role
  });
});

// 管理员路由
app.get('/admin-stats', authenticateJWT, (req, res, next) => {
  if (req.user.role !== 'admin') {
    return res.status(403).json({ message: '需要管理员权限' });
  }
  res.json({ stats: '敏感管理数据' });
});

// JWT验证中间件
function authenticateJWT(req, res, next) {
  const authHeader = req.headers.authorization;
  
  if (authHeader && authHeader.startsWith('Bearer ')) {
    const token = authHeader.split(' ')[1];
    
    jwt.verify(token, SECRET, (err, user) => {
      if (err) {
        const error = handleTokenError(err);
        return res.status(error.status).json(error);
      }
      
      req.user = user;
      next();
    });
  } else {
    res.status(401).json({ message: '需要认证令牌' });
  }
}

// 错误处理函数
function handleTokenError(err) {
  switch(err.name) {
    case 'JsonWebTokenError':
      return { status: 401, message: '无效令牌' };
    case 'TokenExpiredError':
      return { status: 401, message: '令牌已过期,请重新登录' };
    default:
      return { status: 400, message: '令牌处理错误' };
  }
}

app.listen(PORT, () => {
  console.log(`服务器运行在 http://localhost:${PORT}`);
});

结语

JWT 就像数字世界的护照,它轻巧、自包含且安全。通过本文的学习,你应该已经掌握了:

  • JWT 的结构和工作原理
  • 如何在 Node.js 中生成和验证 JWT
  • 各种相关方法和配置选项
  • 高级应用场景和安全实践

记住,没有绝对安全的系统,JWT 只是工具,合理的使用方式和适当的安全措施才是关键。现在就去你的 Node.js 项目中实践这些知识吧!

以上就是Node.js实现JWT认证的流程步骤的详细内容,更多关于Node.js JWT认证的资料请关注脚本之家其它相关文章!

相关文章

  • Node.js 中的流Stream模块简介及如何使用流进行数据处理

    Node.js 中的流Stream模块简介及如何使用流进行数据处理

    Node.js中的流(Stream)模块用于高效处理流式数据,包括可读流、可写流、双边流和转换流等,通过`fs.createReadStream`和`.pipe`方法可以方便地读取文件并写入控制台或处理网络请求,在实际开发中,需要注意错误处理、资源管理和性能优化等问题
    2025-03-03
  • 在Node.js中使用HTTP上传文件的方法

    在Node.js中使用HTTP上传文件的方法

    这篇文章主要介绍了在Node.js中使用HTTP上传文件的方法,作者以windows下的visual studio作为操作node的环境,推荐阅读!需要的朋友可以参考下
    2015-06-06
  • nodejs npm install全局安装和本地安装的区别

    nodejs npm install全局安装和本地安装的区别

    这篇文章主要介绍了nodejs npm install 全局安装和非全局安装的区别,即带参数-g和不带参数-g安装的区别,需要的朋友可以参考下
    2014-06-06
  • Node.js(安装,启动,测试)

    Node.js(安装,启动,测试)

    这里主要介绍基于windows平台上最简单方便的安装方式,启动以及简单测试
    2014-06-06
  • 如何用nodejs搭建代理服务器

    如何用nodejs搭建代理服务器

    这篇文章主要介绍了如何用nodejs搭建代理服务器,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-08-08
  • node.js文件的压缩解压问题

    node.js文件的压缩解压问题

    这篇文章主要介绍了node.js文件的压缩解压问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-03-03
  • 利用yarn代替npm管理前端项目模块依赖的方法详解

    利用yarn代替npm管理前端项目模块依赖的方法详解

    这篇文章主要给大家介绍了关于利用yarn代替npm管理前端项目模块依赖的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。
    2017-09-09
  • 简单模拟node.js中require的加载机制

    简单模拟node.js中require的加载机制

    大家都知道Node 采用的模块化结构是按照 CommonJS 规范,模块与文件是一一对应关系,即加载一个模块,实际上就是加载对应的一个模块文件。这篇文章显示简单的介绍了nodejs中require的加载机制,而后简单的模拟require函数,有需要的朋友们下面来一起看看吧。
    2016-10-10
  • node版本切换与版本升级降级教程(win)

    node版本切换与版本升级降级教程(win)

    nvm是一个node的版本管理工具,可以简单操作node版本的切换、安装、查看,下面这篇文章主要给大家介绍了关于node版本切换与版本升级降级教程的相关资料,需要的朋友可以参考下
    2022-11-11
  • 深入理解Node.js的HTTP模块

    深入理解Node.js的HTTP模块

    最近在工作中接触到了Node.js标准库提供了http模块,所以这篇文章想总结下Node.js的HTTP模块,方便大家和自己以后使用的时候参考借鉴。有需要的朋友们下面来一起看看吧。
    2016-10-10

最新评论