token验证

主要面向刚接触鉴权的同学,用最 simple 的语言解释 Token(令牌)是什么、为什么要用、怎么用,帮助 you 了解并快速上手。

用一个比喻先理解「Token」

把 Token 想象成「门票」。用户第一次用账号密码换来一张门票(Token),以后进门就出示这张门票,服务端不会每次都问你账号密码,只要门票有效就让你进。门票可能有有效期、可以被收回(注销)或续签(刷新)。

基本概念(简单版)

  • Token:服务端签发给客户端的一串字符串,代表用户身份。
  • 无状态:服务器不存用户会话,所有判断靠 Token 本身或短期的服务器检查。
  • JWT:一种常见格式,内容可读(不要放敏感信息),有签名防篡改。

流程:

  1. 用户在登录页输入用户名/密码并提交。
  2. 服务端验证成功后,生成 Token(可以是 JWT),返回给前端。
  3. 前端保存 Token(下面有存储建议),并在后续请求把 Token 放到请求头发送给服务端。
  4. 服务端校验 Token,校验通过则返回数据;不通过(例如过期)则返回 401,需要重新登录或刷新 Token。

下面把每一步用最小示例进行演示。

怎么存 Token(实用建议)

  • 推荐(浏览器):把敏感的 Token 放到 HttpOnly 的 Cookie,这样 JavaScript 不能直接读取,能减少 XSS 风险。
  • 简单(但有风险):localStorage / sessionStorage,方便单页应用使用,但容易被 XSS 读取,短期 Token 可以考虑。
  • 无论哪种方式:一定要使用 HTTPS!

代码示例(最简单、可复制)

后端:Express + jsonwebtoken

先安装依赖:

npm init -y
npm install express jsonwebtoken body-parser

server.js 写入:

const express = require('express');
const jwt = require('jsonwebtoken');
const bodyParser = require('body-parser');

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

// 注意:实际项目中 SECRET 要保存在环境变量中
const ACCESS_SECRET = '替换为你自己的长随机字符串';

// 模拟登录接口
app.post('/login', (req, res) => {
    const { username, password } = req.body;
    // 这里只是演示:实际请用数据库验证
    if (username === 'alice' && password === 'password') {
        const accessToken = jwt.sign({ sub: 'alice' }, ACCESS_SECRET, { expiresIn: '15m' });
        return res.json({ accessToken });
    }
    return res.status(401).json({ message: '用户名或密码错误' });
});

// 受保护的接口示例
function verifyAccessToken(req, res, next) {
    const auth = req.headers.authorization;
    const token = auth && auth.split(' ')[1];
    if (!token) return res.status(401).json({ message: '缺少 token' });
    try {
        req.user = jwt.verify(token, ACCESS_SECRET);
        next();
    } catch (err) {
        return res.status(401).json({ message: '无效或已过期的 token' });
    }
}

app.get('/profile', verifyAccessToken, (req, res) => {
    res.json({ message: `欢迎 ${req.user.sub}` });
});

app.listen(3000, () => console.log('Server on http://localhost:3000'));

测试登录和访问(使用 curl):

# 登录拿 token
curl -s -X POST http://localhost:3000/login -H "Content-Type: application/json" -d '{"username":"alice","password":"password"}'

# 假设拿到的 token 是 ABC,访问受保护接口
curl -H "Authorization: Bearer ABC" http://localhost:3000/profile

(实际拿到的 token 很长,替换 ABC 为响应中的 accessToken

每一行示例代码都很短,读懂要点:jwt.sign(payload, secret, options) 生成 token,jwt.verify(token, secret) 验证并解码。

常见进阶:access token 与 refresh token

  • access token(短期门票):有效期短,例如 15 分钟,用来频繁访问接口。
  • refresh token(长期凭证):有效期长,例如 7 天,用来在 access token 过期后换一张新的 access token。刷新流程要非常小心:刷新接口需要保护(绑定客户端、限速、检测异常)。

流程示意:客户端保存两个东西,access(短)和 refresh(长)。当短的过期,用长的换新的短的。

注销(登出)怎么做?

  • 如果只是前端删除 token:用户在当前设备离线有效,但别人持有旧 token 仍可访问。
  • 要强制立即失效:需要服务端维护短期黑名单(例如 Redis),把被撤销的 token 加入黑名单并检查。这会让鉴权变成“部分有状态”。

常见问题

  • Q:JWT 是不是加密的?
    A:默认是签名(可以验证是否被篡改),payload 可以被解码并看到,不是加密。需要加密内容请另外处理。
  • Q:token 放 localStorage安全吗?
    A:localStorage 容易被 XSS 读取,不建议存长期权限敏感的 token。更安全的方式是 HttpOnly Cookie。
  • Q:token 被偷了怎么办?
    A:尽快缩短 access token 有效期,使用 refresh token + 旋转策略,并在发现异常时把 refresh token 列入黑名单。

so you can

  1. 运行上面的 server.js 示例并用 curl 调试,观察返回的 token。
  2. 把 token 放入 Authorization 头,访问受保护接口,理解 401 的含义。
  3. 学习如何在前端用 Axios 自动带上 token,并实现简单的刷新逻辑。

参考资料

posted @ 2023-04-14 17:04  幼儿园技术家  阅读(995)  评论(0)    收藏  举报