首页
/ axios 请求鉴权实战指南:Bearer Token、HTTP Basic、API Key 与 Cookie 会话方案

axios 请求鉴权实战指南:Bearer Token、HTTP Basic、API Key 与 Cookie 会话方案

2026-09-04 15:36:30作者:霍妲思

绝大多数 API 都需要某种形式的鉴权机制。本文围绕 axios 官方文档中的认证(Authentication)专题,系统讲解四种主流鉴权方案——Bearer Token(JWT)、HTTP Basic、API Key 与基于 Cookie 的会话认证——各自的配置写法与推荐用法,并结合 axios 仓库源码深入剖析 auth 配置项在不同适配器(fetch、http、xhr)下的真实处理链路,帮助你在实际项目中做出正确、安全且可维护的鉴权决策。

鉴权方式总览

在展开细节之前,先给出 axios 对四类常见鉴权方案的支持方式,方便快速定位:

鉴权方案 axios 配置方式 底层机制 典型场景
Bearer Token(JWT) 请求拦截器中设置 Authorization 请求头 自定义请求头,每请求动态取值 前后端分离、OAuth2 / JWT 服务
HTTP Basic auth: { username, password } 选项 axios 自动编码并写入 Authorization: Basic ... 内网 API、简单服务账号
API Key 实例默认请求头或 params 查询参数 普通请求头 / URL 参数 第三方开放平台
Cookie / 会话 withCredentials: true 浏览器自动携带同源/跨域 Cookie 同域站点会话、SSO

核心原则(官方文档明确提示):auth 选项只用于 HTTP Basic 认证;Bearer Token 和 API Key 应通过自定义 Authorization 或其他请求头传递,不要滥用 auth

Bearer Token(JWT):请求拦截器动态注入

前后端分离项目中,最常见的方式是把 JWT 放进 Authorization 请求头。官方推荐做法是在 axios 实例上挂请求拦截器,让 token 在每次发请求时实时读取(而非启动时缓存),从而天然规避 token 过期后缓存值失效的问题:

import axios from "axios";

const api = axios.create({ baseURL: "https://api.example.com" });

api.interceptors.request.use((config) => {
  const token = localStorage.getItem("access_token");
  if (token) {
    config.headers.set("Authorization", `Bearer ${token}`);
  }
  return config;
});

几个实现要点:

  • axios.create() 创建独立实例后,拦截器只作用于该实例,便于把鉴权逻辑收敛到 API 客户端层;
  • config.headersAxiosHeaders 实例,使用 .set() 方法(而非直接赋值 config.headers.Authorization)是与 axios 1.x 兼容的推荐写法;
  • 拦截器里读 localStorage 保证 token 是发请求瞬间的最新值,这与后文"token 续期"方案配合使用。

HTTP Basic 认证:auth 选项与 URL 内嵌凭据

对于使用 HTTP Basic 认证的 API,直接传 auth 选项即可,axios 会完成 Base64 编码并自动设置 Authorization 头:

const response = await axios.get("https://api.example.com/data", {
  auth: {
    username: "myUser",
    password: "myPassword",
  },
});

类型定义上,index.d.ts 中声明了专门的结构(index.d.ts):

export interface AxiosBasicCredentials {
  username: string;
  password: string;
}

AxiosRequestConfig 与实例默认配置中都暴露了 auth?: AxiosBasicCredentials(见 index.d.ts)。

源码剖析:resolveConfig 中的 Basic 编码逻辑

auth 选项的核心处理位于配置解析阶段 lib/helpers/resolveConfig.js

// HTTP basic authentication
if (auth) {
  const username = utils.getSafeProp(auth, 'username') || '';
  const password = utils.getSafeProp(auth, 'password') || '';

  try {
    headers.set(
      'Authorization',
      'Basic ' + btoa(username + ':' + (password ? encodeUTF8(password) : ''))
    );
  } catch (e) {
    throw AxiosError.from(e, AxiosError.ERR_BAD_OPTION_VALUE, config);
  }
}

从源码可以看出三个关键实现细节:

  1. 密码支持非 Latin-1 字符:密码会先经过 encodeUTF8encodeURIComponent 转义 + 逐字节还原)转成 Latin-1 字节串再交给 btoa(),因此 open ßç£☃sesame 这类非 ASCII 密码可以正确编码;而用户名若含非 Latin-1 字符会导致 btoa 抛错,并被包装成 code: ERR_BAD_OPTION_VALUEAxiosError。这两点都有测试佐证:tests/browser/basicAuth.browser.test.js 分别验证了非 Latin-1 密码的成功编码与非法用户名的报错,tests/unit/helpers/resolveConfig.test.js 验证了 ERR_BAD_OPTION_VALUE 包装。
  2. 只读取自有属性utils.getSafeProp(auth, 'username') 配合 own() 机制只读取实例自有属性,专门防御原型链污染(如 Object.prototype.username 被恶意注入)。tests/unit/helpers/resolveConfig.test.js 中构造了 Object.prototype.username 继承场景,断言结果只包含 auth: {} 自身字段编码出的 Basic Og==(即 ": " 的 Base64),继承值被安全忽略。
  3. 编码失败不静默:Base64 编码异常会被包装为 AxiosErrorERR_BAD_OPTION_VALUE)抛出,调用方可以按标准 axios 错误流程处理。

URL 内嵌凭据的回退机制

除显式 auth 选项外,Node.js 的 http 适配器与 fetch 适配器还支持从请求 URL 中推断 Basic 凭据,例如:

https://myUser:myPassword@api.example.com/data

fetch 适配器中该逻辑位于 lib/adapters/fetch.js,与文档描述一致,且有两个值得注意的实现行为:

// HTTP basic authentication
let auth = undefined;
const configAuth = own('auth');

if (configAuth) {
  // 显式 auth 优先
  auth = { username, password };
}

if (maybeWithAuthCredentials(url)) {
  const parsedURL = new URL(url, platform.origin);

  // 仅当没有显式 auth 时,才从 URL 解析凭据
  if (!auth && (parsedURL.username || parsedURL.password)) {
    auth = {
      username: decodeURIComponentSafe(parsedURL.username),
      password: decodeURIComponentSafe(parsedURL.password),
    };
  }

  // 无论凭据来自何处,最终都会从 URL 中剥离
  if (parsedURL.username || parsedURL.password) {
    parsedURL.username = '';
    parsedURL.password = '';
    url = parsedURL.href;
  }
}

if (auth) {
  headers.delete('authorization');
  headers.set(
    'Authorization',
    'Basic ' + btoa(encodeUTF8((auth.username || '') + ':' + (auth.password || '')))
  );
}
  • 显式 auth 选项优先if (!auth && ...) 保证 URL 内嵌凭据只是回退来源,auth 选项始终覆盖 URL 中的用户名密码。这也是官方文档对新代码的明确建议:优先使用显式 auth,避免凭据散落在 URL 字符串里(日志、document.referrer 等途径更易泄漏)。
  • 百分号编码会先解码decodeURIComponentSafe()lib/adapters/fetch.js)会把 WHATWG URL 解析器返回的 percent-encoded 凭据解码,例如 my%40email.com:pass 会按 my@email.com:pass 发送;解码失败时回退原值而非抛错。
  • URL 中的凭据会被剥离:最终实际发出的 URL 不含用户名密码,凭据只通过 Authorization 头传输;同时会先 headers.delete('authorization'),防止手动设置的 Authorization 头与 Basic 凭据冲突。

http 适配器(lib/adapters/http.js)实现等价逻辑:将 username:password 组合后通过 Node 的 auth 请求选项下发,同样先删 authorization 头再走 URL 回退。此外它还有一个细节——重定向时保留认证lib/adapters/http.js 通过 beforeRedirects.auth 钩子在 3xx 跳转后恢复 auth,避免跨路径重定向导致 Basic 凭据丢失。

API Key:请求头或查询参数二选一

API Key 的传递方式由服务端约定决定,axios 侧只需选择对应的传递通道:

// 方式一:作为请求头(推荐,凭据不进入 URL)
const api = axios.create({
  baseURL: "https://api.example.com",
  headers: { "X-API-Key": "your-api-key-here" },
});

// 方式二:作为查询参数
const response = await axios.get("https://api.example.com/data", {
  params: { apiKey: "your-api-key-here" },
});

两种方式的差异在于:

  • 请求头方式在实例上配置一次即可全局生效,且凭据不会出现在 URL 中——URL 更容易被代理、CDN、浏览器历史与日志记录,因此除非 API 明确要求,应优先选择请求头;
  • 查询参数方式利用 params 自动做 URL 编码,适合老式 API 的约定,但注意 axios 不会对其做脱敏,落日志时会完整暴露。

Token 续期:响应拦截器 + 失败请求队列

当 access token 过期时,需要静默刷新并重试失败的请求。官方文档给出的完整实现是"响应拦截器 + 刷新锁 + 等待队列"模式,能避免并发请求同时触发多次刷新:

import axios from "axios";

const api = axios.create({ baseURL: "https://api.example.com" });

// 跟踪是否已有刷新请求在途,避免并发重复刷新
let isRefreshing = false;
let failedQueue = [];

const processQueue = (error, token = null) => {
  failedQueue.forEach((prom) => {
    if (error) {
      prom.reject(error);
    } else {
      prom.resolve(token);
    }
  });
  failedQueue = [];
};

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

    if (error.response?.status === 401 && !originalRequest._retry) {
      if (isRefreshing) {
        // 已有刷新在途:把当前请求挂起,等刷新完成后再重试
        return new Promise((resolve, reject) => {
          failedQueue.push({ resolve, reject });
        })
          .then((token) => {
            originalRequest.headers["Authorization"] = `Bearer ${token}`;
            return api(originalRequest);
          })
          .catch((err) => Promise.reject(err));
      }

      originalRequest._retry = true;
      isRefreshing = true;

      try {
        const { data } = await axios.post("/auth/refresh", {
          refreshToken: localStorage.getItem("refresh_token"),
        });

        const newToken = data.access_token;
        localStorage.setItem("access_token", newToken);
        api.defaults.headers.common["Authorization"] = `Bearer ${newToken}`;

        processQueue(null, newToken);
        return api(originalRequest);
      } catch (refreshError) {
        processQueue(refreshError, null);
        // 刷新失败:清理本地凭证,跳转登录或派发全局事件
        localStorage.removeItem("access_token");
        window.location.href = "/login";
        return Promise.reject(refreshError);
      } finally {
        isRefreshing = false;
      }
    }

    return Promise.reject(error);
  }
);

该模式的三个关键点值得理解:

  • _retry 标记防止死循环:同一请求 401 后只允许自动重试一次;如果刷新后的 token 仍然 401,错误会原样上抛,而不会无限循环。
  • isRefreshing 锁 + failedQueue 队列:并发场景下只有第一个 401 请求真正发起刷新,其余 401 请求挂起为 Promise 进入队列;刷新成功时 processQueue(null, newToken) 统一放行并重放,刷新失败时统一 reject。这是处理"短时间大量请求同时过期"的标准方案。
  • 刷新请求走裸 axios 而非 api 实例:避免刷新请求本身又进入该拦截器(虽然 _retry 也能兜底),同时刷新成功后通过 api.defaults.headers.common 更新后续请求的默认头,与请求拦截器配合形成完整闭环。

Cookie 会话认证:withCredentials 与 CORS 约束

对于基于服务端会话、依赖 Cookie 的 API,需要在实例上开启 withCredentials: true,让跨域请求携带 Cookie:

const api = axios.create({
  baseURL: "https://api.example.com",
  withCredentials: true, // 每次请求都携带 Cookie
});

需要注意服务端 CORS 的硬性约束:withCredentials: true 要求服务器响应 Access-Control-Allow-Credentials: true,且 Access-Control-Allow-Origin 必须是具体来源(不能使用 * 通配符),否则浏览器会直接拦截响应。

从源码看,该配置在不同适配器中有对应的落地实现:

  • XHR 适配器lib/adapters/xhr.js):配置存在时直接透传给 XMLHttpRequest——request.withCredentials = !!_config.withCredentials
  • fetch 适配器lib/adapters/fetch.js):把布尔值映射为 fetch 的 credentials 模式——true 映射为 'include'false 映射为 'omit',未设置时默认为 'same-origin'lib/adapters/fetch.js),即默认只携带同源 Cookie,行为与浏览器 fetch 规范一致;
  • http 适配器(Node):Node 环境没有浏览器 Cookie 概念,跨域请求的 Cookie 通常依赖 http 适配器内置的 Cookie 处理逻辑而非该选项,withCredentials 主要针对浏览器端跨域场景。

另外,axios 对同源请求默认会自动携带 XSRF Token:resolveConfig 中(lib/helpers/resolveConfig.js)仅在标准浏览器环境且 withXSRFToken === true 或 URL 同源时,从 xsrfCookieName 指定的 Cookie 读取值并写入 xsrfHeaderName 请求头,用于服务端防跨站请求伪造校验。做 Cookie 会话认证时,可与服务端 CSRF 校验机制配合使用。

选型小结

  • OAuth2 / JWT 前后端分离:请求拦截器注入 Bearer Token + 响应拦截器做 401 自动续期,是 axios 生态最完整的组合;
  • 内网 / 服务间简单认证:用 auth 选项,并注意"新代码优先显式 auth 而非 URL 内嵌凭据"的官方建议——显式选项优先级更高,且凭据不会残留在 URL 中;
  • 第三方开放平台:确认服务端约定后,用实例默认头或 params 传递 API Key,优先请求头;
  • 同域会话 / SSOwithCredentials: true,并确认服务端 CORS 头满足 Access-Control-Allow-Credentials: true + 具体 Origin 的要求。

以上所有配置项与行为均可在当前仓库源码中查证:核心编码逻辑见 lib/helpers/resolveConfig.js,各适配器的凭据处理见 lib/adapters/fetch.jslib/adapters/http.jslib/adapters/xhr.js,行为验证可参考 tests/browser/basicAuth.browser.test.jstests/unit/helpers/resolveConfig.test.js

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
528
590
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
889
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
982
503
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384