小白也能懂的 Token 认证:从原理到实战,用 Express 手把手教你做
前后端分离的项目里,怎么确定 "你就是你"?比如你登录后想查看个人信息,服务器得知道 "现在发请求的人,确实是刚登录成功的那个用户"。以前用 Cookie-Session 的方式,跨域的时候特别麻烦,而 Token 认证就没这问题,现在几乎成了主流。今天就用大白话讲清楚 JWT(最常用的 Token 格式)是啥,再结合 Express 代码,教你搭一个能直接用的认证系统。
一、先搞懂:JWT 到底是个啥?
简单说,JWT 就是一串 "加密的身份小纸条"。服务器给你发这张纸条,你之后每次找服务器要东西,都得带上它 ------ 服务器不用记你的信息,只要看这张纸条对不对,就知道你是不是合法用户。
1. JWT 长啥样?三段式字符串而已
一个完整的 JWT 是这样的,用.分成三部分,比如:
plaintext
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjEsInVzZXJuYW1lIjoidGVzdHVzZXIiLCJpYXQiOjE3MTY2NTM0MDksImV4cCI6MTcxNjY1NzAwOX0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
        这三部分各有各的用,咱们用 "快递包裹" 打比方,一下子就懂了:
| 部分 | 大白话作用 | 里面存啥(举例子) | 能不能随便看? | 
|---|---|---|---|
| Header(头部) | 告诉大家 "我是 JWT,用 XX 方法加密的" | {"alg":"HS256","typ":"JWT"} | 
能(Base64 编码,不是加密) | 
| Payload(载荷) | 存你的身份信息(比如 "你是谁,ID 多少") | {"userId":1,"username":"testuser"} | 
能(同上,千万别存密码) | 
| Signature(签名) | 防止纸条被篡改(服务器的 "防伪标志") | 用密钥算出来的加密串(外人解不开) | 不能(核心防伪部分) | 
2. JWT 怎么工作?就三步
整个认证流程特别简单,跟你去银行办事差不多:
- "办卡"(登录) :你带身份证(用户名 / 密码)去银行(服务器),银行核实后,给你一张银行卡(JWT Token);
 - "用卡"(发请求) :之后你每次去银行办事(访问接口),都得带上这张卡(在请求头里带 Token);
 - "验卡"(验证) :银行看一眼你的卡是不是真的、有没有过期(服务器验证 Token),没问题就给你办事(返回数据),有问题就赶你走(返回 401 错误)。
 
二、动手做:用 Express 搭 JWT 认证系统
接下来咱们照着代码一步步做,不用怕,都是基础操作。需要的工具就三个:express(搭服务器)、jsonwebtoken(生成 Token)、express-jwt(验证 Token)。
1. 先准备环境:装依赖
先建个文件夹,打开命令行,输入这两句,把需要的工具装好:
bash
            
            
              csharp
              
              
            
          
          # 初始化项目(生成package.json,不用管它是啥,装依赖要用)
npm init -y
# 装核心工具:express(服务器)、jsonwebtoken(造Token)、express-jwt(验Token)
npm install express jsonwebtoken express-jwt
        2. 核心代码拆解:从配置到接口
咱们把代码分成几块,每块都讲清楚干啥用的,跟着写就行。
第一步:搭好服务器架子,配基础设置
先把服务器启动起来,再配置好 "读 JSON 数据" 的功能(不然收不到前端发的用户名密码):
javascript
            
            
              ini
              
              
            
          
          // 把装的工具引进来
const express = require('express');
const jwt = require('jsonwebtoken');
const { expressjwt: expressJWT } = require('express-jwt');
// 初始化服务器
const app = express();
// 关键:能读懂前端发的JSON格式数据(比如用户名密码)
app.use(express.json());
// 配置两个重要常量:
const SECRET_KEY = 'your-secret-key-keep-safe'; // 服务器的"密钥",验Token全靠它
const ALGORITHM = 'HS256'; // 加密方法(不用改,就用这个)
        ⚠️ 重点提醒 :SECRET_KEY 就像你家的钥匙,绝对不能写死在代码里!正式项目里要存在 "环境变量" 里(比如用 dotenv 工具),不然别人拿到代码,就能伪造你的 Token 了。
第二步:配 "全局验 Token" 中间件
这个中间件的作用是:所有接口默认都要验 Token,除了登录接口(总不能登录也要 Token 吧,不然永远登不上)。
javascript
            
            
              less
              
              
            
          
          app.use(expressJWT({
  secret: SECRET_KEY, // 用刚才的密钥验Token
  algorithms: [ALGORITHM] // 用刚才的加密方法
}).unless({ path: ['/login'] })); // 排除/login接口,不用验Token
        简单说:以后不管访问/profile还是/dashboard,服务器都会先看你带的 Token 对不对;只有访问/login,才不用验。
第三步:写登录接口:给用户发 Token
登录是唯一不用验 Token 的接口,作用就是 "核实用户名密码,对了就发 Token"。
javascript
            
            
              javascript
              
              
            
          
          // 模拟数据库(正式项目要连MySQL、MongoDB这些真数据库)
const users = [
  { id: 1, username: 'testuser', password: 'testpass' } // 测试账号
];
// 登录接口:POST请求(前端发数据过来)
app.post('/login', (req, res) => {
  // 1. 从前端拿用户名和密码(req.body就是前端发的JSON数据)
  const { username, password } = req.body;
  
  // 2. 查"数据库",看有没有这个用户,密码对不对
  const user = users.find(u => u.username === username && u.password === password);
  if (!user) { // 没找到用户,或密码错了
    return res.status(401).send('用户名或密码错啦!');
  }
  
  // 3. 密码对了,生成Token
  const token = jwt.sign(
    // 第一部分:存用户身份信息(别存密码!谁都能解开)
    { userId: user.id, username: user.username },
    // 第二部分:用服务器密钥加密
    SECRET_KEY,
    // 第三部分:配置(Token有效期1小时,过了就得重新登录)
    { 
      expiresIn: '1h', 
      algorithm: ALGORITHM
    }
  );
  
  // 4. 把Token发给前端
  res.json({
    message: '登录成功!',
    token: token // 前端要把这个Token存起来(比如存在localStorage里)
  });
});
        ⚠️ 安全提醒 :示例里密码是明文存的,正式项目绝对不能这么干!要先用bcrypt这种工具把密码 "哈希"(变成一串乱码),存数据库里;验证的时候,再用工具把前端传的密码和哈希后的密码比对。
第四步:写受保护的接口:只有带正确 Token 才能访问
这些接口需要验 Token,验过了才能返回数据。而且验过之后,服务器能从 Token 里读出用户信息(比如用户 ID)。
javascript
            
            
              javascript
              
              
            
          
          // 个人资料接口(只有登录后才能看)
app.get('/profile', (req, res) => {
  // req.auth里就是从Token里解析出来的用户信息(userId、username)
  res.json({
    message: '这是你的个人资料页,别人看不了!',
    user: req.auth // 把用户信息返回给前端
  });
});
// 仪表盘接口(同理,需要Token)
app.get('/dashboard', (req, res) => {
  res.json({
    message: '这是你的专属仪表盘',
    user: req.auth,
    data: '只有你能看到的数据(比如你的订单、收藏)'
  });
});
        第五步:处理 "Token 验证失败" 的情况
比如 Token 过期了、Token 是伪造的,服务器得告诉前端 "咋回事",不能直接报错。
javascript
            
            
              javascript
              
              
            
          
          app.use((err, req, res, next) => {
  // 如果是Token验证失败的错误
  if (err.name === 'UnauthorizedError') {
    return res.status(401).send({
      message: 'Token有问题!',
      detail: err.message // 具体啥问题(比如"Token过期了""签名不对")
    });
  }
  // 其他服务器错误(比如代码写错了)
  res.status(500).send('服务器出小问题啦,稍后再试~');
});
        常见的 Token 错误:
jwt expired:Token 过期了,让用户重新登录;invalid signature:Token 是假的,可能有人在搞事;invalid token:Token 格式错了,比如前端没加 "Bearer" 前缀。
第六步:启动服务器,准备测试
最后一步,让服务器跑起来,监听 3000 端口:
javascript
            
            
              javascript
              
              
            
          
          app.listen(3000, () => {
  console.log('服务器跑起来啦!地址:http://localhost:3000');
  console.log('测试步骤:');
  console.log('1. 先登录拿Token:发POST请求到 /login,带 {"username":"testuser","password":"testpass"}');
  console.log('2. 再访问受保护接口:发GET请求到 /profile 或 /dashboard,请求头带 Authorization: Bearer 你的Token');
});
        到这里,代码就写完了!接下来咱们测试一下,看看好不好使。
三、测试:用 Postman 实操(小白也会)
Postman 是个测试接口的工具,下载下来打开就行,不用注册。
第一步:登录拿 Token
- 
打开 Postman,选择 "POST" 方法,输入地址:
http://localhost:3000/login; - 
点击 "Body",选择 "raw",再选 "JSON" 格式;
 - 
输入下面的内容(测试账号密码):
json
json{ "username": "testuser", "password": "testpass" } - 
点击 "Send",右边会返回结果,里面有个
token字段,把这串长长的字符复制下来(这就是你的 Token)。 
第二步:用 Token 访问受保护接口
- 
新建一个请求,选择 "GET" 方法,输入地址:
http://localhost:3000/profile; - 
点击 "Headers",添加一行:
- Key:写 
Authorization(注意首字母大写) - Value:写 
Bearer 你刚才复制的Token(注意 "Bearer" 后面有个空格,别漏了) 
 - Key:写 
 - 
点击 "Send",右边会返回你的个人信息,说明认证成功了!
 
如果没带 Token,或者 Token 错了,右边会返回 "Token 有问题",这就对了 ------ 说明我们的认证起作用了。
四、JWT 认证的优缺点:别光用,得知道好坏
优点:为啥大家都用 JWT?
- 服务器不用记东西:服务器不用存你的会话信息,不管多少人登录,都不影响性能,适合多台服务器一起用(比如淘宝的服务器有几百台,总不能每台都记你的信息吧);
 - 跨域方便:Cookie 跨域特别麻烦,Token 直接放请求头里,不管前端后端在不在一个域名下,都能正常用;
 - 简单轻量:Token 字符串不长,传起来快,不占带宽。
 
缺点:别踩坑!
- Token 不能主动作废:比如你登录后退出了,但 Token 没过期,别人拿到你的 Token 还是能访问接口。解决办法是搞个 "黑名单",把退出的 Token 存进去,服务器验证前先查黑名单;
 - 不能存敏感信息:Payload 部分是 Base64 编码,不是加密,随便找个 Base64 解码工具就能解开,所以绝对不能存密码、手机号这些隐私数据;
 - 有效期难平衡:有效期设太长,不安全;设太短,用户老得重新登录,体验差。解决办法是搞 "刷新 Token"------ 短 Token(1 小时)用来访问接口,长 Token(7 天)用来换短 Token,用户不用频繁登录。
 
正式项目里要注意的点(避坑指南)
- 密钥要安全:用随机生成的长字符串(比如 32 位),存在环境变量里,别写代码里;
 - 密码要哈希 :用
bcrypt工具处理密码,别存明文; - 用 HTTPS:Token 在网上传的时候,要用 HTTPS 加密,不然别人能偷你的 Token;
 - 加权限控制 :比如管理员和普通用户看到的内容不一样,可以在 Token 里加个
role字段(比如"role":"admin"),接口里判断角色给不同数据; - 处理 Token 过期:前端要监听 401 错误,Token 过期了就跳登录页,或者用刷新 Token 自动续期。
 
五、总结
其实 JWT 认证没那么复杂,核心就是 "发 Token - 带 Token - 验 Token" 这三步。今天咱们从原理讲到代码,再到测试,小白也能跟着做出来。
正式项目里,重点关注安全问题:密钥要藏好、密码要哈希、用 HTTPS、处理 Token 过期。只要把这些细节做好,JWT 就能帮你安全地识别用户身份,让你的项目更靠谱。
希望这篇文章能帮你搞懂 Token 认证,下次再遇到类似需求,就能轻松搞定啦!