前言前言为什么非要写这个博客呢?因为这件事让我有一种蛋蛋的优疼。剩下的都别问,反正问我也不会说。因为流程图我都不想(懒得)画。开发架构开发架构前端页面:Vue网络请求:Axios;方式:vue add axios缓存方案全局变量: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:从本地取已存储tokencheckToken:检查token时效,失效调用refreshToken函数成功则存储本地,否则返回错误原因refreshToken:调用JS方法从App获取签名参数重新请求token注意事项在checkToken过程中token过期时,先移除本地已过期token缓存数据。
/* eslint-disable no-console */
/* eslint-disable no-unused-vars */
"use strict";

import Vue from 'vue';
import axios from "axios";
import { getToken } from '../utils/storage.js'
import { checkToken, refreshToken, clearCache } from "../utils/utils.js";

// Full config: 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";

let cancel,
promiseArr = {};
let config = {
baseURL: process.env.VUE_APP_BASE_URL,
timeout: 8 * 1000, // Timeout
withCredentials: true, // Check cross-site Access-Control
};

const _axios = axios.create(config);

_axios.interceptors.request.use(
function (config) {

// Do something before request is sent

let token = getToken();

// alert("token1:" + token);

//发起请求时,取消掉当前正在进行的相同请求

if (promiseArr[config.url]) {

promiseArr[config.url]("请稍后");

promiseArr[config.url] = cancel;

} else {

promiseArr[config.url] = cancel;

}

if (token) {

return checkToken(null)

.then((result) => {

// console.log("refreshToken result:", result);

if (result === true) {

token = getToken()

// alert("token2:" + token);

config.headers.common["authorization"] = token;

return config;

} else {

return Promise.reject(Error(result))

}

}).catch((err) => {

// 终止这个请求

return Promise.reject(err);

});

}

return config;
},
function (error) {

// Do something with request error

return Promise.reject(error);
}
);

// Add a response interceptor
_axios.interceptors.response.use(
function (response) {

// Do something with response data

let { status, statusText, data } = response;

if (err_check(status, statusText, data) && data) {

// var randomColor = `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()

var config = response.config;

var url = config.url;

url = url.replace("/apis", "").replace(process.env.VUE_APP_BASE_URL, "")

config.url = url;

// alert(JSON.stringify(config))

return refreshToken(null)

.then((result) => {

// console.log("refreshToken result:", result);

if (result == true) {

let token = getToken()

if (token) {

config.headers["authorization"] = token;

}

return axios(config)

.then((result) => {

let { status, statusText, data } = result;

// console.log('接口二次请求 result:', result);

if (err_check(status, statusText, data) && data) {

return Promise.resolve(data)

} else {

return Promise.reject(Error(data.resDesc));

}

}).catch((err) => {

// console.log('接口二次请求 err:' + err);

return Promise.reject(err);

});

} else {

// alert("result:" + result)

return Promise.reject(Error(data.resDesc))

}

}).catch((err) => {

// 终止这个请求

// alert("终止这个请求:" + err.message)

// console.log("refreshToken err:", err);

return Promise.reject(err);

});

} else {

return Promise.resolve(data);

}

} else {

return Promise.reject(Error(statusText));

}

// return response;
},
function (error) {

// Do something with response error

// console.log("error", error);

return Promise.reject(error);
}
);

// eslint-disable-next-line no-unused-vars
const err_check = (code, message, data) => {
if (code == 200) {

return true;
}
return false;
};

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)
export default Plugin;


/* eslint-disable no-console */
/* eslint-disable no-unused-vars */
"use strict";

import Vue from 'vue';
import axios from "axios";
import { getToken } from '../utils/storage.js'
import { checkToken, refreshToken, clearCache } from "../utils/utils.js";

// Full config: 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";

let cancel,
promiseArr = {};
let config = {
baseURL: process.env.VUE_APP_BASE_URL,
timeout: 8 * 1000, // Timeout
withCredentials: true, // Check cross-site Access-Control
};

const _axios = axios.create(config);

_axios.interceptors.request.use(
function (config) {

// Do something before request is sent

let token = getToken();

// alert("token1:" + token);

//发起请求时,取消掉当前正在进行的相同请求

if (promiseArr[config.url]) {

promiseArr[config.url]("请稍后");

promiseArr[config.url] = cancel;

} else {

promiseArr[config.url] = cancel;

}

if (token) {

return checkToken(null)

.then((result) => {

// console.log("refreshToken result:", result);

if (result === true) {

token = getToken()

// alert("token2:" + token);

config.headers.common["authorization"] = token;

return config;

} else {

return Promise.reject(Error(result))

}

}).catch((err) => {

// 终止这个请求

return Promise.reject(err);

});

}

return config;
},
function (error) {

// Do something with request error

return Promise.reject(error);
}
);

// Add a response interceptor
_axios.interceptors.response.use(
function (response) {

// Do something with response data

let { status, statusText, data } = response;

if (err_check(status, statusText, data) && data) {

// var randomColor = `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()

var config = response.config;

var url = config.url;

url = url.replace("/apis", "").replace(process.env.VUE_APP_BASE_URL, "")

config.url = url;

// alert(JSON.stringify(config))

return refreshToken(null)

.then((result) => {

// console.log("refreshToken result:", result);

if (result == true) {

let token = getToken()

if (token) {

config.headers["authorization"] = token;

}

return axios(config)

.then((result) => {

let { status, statusText, data } = result;

// console.log('接口二次请求 result:', result);

if (err_check(status, statusText, data) && data) {

return Promise.resolve(data)

} else {

return Promise.reject(Error(data.resDesc));

}

}).catch((err) => {

// console.log('接口二次请求 err:' + err);

return Promise.reject(err);

});

} else {

// alert("result:" + result)

return Promise.reject(Error(data.resDesc))

}

}).catch((err) => {

// 终止这个请求

// alert("终止这个请求:" + err.message)

// console.log("refreshToken err:", err);

return Promise.reject(err);

});

} else {

return Promise.resolve(data);

}

} else {

return Promise.reject(Error(statusText));

}

// return response;
},
function (error) {

// Do something with response error

// console.log("error", error);

return Promise.reject(error);
}
);

// eslint-disable-next-line no-unused-vars
const err_check = (code, message, data) => {
if (code == 200) {

return true;
}
return false;
};

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)
export default Plugin;

补充知识:vue+ axios+token 封装axios 封装接口url,带token请求,token失效刷新补充知识:补充知识:vue+ axios+token 封装axios 封装接口url,带token请求,token失效刷新一、封装axios一、封装axios
import axios from 'axios'
import qs from "qs"
const TIME_OUT_MS = 60 * 1000 // 默认请求超时时间
//axios.defaults.baseURL = 'http://localhost:8080';

// http request 拦截器
axios.interceptors.request.use(

config => {

if ($cookies.get("access_token")) { // 判断是否存在token,如果存在的话,则每个http header都加上token

config.headers.Authorization ='Bearer '+ $cookies.get("access_token");

}

return config;

},

err => {

return Promise.reject(err);
});

// http response 拦截器
axios.interceptors.response.use(

response => {

return response;

},

error => {

console.log("response error :"+error);

if (error.response) {

switch (error.response.status) {

case 401:

console.log("token 过期");

var config = error.config;

refresh(config);

return;

}

}

return Promise.reject(error)
// 返回接口返回的错误信息

});
/*
*刷新token
*/
function refresh(config){

var refreshToken = $cookies.get("refresh_token");

var grant_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();

});
}
/*
* @param response 返回数据列表
*/
function handleResults (response) {


var result = {

success: false,

message: '',

status: [],

errorCode: '',

data: {}

}

if (response.status == '200') {

result.status = response.status;

result.data = response.data;

result.success = true;

}

return result
}

// function handleUrl (url) {
//
//url = BASE_URL + url
//
url =root +url;
// // BASE_URL是接口的ip前缀,比如http:10.100.1.1:8989/
//
return url
// }

/*
* @param data 参数列表
* @return
*/
function handleParams (data) {

return qs.stringify(data);
}
export default {

/*

* @param url

* @param data

* @param response 请求成功时的回调函数

* @param exception 异常的回调函数

*/

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 请求

* @param url

* @param response 请求成功时的回调函数

* @param exception 异常的回调函数

*/

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)

}

}

)

}
}
import axios from 'axios'
import qs from "qs"
const TIME_OUT_MS = 60 * 1000 // 默认请求超时时间
//axios.defaults.baseURL = 'http://localhost:8080';

// http request 拦截器
axios.interceptors.request.use(

config => {

if ($cookies.get("access_token")) { // 判断是否存在token,如果存在的话,则每个http header都加上token

config.headers.Authorization ='Bearer '+ $cookies.get("access_token");

}

return config;

},

err => {

return Promise.reject(err);
});

// http response 拦截器
axios.interceptors.response.use(

response => {

return response;

},

error => {

console.log("response error :"+error);

if (error.response) {

switch (error.response.status) {

case 401:

console.log("token 过期");

var config = error.config;

refresh(config);

return;

}

}

return Promise.reject(error)
// 返回接口返回的错误信息

});
/*
*刷新token
*/
function refresh(config){

var refreshToken = $cookies.get("refresh_token");

var grant_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();

});
}
/*
* @param response 返回数据列表
*/
function handleResults (response) {


var result = {

success: false,

message: '',

status: [],

errorCode: '',

data: {}

}

if (response.status == '200') {

result.status = response.status;

result.data = response.data;

result.success = true;

}

return result
}

// function handleUrl (url) {
//
//url = BASE_URL + url
//
url =root +url;
// // BASE_URL是接口的ip前缀,比如http:10.100.1.1:8989/
//
return url
// }

/*
* @param data 参数列表
* @return
*/
function handleParams (data) {

return qs.stringify(data);
}
export default {

/*

* @param url

* @param data

* @param response 请求成功时的回调函数

* @param exception 异常的回调函数

*/

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 请求

* @param url

* @param response 请求成功时的回调函数

* @param exception 异常的回调函数

*/

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二、配置axios 跨域,以及请求baseUrl1.config-->index.js
'
'use strict'
// Template version: 1.3.1
// see http://vuejs-templates.github.io/webpack for documentation.

const path = require('path')

//引入跨域配置
var proxyConfig = require('./proxyConfig')
module.exports = {

dev: {


// Paths

assetsSubDirectory: 'static',

assetsPublicPath: '/',

//proxyTable: {}, //默认跨域配置为空

proxyTable: proxyConfig.proxy,


// Various Dev Server settings

host: 'localhost', // can be overwritten by process.env.HOST

port: 8886, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined

autoOpenBrowser: false,

errorOverlay: true,

notifyOnErrors: true,

poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-


/**

* Source Maps

*/


// https://webpack.js.org/configuration/devtool/#development

devtool: 'cheap-module-eval-source-map',


// If you have problems debugging vue-files in devtools,

// set this to false - it *may* help

// https://vue-loader.vuejs.org/en/options.html#cachebusting

cacheBusting: true,


cssSourceMap: true

},



build: {

// Template for index.html

index: path.resolve(__dirname, '../dist/index.html'),



// Paths

assetsRoot: path.resolve(__dirname, '../dist'),

assetsSubDirectory: 'static',

// 项目名字改变时这里需要变化 原先为assetsPublicPath: '.'

assetsPublicPath: './',


/**

* Source Maps

*/


productionSourceMap: true,

// https://webpack.js.org/configuration/devtool/#production

devtool: '#source-map',


// Gzip off by default as many popular static hosts such as

// Surge or Netlify already gzip all static assets for you.

// Before setting to `true`, make sure to:

// npm install --save-dev compression-webpack-plugin

productionGzip: false,

productionGzipExtensions: ['js', 'css'],



// Run the build command with an extra argument to

// View the bundle analyzer report after build finishes:

// `npm run build --report`

// Set to `true` or `false` to always turn it on or off

bundleAnalyzerReport: process.env.npm_config_report

}
}


'
'use strict'
// Template version: 1.3.1
// see http://vuejs-templates.github.io/webpack for documentation.

const path = require('path')

//引入跨域配置
var proxyConfig = require('./proxyConfig')
module.exports = {

dev: {


// Paths

assetsSubDirectory: 'static',

assetsPublicPath: '/',

//proxyTable: {}, //默认跨域配置为空

proxyTable: proxyConfig.proxy,


// Various Dev Server settings

host: 'localhost', // can be overwritten by process.env.HOST

port: 8886, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined

autoOpenBrowser: false,

errorOverlay: true,

notifyOnErrors: true,

poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-


/**

* Source Maps

*/


// https://webpack.js.org/configuration/devtool/#development

devtool: 'cheap-module-eval-source-map',


// If you have problems debugging vue-files in devtools,

// set this to false - it *may* help

// https://vue-loader.vuejs.org/en/options.html#cachebusting

cacheBusting: true,


cssSourceMap: true

},



build: {

// Template for index.html

index: path.resolve(__dirname, '../dist/index.html'),



// Paths

assetsRoot: path.resolve(__dirname, '../dist'),

assetsSubDirectory: 'static',

// 项目名字改变时这里需要变化 原先为assetsPublicPath: '.'

assetsPublicPath: './',


/**

* Source Maps

*/


productionSourceMap: true,

// https://webpack.js.org/configuration/devtool/#production

devtool: '#source-map',


// Gzip off by default as many popular static hosts such as

// Surge or Netlify already gzip all static assets for you.

// Before setting to `true`, make sure to:

// npm install --save-dev compression-webpack-plugin

productionGzip: false,

productionGzipExtensions: ['js', 'css'],



// Run the build command with an extra argument to

// View the bundle analyzer report after build finishes:

// `npm run build --report`

// Set to `true` or `false` to always turn it on or off

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:{

'^/':''

}

}

}
}
module.exports={

proxy:{

'/':{ //将localhost:8081 映射为 /apis

target:'http://localhost:8080',//接口地址

changeOrigin: true,// 如果接口跨域,需要进行这个参数配置

secure:false, //如果接口是HTTPS接口,需要设置成true

pathRewrite:{

'^/':''

}

}

}
}三、封装API 请求Url port.js三、封装API 请求Url port.js
export default {

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'

}
}
export default {

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 引入四、main.js 引入
import http from './plugins/http.js'
import ports from './plugins/ports'
Vue.prototype.http = http
Vue.prototype.ports = ports
import http from './plugins/http.js'
import ports from './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();

}

})


}
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();

}

})


}以上这篇Vue axios获取token临时令牌封装案例就是小编分享给大家的全部内容了,希望能给大家一个参考。