axios 请求鉴权实战指南:Bearer Token、HTTP Basic、API Key 与 Cookie 会话方案
【免费下载链接】axiosPromise based HTTP client for the browser and node.js项目地址: https://gitcode.com/GitHub_Trending/ax/axios
绝大多数 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.headers是AxiosHeaders实例,使用.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); } }从源码可以看出三个关键实现细节:
- 密码支持非 Latin-1 字符:密码会先经过 encodeUTF8(
encodeURIComponent转义 + 逐字节还原)转成 Latin-1 字节串再交给btoa(),因此open ßç£☃sesame这类非 ASCII 密码可以正确编码;而用户名若含非 Latin-1 字符会导致btoa抛错,并被包装成code: ERR_BAD_OPTION_VALUE的AxiosError。这两点都有测试佐证:tests/browser/basicAuth.browser.test.js 分别验证了非 Latin-1 密码的成功编码与非法用户名的报错,tests/unit/helpers/resolveConfig.test.js 验证了ERR_BAD_OPTION_VALUE包装。 - 只读取自有属性:
utils.getSafeProp(auth, 'username')配合own()机制只读取实例自有属性,专门防御原型链污染(如Object.prototype.username被恶意注入)。tests/unit/helpers/resolveConfig.test.js 中构造了Object.prototype.username继承场景,断言结果只包含auth: {}自身字段编码出的Basic Og==(即": "的 Base64),继承值被安全忽略。 - 编码失败不静默:Base64 编码异常会被包装为
AxiosError(ERR_BAD_OPTION_VALUE)抛出,调用方可以按标准 axios 错误流程处理。
URL 内嵌凭据的回退机制
除显式auth选项外,Node.js 的 http 适配器与 fetch 适配器还支持从请求 URL 中推断 Basic 凭据,例如:
https://myUser:myPassword@api.example.com/datafetch 适配器中该逻辑位于 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,优先请求头; - 同域会话 / SSO:
withCredentials: true,并确认服务端 CORS 头满足Access-Control-Allow-Credentials: true+ 具体 Origin 的要求。
以上所有配置项与行为均可在当前仓库源码中查证:核心编码逻辑见 lib/helpers/resolveConfig.js,各适配器的凭据处理见 lib/adapters/fetch.js、lib/adapters/http.js、lib/adapters/xhr.js,行为验证可参考 tests/browser/basicAuth.browser.test.js 与 tests/unit/helpers/resolveConfig.test.js。
【免费下载链接】axiosPromise based HTTP client for the browser and node.js项目地址: https://gitcode.com/GitHub_Trending/ax/axios
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考