从零构建安全的Token体系:Access Token与Refresh Token实战指南

1次阅读
没有评论

共计 2091 个字符,预计需要花费 6 分钟才能阅读完成。

image.webp

核心概念:JWT 与双 Token 机制

  1. JWT 结构剖析
    JWT 由 Header.Payload.Signature 三部分组成:
  2. Header 包含算法类型(如 HS256)和 token 类型
  3. Payload 存储用户 ID 等非敏感声明(注意不要放密码)
  4. Signature 防止篡改,用密钥对前两部分签名

    从零构建安全的 Token 体系:Access Token 与 Refresh Token 实战指南

  5. Access Token 设计
    通常设置 15-30 分钟过期(如银行应用更短),特点:

  6. 高频使用:每个 API 请求都携带
  7. 风险较高:一旦泄露相当于短期密码
  8. 无状态验证:服务端只需验证签名不需查库

  9. Refresh Token 设计
    通常设置 7 -30 天过期(甚至更长),特点:

  10. 低频使用:仅用于获取新 Access Token
  11. 严格保管:服务端需要存储验证
  12. 可撤销性:发现异常立即废止

为什么需要双 Token?解决三大痛点

  • 无状态扩展需求
    传统 Session 需要服务器存储状态,分布式环境下扩展困难

  • Token 劫持防护
    Access Token 短期有效,即使泄露攻击窗口也很小

  • 用户体验平衡
    避免频繁登录(Refresh Token 保持会话),又保证安全性(Access Token 短周期)

Node.js 实战:完整认证流实现

// 密钥管理(实际项目应从环境变量读取)const ACCESS_SECRET = 'access_secret_123!@#';
const REFRESH_SECRET = 'refresh_secret_456$%^';

// 签发双 Token
function generateTokens(user) {
  const accessToken = jwt.sign({ userId: user.id},
    ACCESS_SECRET,
    {expiresIn: '15m'}  // 关键:设置短过期时间
  );

  const refreshToken = jwt.sign({ userId: user.id, tokenVersion: user.tokenVersion},
    REFRESH_SECRET,
    {expiresIn: '7d'}   // 关键:设置长过期时间
  );

  return {accessToken, refreshToken};
}

// 刷新 Access Token 端点
app.post('/refresh-token', async (req, res) => {
  try {const { refreshToken} = req.body;

    // 验证 Refresh Token 有效性和版本
    const payload = jwt.verify(refreshToken, REFRESH_SECRET);
    const user = await User.findById(payload.userId);

    if (user.tokenVersion !== payload.tokenVersion) {return res.status(401).send('Token revoked'); // 关键:版本校验
    }

    // 颁发新 Access Token
    const {accessToken} = generateTokens(user);
    res.json({accessToken});
  } catch (err) {res.status(403).send('Invalid token');
  }
});

安全最佳实践

  1. 传输层防护
  2. 强制 HTTPS 防止中间人攻击
  3. 不要将 Token 放在 URL 参数中

  4. 存储方案对比
    | 方案 | 防 XSS | 防 CSRF | 适用场景 |
    |—————–|——-|——–|——————-|
    | HttpOnly Cookie | ✓ | ✗ | 最安全推荐方案 |
    | LocalStorage | ✗ | ✓ | 需要额外 CSRF 防护 |

  5. 撤销机制

  6. 在用户表中增加 tokenVersion 字段
  7. 修改密码 / 注销时递增版本号
  8. 使旧 Refresh Token 立即失效

性能与分布式注意事项

  • 验证优化
    JWT 验证虽不查库,但签名计算有开销,网关层可缓存公钥

  • 分布式一致性
    两种处理方式:

  • 方案 A:所有服务共享 Refresh Token 存储(如 Redis 集群)
  • 方案 B:每个服务独立校验,通过事件同步撤销状态

新手避坑指南

  1. 过期时间设置
  2. Access Token 不超过 1 小时
  3. Refresh Token 建议 7 -30 天
  4. 重要系统可缩短至分钟级

  5. 并发刷新处理
    可能出现场景:

  6. 多标签同时发起刷新请求
  7. 解决方案:

    // 使用 Redis 原子操作
    const key = `refresh_lock:${userId}`;
    const isLocked = await redis.set(key, '1', 'NX', 'EX', 5);
    if (!isLocked) throw new Error('操作过于频繁');

  8. 日志与监控

  9. 记录异常 Token 使用
  10. 报警频繁刷新行为

思考进阶方向

  1. 如何实现跨域 SSO(单点登录)的 Token 共享方案?
  2. 对于 IoT 设备这类无法安全存储 Refresh Token 的场景,替代方案是什么?
  3. 如何设计阶梯式过期策略(如连续活跃用户延长会话)?

通过这套双 Token 机制,我们既获得了无状态架构的优势,又通过分层安全设计有效控制了风险。建议首次实现后使用 Postman 等工具模拟各种异常流(如过期 Token、篡改 Token 等),确保防御措施生效。

正文完
 0
评论(没有评论)