登录获取 Token 和刷新 Token 是两个配合使用的接口下面是完整的使用流程和代码实现。一、两个接口的作用接口类型使用时机返回内容有效期登录接口用户首次登录accessToken refreshTokenaccessToken 短期如30分钟refreshToken 长期如7天刷新Token接口accessToken 过期时新的 accessToken 新的 refreshToken可选重新计时二、完整流程图用户登录 ↓ 调用登录接口 → 获取 accessToken refreshToken ↓ 存储到本地 ↓ 正常请求业务接口携带 accessToken ↓ ┌─────────────────┐ │ 接口返回401过期│ └─────────────────┘ ↓ 是 调用刷新Token接口携带 refreshToken ↓ 获取新的 accessToken ↓ 更新本地存储 ↓ 重试刚才失败的请求 ↓ 继续正常请求...三、完整代码实现1. 定义 API 接口// api/auth.jsconstBASE_URLhttps://your-api.com;exportdefault{// 登录接口login(data){returnuni.request({url:${BASE_URL}/login,method:POST,data:data});},// 刷新Token接口refreshToken(refreshToken){returnuni.request({url:${BASE_URL}/refresh,method:POST,data:{refreshToken:refreshToken}});}};2. 登录逻辑// pages/login/login.vueexportdefault{data(){return{username:,password:}},methods:{asynchandleLogin(){try{// 1. 调用登录接口constresawaitthis.$api.login({username:this.username,password:this.password});if(res.code0){const{accessToken,refreshToken,expiresIn,tokenType}res.data;// 2. 存储Tokenthis.saveToken({accessToken,refreshToken,expiresIn,// 有效期时长毫秒tokenType// Bearer 或 null});// 3. 跳转到首页uni.switchTab({url:/pages/index/index});}else{uni.showToast({title:res.msg||登录失败,icon:none});}}catch(error){console.error(登录失败,error);}},// 存储TokensaveToken(tokenData){constnowDate.now();// 存储基础信息uni.setStorageSync(accessToken,tokenData.accessToken);uni.setStorageSync(refreshToken,tokenData.refreshToken);uni.setStorageSync(tokenType,tokenData.tokenType||);// 计算过期时间绝对时间戳if(tokenData.expiresIn){uni.setStorageSync(expiresTime,nowtokenData.expiresIn);}elseif(tokenData.expiresTime){uni.setStorageSync(expiresTime,tokenData.expiresTime);}}}};3. 刷新Token逻辑// utils/refreshToken.js// 防止多个请求同时刷新Token锁机制letisRefreshingfalse;letrefreshSubscribers[];// 待刷新期间缓存的请求functionsubscribeTokenRefresh(callback){refreshSubscribers.push(callback);}functiononTokenRefreshed(newToken){refreshSubscribers.forEach(callbackcallback(newToken));refreshSubscribers[];}// 刷新TokenasyncfunctionrefreshAccessToken(){try{constrefreshTokenuni.getStorageSync(refreshToken);if(!refreshToken){// 没有refreshToken跳转登录redirectToLogin();returnnull;}// 调用刷新接口constresawaituni.request({url:https://your-api.com/refresh,method:POST,data:{refreshToken:refreshToken}});if(res.statusCode200res.data.code0){const{accessToken,refreshToken:newRefreshToken,expiresIn}res.data.data;// 更新存储uni.setStorageSync(accessToken,accessToken);uni.setStorageSync(expiresTime,Date.now()expiresIn);// 如果返回了新的refreshToken通常刷新接口也会返回新的if(newRefreshToken){uni.setStorageSync(refreshToken,newRefreshToken);}// 通知所有等待的请求onTokenRefreshed(accessToken);returnaccessToken;}else{// 刷新失败清空Token并跳转登录clearTokenAndRedirect();returnnull;}}catch(error){console.error(刷新Token失败,error);clearTokenAndRedirect();returnnull;}finally{isRefreshingfalse;}}// 跳转登录页functionredirectToLogin(){clearToken();uni.reLaunch({url:/pages/login/login});}// 清空TokenfunctionclearToken(){uni.removeStorageSync(accessToken);uni.removeStorageSync(refreshToken);uni.removeStorageSync(expiresTime);uni.removeStorageSync(tokenType);}// 导出刷新方法export{refreshAccessToken,clearToken,redirectToLogin};4. 请求拦截器自动处理Token过期// utils/request.jsimport{refreshAccessToken,redirectToLogin}from./refreshToken.js;// 请求队列存放过期期间等待的请求letrequestQueue[];letisRefreshingfalse;// 发起请求的核心方法functionrequest(config){returnnewPromise((resolve,reject){// 1. 检查Token是否过期constexpiresTimeuni.getStorageSync(expiresTime);constnowDate.now();// 判断是否需要刷新Tokenif(expiresTimenowexpiresTime){// Token已过期需要刷新if(!isRefreshing){isRefreshingtrue;// 刷新TokenrefreshAccessToken().then(newToken{if(newToken){// 刷新成功执行队列中的请求requestQueue.forEach(cbcb(newToken));requestQueue[];// 重新发起当前请求doRequest(config).then(resolve).catch(reject);}else{reject({message:Token刷新失败});}isRefreshingfalse;}).catch((){isRefreshingfalse;reject({message:Token刷新失败});});}// 将当前请求加入队列等刷新完成后重试requestQueue.push((newToken){// 更新config中的tokenconfig.headergetAuthHeader(newToken);doRequest(config).then(resolve).catch(reject);});}else{// Token未过期直接发起请求config.headergetAuthHeader();doRequest(config).then(resolve).catch(reject);}});}// 实际发起请求functiondoRequest(config){returnnewPromise((resolve,reject){uni.request({url:config.url,method:config.method||GET,data:config.data,header:config.header,success:(res){// 处理业务错误如Token无效if(res.datares.data.code401){// Token无效强制刷新refreshAccessToken().then((){// 重试请求request(config).then(resolve).catch(reject);}).catch((){redirectToLogin();reject({message:登录已过期});});}else{resolve(res);}},fail:(err){reject(err);}});});}// 获取认证请求头functiongetAuthHeader(customTokennull){consttokencustomToken||uni.getStorageSync(accessToken);consttokenTypeuni.getStorageSync(tokenType);if(tokenTypeBearer){return{Authorization:Bearer${token}};}else{return{accessToken:token};}}exportdefaultrequest;5. 实际使用示例// 在 main.js 中全局挂载importrequestfrom/utils/request.js;Vue.prototype.$requestrequest;// 在页面中使用// pages/index/index.vueexportdefault{asyncmounted(){try{// 自动处理Token过期和刷新constresawaitthis.$request({url:https://your-api.com/user/info,method:GET});console.log(用户信息:,res.data);}catch(error){console.error(请求失败:,error);if(error.message登录已过期){// 跳转到登录页uni.navigateTo({url:/pages/login/login});}}}};四、关键点总结1. 刷新策略主动刷新每次请求前检查剩余时间 5分钟就提前刷新被动刷新请求返回401时触发刷新2. 并发控制重要// 避免短时间内多次刷新TokenletisRefreshingfalse;// 锁标志letrequestQueue[];// 等待队列3. 刷新时机// 在请求拦截器中判断constexpiresTimeuni.getStorageSync(expiresTime);consttimeRemainingexpiresTime-Date.now();if(timeRemaining5*60*1000){// 剩余不足5分钟awaitrefreshAccessToken();// 提前刷新}4. Token失效后处理// 刷新接口也返回401时清空所有Token并跳转登录if(res.statusCode401){uni.removeStorageSync(accessToken);uni.removeStorageSync(refreshToken);uni.reLaunch({url:/pages/login/login});}五、注意事项refreshToken 也有有效期通常比 accessToken 长如7天、30天过期后需要重新登录刷新接口也要做防抖避免短时间内多次调用存储到本地时建议加密特别是 refreshToken登出时要同时清空accessToken 和 refreshToken刷新Token接口建议使用 POST 请求并将 refreshToken 放在 Body 中按照以上方案实现就能完美处理 Token 的获取和自动刷新了。