前端必会能力 - 网络请求  高可用封装  鉴权权限体系 模块三网络请求 高可用封装 鉴权权限体系本模块为前端工程化核心基建覆盖企业级Axios高可用封装、请求防抖/取消/重试、TS类型约束、双Token无感刷新、RBAC四层权限架构全体系。解决项目请求混乱、重复请求、接口报错容错差、Token过期掉线、权限控制漏洞等生产核心问题所有方案为一线企业通用落地规范可直接用于业务开发、面试高阶应答。目录模块三网络请求 高可用封装 鉴权权限体系1. Axios 企业级高可用封装1.1 多实例隔离企业级规范1.2 全方位拦截器体系1.2.1 请求拦截器统一注入Token、防缓存、全局Loading1.2.2 响应拦截器剥离包装层、业务码处理、全局错误兜底1.3 基于 AbortController 请求防抖 取消机制1.4 智能容错重试机制1.5 全量 TypeScript 泛型封装类型绝对安全2. 双 Token 无感刷新方案企业级安全核心2.1 分层存储安全策略安全核心2.2 无感刷新核心机制刷新锁 请求队列2.3 跨域 Cookie 核心配置前端必配后端必配3. RBAC 权限管控架构企业级四层权限体系3.1 RBAC 核心模型3.2 四层权限逐级管控完整落地第一层路由权限动态路由注册第二层菜单权限动态渲染菜单第三层按钮权限自定义指令精准控制第四层接口权限后端最终兜底4. 模块标准面试话术1. Axios 企业级高可用封装原生Axios裸用存在实例混乱、无统一拦截、无容错、无防抖、类型松散等问题。企业级封装核心目标多场景实例隔离、统一请求/响应拦截、请求可控、异常容错、全量TS类型安全、用户体验最优。1.1 多实例隔离企业级规范中大型项目存在多类接口场景常规业务接口、文件上传、第三方接口、后台管理接口不同场景的超时时间、基础域名、请求头、拦截逻辑完全不同。单实例混用会导致逻辑耦合、bug难以定位必须采用多实例隔离。// api/request.ts import axios, { AxiosRequestConfig, AxiosInstance } from axios; import { useAuthStore } from /stores/auth; import { useGlobalLoading } from /composables/useLoading; // 1. 常规业务接口实例默认15s超时 const service: AxiosInstance axios.create({ baseURL: import.meta.env.VITE_API_BASE, timeout: 15000, headers: { Content-Type: application/json }, }); // 2. 文件上传专属实例超时更长、表单格式 const uploadService: AxiosInstance axios.create({ baseURL: import.meta.env.VITE_UPLOAD_BASE, timeout: 60000, // 上传文件超时延长至60s headers: { Content-Type: multipart/form-data }, }); // 3. 后台管理专属实例独立域名、严格超时 const adminService: AxiosInstance axios.create({ baseURL: import.meta.env.VITE_ADMIN_BASE, timeout: 10000, }); export { service, uploadService, adminService };1.2 全方位拦截器体系拦截器是Axios封装的核心分为请求拦截器统一注入参数与响应拦截器统一处理结果、错误兜底实现全局逻辑统一收口。1.2.1 请求拦截器统一注入Token、防缓存、全局Loading// 请求拦截器所有请求发起前统一处理 service.interceptors.request.use( (config) { const authStore useAuthStore(); // 1. 统一注入内存AccessToken用于接口鉴权 if (authStore.accessToken) { config.headers.Authorization Bearer ${authStore.accessToken}; } // 2. GET请求防缓存追加时间戳避免浏览器304缓存旧数据 if (config.method?.toLowerCase() get) { config.params { ...config.params, _t: Date.now(), }; } // 3. 全局Loading计数解决并发请求loading闪烁问题 const loading useGlobalLoading(); loading.start(); return config; }, (error) { // 请求前置错误直接兜底返回 return Promise.reject(error); } );1.2.2 响应拦截器剥离包装层、业务码处理、全局错误兜底适配后端统一返回范式{ code, data, message }统一剥离外层包装、区分业务成功/失败、处理网络异常、服务端异常。import { showErrorToast } from /utils/toast; service.interceptors.response.use( (response) { const loading useGlobalLoading(); loading.end(); const { code, data, message } response.data; // 1. 业务请求成功直接剥离外层返回核心数据 if (code 200) return data; // 2. 业务参数错误、客户端错误 if (code 400) { showErrorToast(message || 请求参数异常); return Promise.reject(new Error(message)); } // 3. Token过期/未授权交给双Token刷新逻辑处理 if (code 401) { return Promise.reject({ ...response.data, __needRefresh: true }); } // 4. 其余业务异常统一提示 showErrorToast(message || 服务器业务异常); return Promise.reject(new Error(message)); }, (error) { const loading useGlobalLoading(); loading.end(); // 网络层级错误兜底 if (error.message.includes(timeout)) { showErrorToast(请求超时请检查网络后重试); } else if (!error.response) { showErrorToast(网络连接失败请检查网络); } else if (error.response.status 500) { showErrorToast(服务器异常请稍后重试); } return Promise.reject(error); } );1.3 基于 AbortController 请求防抖 取消机制解决两大高频问题用户快速点击重复提交、路由跳转残留无效请求基于浏览器原生AbortController实现精准取消无兼容性问题。import type { AxiosRequestConfig } from axios; // 存储 pending 中的请求keymethod:urlvalue控制器实例 const pendingMap new Mapstring, AbortController(); // 生成请求唯一key function generateRequestKey(config: AxiosRequestConfig) { return ${config.method}:${config.url}; } // 添加 pending 请求重复请求自动取消上一次 function addPending(config: AxiosRequestConfig) { const key generateRequestKey(config); // 重复请求取消上一次 if (pendingMap.has(key)) { pendingMap.get(key)?.abort(); } // 绑定新控制器 const controller new AbortController(); config.signal controller.signal; pendingMap.set(key, controller); } // 移除完成的 pending 请求 function removePending(config: AxiosRequestConfig) { const key generateRequestKey(config); pendingMap.delete(key); } // 拦截器注入 service.interceptors.request.use((config) { addPending(config); return config; }); service.interceptors.response.use( (res) { removePending(res.config); return res; }, (err) { // 手动取消的请求不重复报错 if (!axios.isCancel(err)) { removePending(err.config); } return Promise.reject(err); } ); // 路由跳转清空所有残留请求防止页面销毁后回调报错 import router from /router; router.beforeEach(() { pendingMap.forEach(controller controller.abort()); pendingMap.clear(); });1.4 智能容错重试机制针对网络抖动、服务端5xx临时故障、请求超时做有限次数重试提升接口稳定性杜绝无脑重试。// 重试配置最大重试次数、重试延迟阶梯递增 const MAX_RETRY 2; const RETRY_DELAY 1000; // 判断是否需要重试仅网络异常、5xx服务端错误重试 function shouldRetry(error: any): boolean { // 无响应 网络断开/超时500-599 服务端临时故障 return !error.response || (error.response.status 500 error.response.status 600); } // 响应拦截器注入重试逻辑 service.interceptors.response.use( undefined, async (error) { const config error.config; // 初始化重试计数 config.__retryCount config.__retryCount || 0; // 满足条件则重试 if (config.__retryCount MAX_RETRY shouldRetry(error)) { config.__retryCount 1; // 阶梯延迟重试避免瞬间打崩服务器 await new Promise(resolve setTimeout(resolve, RETRY_DELAY * config.__retryCount)); // 重新发起请求 return service(config); } return Promise.reject(error); } );1.5 全量 TypeScript 泛型封装类型绝对安全封装通用请求方法统一约束入参、出参类型杜绝any实现全局接口类型提示、编译时报错校验。// api/types.ts 全局接口通用类型 export interface ApiResponseT any { code: number; data: T; message: string; } // 通用请求泛型方法 import type { AxiosRequestConfig } from axios; export async function requestT any(config: AxiosRequestConfig): PromiseT { // 响应拦截器已剥离外层包装直接返回业务data return service(config) as PromiseT; } // 使用示例 // 定义业务接口类型 interface UserInfo { id: number; username: string; avatar: string; role: string; } // 类型自动推断、编译校验 export function getUserInfo() { return requestUserInfo({ url: /user/info, method: GET }); }2. 双 Token 无感刷新方案企业级安全核心单Token方案存在致命问题短期Token频繁掉线、长期Token泄露风险极高。双Token架构是目前前端企业级项目的标准鉴权方案兼顾安全性与用户体验。2.1 分层存储安全策略安全核心令牌类型有效期存储位置核心作用安全优势AccessToken短期1hPinia/Redux 内存存储日常接口鉴权页面刷新即销毁XSS无法窃取泄露窗口极小RefreshToken长期30天HttpOnlySecureLax Cookie过期续期、刷新凭证JS无法读取彻底防XSS窃取仅刷新接口自动携带2.2 无感刷新核心机制刷新锁 请求队列解决并发多个接口同时401、重复刷新Token、请求丢失、死循环四大问题。核心逻辑同一时间仅允许一次刷新其余401请求进入队列等待刷新成功批量重试。// api/auth.ts import axios from axios; import { useAuthStore } from /stores/auth; import router from /router; // 全局刷新锁防止并发刷新 let isRefreshing false; // 等待重试的请求队列 let pendingQueue: Array(newToken: string) void []; // 刷新AccessTokenRefreshToken由Cookie自动携带 async function refreshAccessToken(): Promisestring { const res await axios.post(/api/auth/refresh, null, { withCredentials: true, // 关键允许跨域携带Cookie }); return res.data.accessToken; } // 绑定至响应拦截器 service.interceptors.response.use( res res, async (error) { const originalRequest error.config; // 精准拦截401未授权、未重试过、非刷新接口本身防死循环 if ( error.response?.status 401 !originalRequest._retry !originalRequest.url?.includes(/api/auth/refresh) ) { // 场景1正在刷新当前请求入队等待 if (isRefreshing) { return new Promise(resolve { pendingQueue.push((newToken) { originalRequest.headers.Authorization Bearer ${newToken}; resolve(service(originalRequest)); }); }); } // 场景2开启新一轮刷新 isRefreshing true; originalRequest._retry true; try { // 获取新令牌 const newAccessToken await refreshAccessToken(); // 更新全局状态 useAuthStore().setToken(newAccessToken); // 批量执行队列中所有等待请求 pendingQueue.forEach(cb cb(newAccessToken)); pendingQueue []; // 重试当前失败请求 originalRequest.headers.Authorization Bearer ${newAccessToken}; return service(originalRequest); } catch (refreshErr) { // 刷新失败RefreshToken过期/失效强制登出 useAuthStore().logout(); pendingQueue []; router.replace(/login); return Promise.reject(refreshErr); } finally { // 释放刷新锁 isRefreshing false; } } return Promise.reject(error); } );2.3 跨域 Cookie 核心配置前端必配所有请求实例开启withCredentials: true否则浏览器拒绝携带跨域CookieRefreshToken失效。后端必配Access-Control-Allow-Credentials: trueAccess-Control-Allow-Origin禁止为*必须配置具体可信域名3. RBAC 权限管控架构企业级四层权限体系前端权限核心遵循前端做展示控制后端做安全兜底。采用标准 RBAC 模型用户-角色-权限标识实现精细化权限管控。3.1 RBAC 核心模型用户绑定角色角色绑定权限标识字符串唯一标识如user:read、user:add、user:delete。后端返回当前用户权限数组前端基于该数组动态控制路由、菜单、按钮、接口访问。3.2 四层权限逐级管控完整落地第一层路由权限动态路由注册无权限页面无法注册路由直接404拦截从根源杜绝越权访问页面。// router/index.ts import { createRouter, createWebHistory, RouteRecordRaw } from vue-router; import { useAuthStore } from /stores/auth; // 固定路由所有用户可访问 const constantRoutes: RouteRecordRaw[] [ { path: /login, component: () import(/views/Login.vue) }, { path: /404, component: () import(/views/404.vue) }, ]; // 异步权限路由需权限匹配 const asyncRoutes: RouteRecordRaw[] [ { path: /user, name: UserManage, component: () import(/views/UserManage.vue), meta: { permission: user:read, title: 用户管理 } }, { path: /order, name: OrderManage, component: () import(/views/OrderManage.vue), meta: { permission: order:read, title: 订单管理 } } ]; const router createRouter({ history: createWebHistory(), routes: constantRoutes }); // 全局路由守卫动态注册权限路由 router.beforeEach(async (to, from, next) { const authStore useAuthStore(); // 登录页直接放行 if (to.path /login) return next(); // 未登录跳转登录 if (!authStore.accessToken) return next(/login); // 已登录但未加载权限路由 if (!authStore.hasLoadedRoutes) { // 根据权限过滤可访问路由 const accessRoutes await authStore.generateRoutes(asyncRoutes); // 动态注册路由 accessRoutes.forEach(route router.addRoute(route)); // 防止路由空白强制重试当前路由 next({ ...to, replace: true }); } else { next(); } }); export default router;第二层菜单权限动态渲染菜单根据用户权限过滤菜单列表无权限菜单不展示适配侧边栏动态渲染。script setup langts import { computed } from vue; import { useAuthStore } from /stores/auth; import { buildMenuTree } from /utils/menu; const authStore useAuthStore(); // 动态生成有权限的菜单树 const menuList computed(() { return buildMenuTree(asyncRoutes, authStore.permissions); }); /script template el-menu modevertical template v-foritem in menuList :keyitem.path el-menu-item :indexitem.path{{ item.meta.title }}/el-menu-item /template /el-menu /template第三层按钮权限自定义指令精准控制精细化控制页面按钮、操作权限无权限直接移除DOM简洁高效。// directives/permission.ts import { useAuthStore } from /stores/auth; // 全局权限指令 v-permission权限标识 export default { mounted(el: HTMLElement, binding: { value: string }) { const authStore useAuthStore(); const needPermission binding.value; // 无权限则移除按钮 if (!authStore.permissions.includes(needPermission)) { el.parentNode?.removeChild(el); } } };!-- 页面使用 -- button v-permissionuser:add新增用户/button button v-permissionuser:delete删除用户/button第四层接口权限后端最终兜底核心红线前端所有权限控制仅为展示优化不可作为安全依据。所有新增、删除、修改等敏感接口后端必须二次校验权限非法请求直接返回403拦截彻底杜绝前端篡改权限越权操作。4. 模块标准面试话术在项目中搭建了企业级Axios高可用请求架构通过多实例隔离区分业务、上传、后台接口实现逻辑解耦。通过请求拦截统一注入Token、防缓存、全局Loading响应拦截统一处理业务码、网络异常、服务端报错。同时基于AbortController实现路由跳转清请求、重复点击防抖搭配阶梯重试机制解决网络抖动和5xx临时故障结合TS泛型实现全接口类型安全。鉴权层面采用企业级双Token无感刷新方案短期AccessToken内存存储防XSS泄露长期RefreshToken存入HttpOnly Cookie配合全局刷新锁和请求队列解决并发401重复刷新、请求丢失问题实现用户无感知续期。权限体系基于RBAC模型搭建四层权限架构实现路由动态注册、菜单动态渲染、按钮指令级控制同时严格遵循前端展示、后端兜底的安全原则保障系统权限安全可控。