写点什么

JWTの求生记录

作者:空城机
  • 2025-06-11
    浙江
  • 本文字数:3118 字

    阅读完需:约 10 分钟

JWTの求生记录

Token 三巨头通常指的是三种主流的令牌(Token)技术,它们各自解决了不同场景下的身份验证和授权问题


Token 验证是现代 Web 和移动应用中常用的身份验证方式,它比传统的 session-cookie 机制更适用于分布式系统和 RESTful API。

基本 Token 验证流程

  1. 用户登录:客户端发送用户名和密码到服务器

  2. 服务器验证:验证凭证有效性

  3. 生成 Token:验证通过后生成加密的 token

  4. 返回 Token:将 token 返回给客户端

  5. 后续请求:客户端在请求头中携带 token

  6. 服务器验证 Token:验证 token 有效性并返回数据

常见的 Token 类型

1. JWT (JSON Web Token)

  • 自包含的 token,包含用户信息、过期时间等

  • 由三部分组成:头部(Header),载荷(Payload),签名(Signature)

  • 不需要服务器存储 session

  • 适合前后端分离架构

2. OAuth Token

  • 用于第三方授权

  • 包括 access token 和 refresh token

3. Session Token

  • 传统 session ID 的变种

  • 服务器需要维护 token 状态



从删库到跑路之 JWT 速成

当前我正在编写一个聊天室项目,已经完成了用户注册,需要对用户登录进行 token 验证编写,目前采用的方案是 JWT 方式。

JWT 优缺点

优势


  • 适合 RESTful API 和微服务架构

  • 跨语言支持(标准 RFC 7519)

  • 减少服务端存储压力


短板


  • 无法主动废止(需借助黑名单或短有效期)

  • Payload 大小影响网络传输


JWT 方式是服务器索性不保存 session 数据了,所有数据都保存在客户端,每次请求都发回服务器。


总结:JWT 就像渣男三件套:会自爆(过期)、能续杯(Refresh)、可拉黑(黑名单)

项目框架

  • 前端:Vue3 + Vite

  • 后端:Node.js + Express + Socket.io

  • 数据库:MongoDB

JWT

首先要了解 JWT 的基本概念:


  1. JWT 结构



载荷 payload 官方字段


* iss (issuer):签发人- exp (expiration time):过期时间- sub (subject):主题- aud (audience):受众- nbf (Not Before):生效时间- iat (Issued At):签发时间- jti (JWT ID):编号
复制代码


  1. 密钥:生成和验证 JWT 时,你需要一个密钥。密钥可以是对称密钥(使用相同的密钥进行签名和验证)或非对称密钥(使用不同的密钥进行签名和验证)。

  2. 签名验证: 接收 JWT 的服务器使用密钥验证签名以确保 JWT 未被篡改。如果签名验证成功,服务器可以信任 JWT 中的信息。


JWT 的最大缺点是,由于服务器不保存 session 状态,因此无法在使用过程中废止某个 token,或者更改 token 的权限。也就是说,一旦 JWT 签发了,在到期之前就会始终有效,除非服务器部署额外的逻辑。并且 JWT 本身包含了认证信息,一旦泄露,任何人都可以获得该令牌的所有权限。为了减少盗用,JWT 的有效期应该设置得比较短。对于一些比较重要的权限,使用时应该再次对用户进行认证。

实现步骤

实现思路图

1. 安装依赖

yarn add jsonwebtoken
复制代码


JsonWebToken 中文网:开发文档 | JsonWebToken 中文网

2. 基础生成

services文件夹中创建一个tokenService.js,用于编写与 JWT 相关的代码。



可以使用以下代码生成一个基础的token,设置expiresIn过期时间 15 分钟,以及notBefore10 秒后生效:


const jwt = require('jsonwebtoken');
/** * 获取 JWT 签名密钥 */const secretKey = 'chat_box_wang_wu';
/** * 生成 JWT token * @param {Object} user - 用户信息对象 * @returns {string} - 生成的 token */const generateToken = (user) => { return jwt.sign(user, secretKey, { expiresIn: '15m', notBefore: '10s' });};
复制代码


jwt.sign 方法传入的参数:jwt.sign(payload, secretOrPrivateKey, [options, callback])


  • payload: 可以表示有效 JSON 对象字面量、缓冲区或字符串。

  • secretOrPrivateKey: 是一个字符串(utf-8 编码)、缓冲区、对象或 KeyObject。包含 HMAC 算法的密钥或 RSA 和 ECDSA 的 PEM 编码私钥。

  • options:



3. 用户登录路由

创建用户登录处理方法,然后将此方法挂载到路由当中


loginRouter.get('/login', loginCheck);
复制代码


/** * 用户登录处理方法 * @param {Object} req - 请求对象,包含用户登录信息 * @param {Object} res - 响应对象,用于发送响应数据 * @returns {Promise<void>} - 一个Promise,用于处理异步操作 * @throws {Error} - 如果登录过程中发生错误,将抛出一个错误 * */const loginCheck = async (req, res) => {    try {        const { username, password } = req.query;        const result = await loginService.loginUserExist(username, password);        if (result.exists) {            const token = generateToken({ username: username, userid: result.userid });            res.json({ status: 'success', code: 200, message: '登录成功', token });        } else {            res.json({ status: 'failure', code: 400, message: '登录失败,用户名或密码错误' });        }    } catch (error) {        res.status(500).json({ error: '服务器错误', code: 500 });        console.error(`登录失败: ${error.message}`);    }}
复制代码


这样就能够使用用户名和密码访问该路由接口,获取到一个 JWT 生成的 token 结果



解码之后的结果:


4. 验证 JWT token

在登陆后请求返回 token,需要在服务器上验证其真实性,下面是使用jwt.verify进行验证的方法:


/** * 验证 JWT token * @param {string} token - 待验证的 token * @returns {Object|boolean} - 验证成功返回用户信息,失败返回 false */const verifyToken = (token) => {  try {    return jwt.verify(token, secretKey);  } catch (error) {    return false;  }};
复制代码


为了更容易在项目当中使用,可以创建一个配套的验证中间件来保护特定路由:


/** * 验证 token 的中间件 * @param {Object} req - 请求对象 * @param {Object} res - 响应对象 * @param {Function} next - 下一步函数 */const authMiddleware = (req, res, next) => {  // 检查响应是否已发送  if (res.headersSent) {    return;  }  const token = req.headers['authorization'];    if (!token) {    return res.status(401).json({ error: '未提供 token', code: 401 });  }  try {    const user = verifyToken(token.replace('Bearer ', ''));    if (!user) {      return res.status(403).json({ error: '无效的 token', code: 403 });    }    req.user = user;    next();  } catch (error) {    return res.status(403).json({ error: '无效的 token', code: 403 });  }};
复制代码

5. 聊天室创建使用 JWT

访问创建聊天室接口,如果当前 auth 未携带 token,返回提示信息,在上一步中已经创建了验证 token 中间件,将中间件放在创建方法头部




提供正确的 token 后,创建任务就可以成功



这里利用 JWT 解析后的数据,可以判断 token 中的用户和发送请求的用户是否一致:


  const user = verifyToken(token.replace('Bearer ', ''));    if (!user) {      return res.status(403).json({ error: '无效的 token', code: 403 });    }    // 验证请求中的creatorId是否与token中的userid一致    if (req.body.creatorId && user.userid && req.body.creatorId !== user.userid) {      return res.status(403).json({ error: '无权操作他人数据', code: 403 });    }        req.user = user;    next();
复制代码




安全最佳实践

  1. 使用 HTTPS 传输 token

  2. 设置合理的 token 过期时间

  3. 对于敏感操作要求重新认证

  4. 使用 refresh token 机制更新 access token

  5. 防范 CSRF 和 XSS 攻击

  6. 不要在客户端存储敏感信息

  7. 实现 token 黑名单机制(用于注销)




本文并未将 JWT 实战全部写完,对于 token 过期处理、多端登录、注销问题、黑名单等常见问题还需后续实践中不断学习,这些问题的解决方案往往需要在安全性、性能和用户体验之间寻找最佳平衡点



发布于: 刚刚阅读数: 6
用户头像

空城机

关注

曾经沧海难为水,只是当时已惘然 2021-03-22 加入

业余作者,在线水文 主要干前端的活,业余会学学python 欢迎各位关注,互相学习,互相进步

评论

发布
暂无评论
JWTの求生记录_前端_空城机_InfoQ写作社区