前言

        如果你刚开始学后端,一定会遇到一个词:Token。登录要用它,接口要验它,到处都是它,但没人告诉你它到底是什么。

        这篇文章用最直白的方式,从"为什么需要 Token"讲起,带你走完整个流程。看完你就能在自己的项目里用起来。

一、Token 是什么?

        想象一个场景:你去酒店入住,前台验证你的身份证后,给你一张房卡。之后你在酒店里刷房卡就行,不用每次都掏身份证。Token 就是这张"房卡"。

  • 用户登录时,提交用户名+密码(出示身份证)
  • 服务器验证通过后,生成一个 Token 字符串返回给客户端(发房卡)
  • 之后客户端每次请求都带上这个 Token(刷房卡)
  • 服务器验证 Token 合法就放行(房卡有效就开门)

这样服务器就不需要记住"谁登录了",Token 本身就携带了用户信息。

二、最常用的方案:JWT (JSON Web Token)

1.JWT 是一个编码后的字符串,长这样:

eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOjEsImV4cCI6MTcxMH0.abc123signature

2.结构:由三段组成,用 . 分隔:

Header.Payload.Signature
头部.载荷.签名

  • Header:声明加密算法(如 HS256)
  • Payload:存放数据(如 userId、过期时间)
  • Signature:用服务器的密钥对前两部分签名,防篡改

关键点:Payload 只是 Base64 编码(不是加密),任何人都能解码看到内容。安全性靠的是 Signature — 没有密钥就伪造不了签名。

3.手动解码演示:

# 把 JWT 的第二段(Payload)拿出来,Base64 解码
echo 'eyJ1c2VySWQiOjEsInVzZXJuYW1lIjoiYWxpY2UiLCJpYXQiOjE3MTAwMDAwMDAsImV4cCI6MTcxMDAwNzIwMH0=' | base64 -d
# 输出:{"userId":1,"username":"alice","iat":1710000000,"exp":1710007200}

看到了吧,Payload 就是明文 JSON,谁都能看。所以不要把密码、手机号这类敏感信息放进去。

三、实操演示(Node.js + Express)

// 1. 初始化项目后安装依赖:
//    npm init -y
//    npm install express jsonwebtoken

const express = require('express');
const jwt = require('jsonwebtoken');

const app = express();
app.use(express.json());

// 密钥,实际项目中放环境变量里,绝对不能硬编码
const SECRET_KEY = 'my-secret-key-change-in-production';

// 模拟数据库中的用户
const users = [
  { id: 1, username: 'alice', password: '123456' }
];

// ============ 登录接口:验证身份,发 Token ============
app.post('/login', (req, res) => {
  const { username, password } = req.body;

  // 查找用户(实际项目密码要用 bcrypt 哈希比对)
  const user = users.find(
    u => u.username === username && u.password === password
  );

  if (!user) {
    return res.status(401).json({ message: '用户名或密码错误' });
  }

  // 生成 Token,把 userId 放进去,设置 2 小时过期
  const token = jwt.sign(
    { userId: user.id, username: user.username },
    SECRET_KEY,
    { expiresIn: '2h' }
  );

  res.json({ token });
});

// ============ 中间件:检查 Token ============
function authMiddleware(req, res, next) {
  // 从请求头拿 Token,格式约定:Authorization: Bearer <token>
  const authHeader = req.headers.authorization;

  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    return res.status(401).json({ message: '未提供 Token' });
  }

  const token = authHeader.split(' ')[1]; // 取 Bearer 后面的部分

  try {
    // 验证 Token:检查签名是否合法、是否过期
    const decoded = jwt.verify(token, SECRET_KEY);
    req.user = decoded; // 把解码出的用户信息挂到 req 上
    next(); // 验证通过,继续执行后续逻辑
  } catch (err) {
    if (err.name === 'TokenExpiredError') {
      return res.status(401).json({ message: 'Token 已过期,请重新登录' });
    }
    return res.status(401).json({ message: 'Token 无效' });
  }
}

// ============ 受保护的接口:需要登录才能访问 ============
app.get('/profile', authMiddleware, (req, res) => {
  // req.user 就是从 Token 里解码出来的信息
  res.json({
    message: '访问成功',
    user: req.user  // { userId: 1, username: 'alice', iat: ..., exp: ... }
  });
});

// 公开接口,不需要 Token
app.get('/public', (req, res) => {
  res.json({ message: '这是公开信息,不需要登录' });
});

app.listen(3000, () => console.log('服务运行在 http://localhost:3000'));

测试--用 curl 模拟客户端:

# 1. 登录,拿到 Token
curl -X POST http://localhost:3000/login \
  -H "Content-Type: application/json" \
  -d '{"username":"alice","password":"123456"}'
# 返回:{"token":"eyJhbGciOi..."}

# 2. 带 Token 访问受保护接口(把下面的 <token> 替换成上一步拿到的)
curl http://localhost:3000/profile \
  -H "Authorization: Bearer <token>"
# 返回:{"message":"访问成功","user":{...}}

# 3. 不带 Token 访问
curl http://localhost:3000/profile
# 返回:{"message":"未提供 Token"}

四、流程总结

几个实际开发中的注意点:

  • 密钥放环境变量(process.env.JWT_SECRET),不要写在代码里
  • 密码存数据库前用 bcrypt 哈希,永远不存明文
  • Token 过期时间别设太长,配合 Refresh Token 机制做续期
  • HTTPS 是必须的,否则 Token 在传输中会被截获
  • Token 一旦签发,服务端无法主动让它失效(除非额外维护黑名单)

五、Token vs Session

        简单说:小项目、单服务器用 Session 没问题;分布式、前后端分离、移动端场景,Token 更合适。现在大多数项目选 Token。

对比项 Session Token (JWT)
状态存储 服务器保存会话数据 服务器不存,信息在 Token 里
扩展性 多台服务器需要共享 Session(如 Redis) 天然支持分布式,任何服务器都能验证
跨域 依赖 Cookie,跨域麻烦 放在 Header 里,跨域友好
注销 服务端删除 Session 即可 无法主动失效,需要额外机制

结尾

        Token 的核心就一句话:用密码换令牌,用令牌证身份。理解了这个思路,不管你用的是 Node.js、Java 还是 Go,换的只是库和语法,逻辑完全一样。建议你把上面的代码跑一遍,用 curl 走一遍登录→带 Token 请求→不带 Token 请求的流程,跑通了就真的懂了。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐