Vue axios获取token临时令牌封装案例
前言
为什么非要写这个博客呢?因为这件事让我有一种蛋蛋的优疼。剩下的都别问,反正问我也不会说。因为流程图我都不想(懒得)画。
开发架构
前端页面:Vue
网络请求:Axios;方式:vueaddaxios
缓存方案
全局变量:Vuex
本地缓存:LocalStorage
技术依赖
你猜?
背景
公司开发一个嵌入App的Web页面,安全方面使用老套路:App通过URL传参给前端(包含签名),前端把参数透传给H5后端验签,完事儿之后前端再决定用户是否合法。另外定义了N个JS方法前端根据固定GET参数判断是安卓还是苹果来调用。
初步设想
关于token设计方案的初步设想是这样的:第一次进入的时候获取token,后端检查签名是否通过。不通过则弹框请从合法途径进入页面并且不消失。
否则就可以让用户继续后续操作,直到后端返回token过期特定状态码回来前端在用户无感的情况下调用JS方法重新获取URL参数请求token,完事儿之后继续用户的请求操作。(为避免用户使用旧token在其他地方操作数据,每次获取token都重新从App中获取并验证,而不是在接口中刷新并返回新的token)
蛋疼事项
一期的时候定义URL参数时没有版本控制,导致二期新增JS方法迭代版本时前端新增页面调用了未知方法页面毫无反应;埋点数据也不知道是几期的…
为尽量避免请求过程中出现token过期导致的1次请求变3次请求现象每次调用请求之前需要先检查token时效的异步方法(如果token过期则调用getToken获取新的token并存储在本地)导致block嵌套。
后面又封装了N个方法就不说了…
升级设想
版本什么的这个先不说,就这个token问题我总不能每次新增一个请求就复制粘贴复制粘贴的吧?能烦死人!那我只能在axios请求之前判断token时效性啦。
直奔主题
函数声明
getToken:从本地取已存储token
checkToken:检查token时效,失效调用refreshToken函数成功则存储本地,否则返回错误原因
refreshToken:调用JS方法从App获取签名参数重新请求token
注意事项
在checkToken过程中token过期时,先移除本地已过期token缓存数据。
/*eslint-disableno-console*/ /*eslint-disableno-unused-vars*/ "usestrict"; importVuefrom'vue'; importaxiosfrom"axios"; import{getToken}from'../utils/storage.js' import{checkToken,refreshToken,clearCache}from"../utils/utils.js"; //Fullconfig:https://github.com/axios/axios#request-config //axios.defaults.baseURL=process.env.baseURL||process.env.apiUrl||''; //axios.defaults.headers.common['Authorization']=AUTH_TOKEN; //axios.defaults.headers.post['Content-Type']='application/x-www-form-urlencoded'; axios.defaults.headers.post["Content-Type"]="application/json"; letcancel, promiseArr={}; letconfig={ baseURL:process.env.VUE_APP_BASE_URL, timeout:8*1000,//Timeout withCredentials:true,//Checkcross-siteAccess-Control }; const_axios=axios.create(config); _axios.interceptors.request.use( function(config){ //Dosomethingbeforerequestissent lettoken=getToken(); //alert("token1:"+token); //发起请求时,取消掉当前正在进行的相同请求 if(promiseArr[config.url]){ promiseArr[config.url]("请稍后"); promiseArr[config.url]=cancel; }else{ promiseArr[config.url]=cancel; } if(token){ returncheckToken(null) .then((result)=>{ //console.log("refreshTokenresult:",result); if(result===true){ token=getToken() //alert("token2:"+token); config.headers.common["authorization"]=token; returnconfig; }else{ returnPromise.reject(Error(result)) } }).catch((err)=>{ //终止这个请求 returnPromise.reject(err); }); } returnconfig; }, function(error){ //Dosomethingwithrequesterror returnPromise.reject(error); } ); //Addaresponseinterceptor _axios.interceptors.response.use( function(response){ //Dosomethingwithresponsedata let{status,statusText,data}=response; if(err_check(status,statusText,data)&&data){ //varrandomColor=`rgba(${parseInt(Math.random()*255)},${parseInt( //Math.random()*255 //)},${parseInt(Math.random()*255)})`; //console.log( //"%c┍------------------------------------------------------------------┑", //`color:${randomColor};` //); //console.log("|请求地址:",response.config.url); //console.log("|请求参数:",response.config.data); //console.log("|返回数据:",response.data); //console.log( //"%c┕------------------------------------------------------------------┙", //`color:${randomColor};` //); if(data.resCode==="0001"){ clearCache() varconfig=response.config; varurl=config.url; url=url.replace("/apis","").replace(process.env.VUE_APP_BASE_URL,"") config.url=url; //alert(JSON.stringify(config)) returnrefreshToken(null) .then((result)=>{ //console.log("refreshTokenresult:",result); if(result==true){ lettoken=getToken() if(token){ config.headers["authorization"]=token; } returnaxios(config) .then((result)=>{ let{status,statusText,data}=result; //console.log('接口二次请求result:',result); if(err_check(status,statusText,data)&&data){ returnPromise.resolve(data) }else{ returnPromise.reject(Error(data.resDesc)); } }).catch((err)=>{ //console.log('接口二次请求err:'+err); returnPromise.reject(err); }); }else{ //alert("result:"+result) returnPromise.reject(Error(data.resDesc)) } }).catch((err)=>{ //终止这个请求 //alert("终止这个请求:"+err.message) //console.log("refreshTokenerr:",err); returnPromise.reject(err); }); }else{ returnPromise.resolve(data); } }else{ returnPromise.reject(Error(statusText)); } //returnresponse; }, function(error){ //Dosomethingwithresponseerror //console.log("error",error); returnPromise.reject(error); } ); //eslint-disable-next-lineno-unused-vars consterr_check=(code,message,data)=>{ if(code==200){ returntrue; } returnfalse; }; Plugin.install=function(Vue,options){ Vue.axios=_axios; window.axios=_axios; Object.defineProperties(Vue.prototype,{ axios:{ get(){ return_axios; } }, $axios:{ get(){ return_axios; } }, }); }; Vue.use(Plugin) exportdefaultPlugin;
补充知识:vue+axios+token封装axios封装接口url,带token请求,token失效刷新
一、封装axios
importaxiosfrom'axios' importqsfrom"qs" constTIME_OUT_MS=60*1000//默认请求超时时间 //axios.defaults.baseURL='http://localhost:8080'; //httprequest拦截器 axios.interceptors.request.use( config=>{ if($cookies.get("access_token")){//判断是否存在token,如果存在的话,则每个httpheader都加上token config.headers.Authorization='Bearer'+$cookies.get("access_token"); } returnconfig; }, err=>{ returnPromise.reject(err); }); //httpresponse拦截器 axios.interceptors.response.use( response=>{ returnresponse; }, error=>{ console.log("responseerror:"+error); if(error.response){ switch(error.response.status){ case401: console.log("token过期"); varconfig=error.config; refresh(config); return; } } returnPromise.reject(error)//返回接口返回的错误信息 }); /* *刷新token */ functionrefresh(config){ varrefreshToken=$cookies.get("refresh_token"); vargrant_type="refresh_token"; axios({ method:'post', url:'/oauth/token', data:handleParams({"grant_type":grant_type,"refresh_token":refreshToken}), timeout:TIME_OUT_MS, headers:{} }).then( (result)=>{ if(result.data.access_token){//重新保存token $cookies.set("access_token",result.data.access_token); $cookies.set("refresh_token",result.data.refresh_token); //需要重新执行 axios(config); }else{ //this.$events.emit('goto','login'); window.location.reload(); } } ).catch((error)=>{ //this.$events.emit('goto','login'); window.location.reload(); }); } /* *@paramresponse返回数据列表 */ functionhandleResults(response){ varresult={ success:false, message:'', status:[], errorCode:'', data:{} } if(response.status=='200'){ result.status=response.status; result.data=response.data; result.success=true; } returnresult } //functionhandleUrl(url){ ////url=BASE_URL+url //url=root+url; ////BASE_URL是接口的ip前缀,比如http:10.100.1.1:8989/ //returnurl //} /* *@paramdata参数列表 *@return */ functionhandleParams(data){ returnqs.stringify(data); } exportdefault{ /* *@paramurl *@paramdata *@paramresponse请求成功时的回调函数 *@paramexception异常的回调函数 */ post(url,data,response,exception){ axios({ method:'post', //url:handleUrl(url), url:url, data:handleParams(data), timeout:TIME_OUT_MS, headers:{ //'Content-Type':'application/json;charset=UTF-8' } }).then( (result)=>{ response(handleResults(result)) } ).catch( (error)=>{ if(exception){ exception(error) }else{ console.log(error) } } ) }, /* *get请求 *@paramurl *@paramresponse请求成功时的回调函数 *@paramexception异常的回调函数 */ get(url,data,response,exception){ axios({ method:'get', url:url, params:data, timeout:TIME_OUT_MS, headers:{ 'Content-Type':'application/json;charset=UTF-8' } }).then( (result)=>{ response(handleResults(result)) } ).catch( (error)=>{ console.log("error"+response); if(exception){ exception(error) }else{ console.log(error) } } ) } }
二、配置axios跨域,以及请求baseUrl
1.config-->index.js
' 'usestrict' //Templateversion:1.3.1 //seehttp://vuejs-templates.github.io/webpackfordocumentation. constpath=require('path') //引入跨域配置 varproxyConfig=require('./proxyConfig') module.exports={ dev:{ //Paths assetsSubDirectory:'static', assetsPublicPath:'/', //proxyTable:{},//默认跨域配置为空 proxyTable:proxyConfig.proxy, //VariousDevServersettings host:'localhost',//canbeoverwrittenbyprocess.env.HOST port:8886,//canbeoverwrittenbyprocess.env.PORT,ifportisinuse,afreeonewillbedetermined autoOpenBrowser:false, errorOverlay:true, notifyOnErrors:true, poll:false,//https://webpack.js.org/configuration/dev-server/#devserver-watchoptions- /** *SourceMaps */ //https://webpack.js.org/configuration/devtool/#development devtool:'cheap-module-eval-source-map', //Ifyouhaveproblemsdebuggingvue-filesindevtools, //setthistofalse-it*may*help //https://vue-loader.vuejs.org/en/options.html#cachebusting cacheBusting:true, cssSourceMap:true }, build:{ //Templateforindex.html index:path.resolve(__dirname,'../dist/index.html'), //Paths assetsRoot:path.resolve(__dirname,'../dist'), assetsSubDirectory:'static', //项目名字改变时这里需要变化原先为assetsPublicPath:'.' assetsPublicPath:'./', /** *SourceMaps */ productionSourceMap:true, //https://webpack.js.org/configuration/devtool/#production devtool:'#source-map', //Gzipoffbydefaultasmanypopularstatichostssuchas //SurgeorNetlifyalreadygzipallstaticassetsforyou. //Beforesettingto`true`,makesureto: //npminstall--save-devcompression-webpack-plugin productionGzip:false, productionGzipExtensions:['js','css'], //Runthebuildcommandwithanextraargumentto //Viewthebundleanalyzerreportafterbuildfinishes: //`npmrunbuild--report` //Setto`true`or`false`toalwaysturnitonoroff bundleAnalyzerReport:process.env.npm_config_report } }
2.config目录下创建一个文件proxyConfig.js文件
module.exports={ proxy:{ '/':{//将localhost:8081映射为/apis target:'http://localhost:8080',//接口地址 changeOrigin:true,//如果接口跨域,需要进行这个参数配置 secure:false,//如果接口是HTTPS接口,需要设置成true pathRewrite:{ '^/':'' } } } }
三、封装API请求Urlport.js
exportdefault{ oauth:{ login:'/oauth/token',//登录 logout:'/oauth/logout'////退出 }, user:{ addUser:'/user/add', updateUser:'/user/update', getUser:'/user/',//+Id exists:'/exists/',//+id enable:'/enable/',//+id disable:'/disable/',//+id delete:'/delete/',//+id password:'/password', query:'/query' } }
四、main.js引入
importhttpfrom'./plugins/http.js' importportsfrom'./plugins/ports' Vue.prototype.http=http Vue.prototype.ports=ports
五、使用
login.vue中使用
login(){ this.http.post(this.ports.oauth.login,{username:this.userId, password:this.password,grant_type:'password'},res=>{ if(res.success){ //返回正确的处理 页面跳转 this.$events.emit('goto','edit'); }else{ //返回错误的处理 //alert("等待处理"); } },err=>{ //console.log("正在处理"+err.response.status); if(err.response.status=='400'){ //显示用户名或密码错误 this.$refs.username.focus(); this.$refs.hint.click(); } }) }
以上这篇Vueaxios获取token临时令牌封装案例就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持毛票票。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。