如何正确处理401 invalid access token or token expired错误:从原理到实践指南

1次阅读
没有评论

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

image.webp

核心概念:理解 Access Token

  1. Access Token 的作用
    相当于临时门禁卡,服务端用它验证客户端是否有权限访问资源。常见形式是 JWT(JSON Web Token),包含三部分:
  2. Header(算法类型)
  3. Payload(用户信息 + 过期时间)
  4. Signature(防篡改校验)

    如何正确处理 401 invalid access token or token expired 错误:从原理到实践指南

  5. Token 过期机制
    通过 Payload 中的 exp 字段设置有效期(通常 30 分钟 - 2 小时),过期后需用 Refresh Token 获取新 Access Token。这是安全最佳实践——即使 token 泄露,攻击者也只能短期滥用。


新手常见痛点分析

  • 硬编码测试 token:忘记替换为动态获取的 token,上线后立即失效
  • 忽略 401 错误处理:前端直接抛出错误导致用户体验中断
  • 刷新机制缺失:强制用户重新登录,降低留存率
  • 本地存储不安全:将 token 直接存到 localStorage 易受 XSS 攻击

技术方案实现

1. 完整验证流程

flowchart LR
    A[发起请求] --> B{Token 有效?}
    B -- 是 --> C[正常响应]
    B -- 401 --> D[尝试刷新 Token]
    D -- 成功 --> E[重试原请求]
    D -- 失败 --> F[跳转登录页]

2. Axios 拦截器实现(TypeScript)

// 存储当前刷新状态
let isRefreshing = false;
let failedQueue: Array<() => void> = [];

axios.interceptors.response.use(
  response => response,
  async error => {
    const originalRequest = error.config;

    // 只处理 401 错误且非刷新 token 请求
    if (error.response?.status === 401 && !originalRequest._retry) {if (isRefreshing) {
        // 将请求加入队列等待刷新完成
        return new Promise(resolve => {failedQueue.push(() => resolve(axios(originalRequest)));
        });
      }

      originalRequest._retry = true;
      isRefreshing = true;

      try {const newToken = await refreshToken();
        setAuthToken(newToken);

        // 重试所有积压请求
        failedQueue.forEach(cb => cb());
        failedQueue = [];

        return axios(originalRequest);
      } catch (refreshError) {
        // 刷新失败则清空用户状态
        clearAuth();
        window.location.href = '/login';
        return Promise.reject(refreshError);
      } finally {isRefreshing = false;}
    }

    return Promise.reject(error);
  }
);

3. Node.js 后端验证示例

import jwt from 'jsonwebtoken';

export const verifyToken = (req, res, next) => {const token = req.headers.authorization?.split(' ')[1];

  if (!token) {return res.status(401).json({message: 'Missing token'});
  }

  try {const decoded = jwt.verify(token, process.env.JWT_SECRET);
    req.user = decoded;
    next();} catch (err) {if (err.name === 'TokenExpiredError') {return res.status(401).json({ 
        code: 'TOKEN_EXPIRED', 
        message: 'Token expired' 
      });
    }
    res.status(401).json({message: 'Invalid token'});
  }
};

避坑指南

  1. 并发请求处理
    使用 isRefreshing 标志位 + 请求队列避免多个请求同时触发刷新

  2. 安全存储方案

  3. 生产环境使用 HttpOnly Cookie(防 XSS)
  4. 移动端考虑 SecureStorage/Keychain
  5. 永远不要前端存储 Refresh Token

  6. 过期时间设置

  7. Access Token:15-30 分钟(平衡安全性与用户体验)
  8. Refresh Token:7-30 天(需配合撤销机制)

性能优化考量

存储方式 优点 缺点
内存存储 读取速度快 服务器重启后失效
Redis 高性能,支持分布式 需要额外基础设施
数据库 持久化可靠 查询延迟较高

建议:中小项目可用 Redis+ 内存二级缓存,大型系统考虑 JWT 的无状态特性。


留给读者的思考

  1. 如何实现服务端主动撤销特定用户的 token(如用户修改密码后)?
  2. 在微服务架构中,token 验证应该如何跨服务传递?
  3. 针对敏感操作(如支付),是否需要设计独立的短时效 token?

通过这套方案,我们构建了健壮的认证流程:当检测到 401 错误时自动静默刷新,用户无感知;刷新失败则优雅降级到登录页。关键是理解 token 的生命周期,并在安全和体验间找到平衡点。

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