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(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。