Vue axios獲取token臨時(shí)令牌封裝案例
前言
為什么非要寫這個(gè)博客呢?因?yàn)檫@件事讓我有一種蛋蛋的優(yōu)疼。剩下的都別問(wèn),反正問(wèn)我也不會(huì)說(shuō)。因?yàn)榱鞒虉D我都不想(懶得)畫。
開(kāi)發(fā)架構(gòu)
前端頁(yè)面:Vue
網(wǎng)絡(luò)請(qǐng)求:Axios;方式:vue add axios
緩存方案
全局變量:Vuex
本地緩存:LocalStorage
技術(shù)依賴
你猜?
背景
公司開(kāi)發(fā)一個(gè)嵌入App的Web頁(yè)面,安全方面使用老套路:App通過(guò)URL傳參給前端(包含簽名),前端把參數(shù)透?jìng)鹘oH5后端驗(yàn)簽,完事兒之后前端再?zèng)Q定用戶是否合法。另外定義了N個(gè)JS方法前端根據(jù)固定GET參數(shù)判斷是安卓還是蘋果來(lái)調(diào)用。
初步設(shè)想
關(guān)于token設(shè)計(jì)方案的初步設(shè)想是這樣的:第一次進(jìn)入的時(shí)候獲取token,后端檢查簽名是否通過(guò)。不通過(guò)則彈框請(qǐng)從合法途徑進(jìn)入頁(yè)面并且不消失。
否則就可以讓用戶繼續(xù)后續(xù)操作,直到后端返回token過(guò)期特定狀態(tài)碼回來(lái)前端在用戶無(wú)感的情況下調(diào)用JS方法重新獲取URL參數(shù)請(qǐng)求token,完事兒之后繼續(xù)用戶的請(qǐng)求操作。(為避免用戶使用舊token在其他地方操作數(shù)據(jù),每次獲取token都重新從App中獲取并驗(yàn)證,而不是在接口中刷新并返回新的token)
蛋疼事項(xiàng)
一期的時(shí)候定義URL參數(shù)時(shí)沒(méi)有版本控制,導(dǎo)致二期新增JS方法迭代版本時(shí)前端新增頁(yè)面調(diào)用了未知方法頁(yè)面毫無(wú)反應(yīng);埋點(diǎn)數(shù)據(jù)也不知道是幾期的…
為盡量避免請(qǐng)求過(guò)程中出現(xiàn)token過(guò)期導(dǎo)致的1次請(qǐng)求變3次請(qǐng)求現(xiàn)象每次調(diào)用請(qǐng)求之前需要先檢查token時(shí)效的異步方法(如果token過(guò)期則調(diào)用getToken獲取新的token并存儲(chǔ)在本地)導(dǎo)致block嵌套。
后面又封裝了N個(gè)方法就不說(shuō)了…
升級(jí)設(shè)想
版本什么的這個(gè)先不說(shuō),就這個(gè)token問(wèn)題我總不能每次新增一個(gè)請(qǐng)求就復(fù)制粘貼復(fù)制粘貼的吧?能煩死人!那我只能在axios請(qǐng)求之前判斷token時(shí)效性啦。
直奔主題
函數(shù)聲明
getToken:從本地取已存儲(chǔ)token
checkToken:檢查token時(shí)效,失效調(diào)用refreshToken函數(shù)成功則存儲(chǔ)本地,否則返回錯(cuò)誤原因
refreshToken:調(diào)用JS方法從App獲取簽名參數(shù)重新請(qǐng)求token
注意事項(xiàng)
在checkToken過(guò)程中token過(guò)期時(shí),先移除本地已過(guò)期token緩存數(shù)據(jù)。
/* 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);
//發(fā)起請(qǐng)求時(shí),取消掉當(dāng)前正在進(jìn)行的相同請(qǐng)求
if (promiseArr[config.url]) {
promiseArr[config.url]("請(qǐng)稍后");
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) => {
// 終止這個(gè)請(qǐng)求
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("| 請(qǐng)求地址:", response.config.url);
// console.log("| 請(qǐng)求參數(shù):", response.config.data);
// console.log("| 返回?cái)?shù)據(jù):", 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('接口二次請(qǐng)求 result:', result);
if (err_check(status, statusText, data) && data) {
return Promise.resolve(data)
} else {
return Promise.reject(Error(data.resDesc));
}
}).catch((err) => {
// console.log('接口二次請(qǐng)求 err:' + err);
return Promise.reject(err);
});
} else {
// alert("result:" + result)
return Promise.reject(Error(data.resDesc))
}
}).catch((err) => {
// 終止這個(gè)請(qǐng)求
// alert("終止這個(gè)請(qǐng)求:" + 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;
補(bǔ)充知識(shí):vue+ axios+token 封裝axios 封裝接口url,帶token請(qǐng)求,token失效刷新
一、封裝axios
import axios from 'axios'
import qs from "qs"
const TIME_OUT_MS = 60 * 1000 // 默認(rèn)請(qǐng)求超時(shí)時(shí)間
//axios.defaults.baseURL = 'http://localhost:8080';
// http request 攔截器
axios.interceptors.request.use(
config => {
if ($cookies.get("access_token")) { // 判斷是否存在token,如果存在的話,則每個(gè)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 過(guò)期");
var config = error.config;
refresh(config);
return;
}
}
return Promise.reject(error) // 返回接口返回的錯(cuò)誤信息
});
/*
*刷新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);
//需要重新執(zhí)行
axios(config);
}else{
//this.$events.emit('goto', 'login');
window.location.reload();
}
}
).catch((error) => {
//this.$events.emit('goto','login');
window.location.reload();
});
}
/*
* @param response 返回?cái)?shù)據(jù)列表
*/
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 參數(shù)列表
* @return
*/
function handleParams (data) {
return qs.stringify(data);
}
export default {
/*
* @param url
* @param data
* @param response 請(qǐng)求成功時(shí)的回調(diào)函數(shù)
* @param exception 異常的回調(diào)函數(shù)
*/
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 請(qǐng)求
* @param url
* @param response 請(qǐng)求成功時(shí)的回調(diào)函數(shù)
* @param exception 異常的回調(diào)函數(shù)
*/
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 跨域,以及請(qǐng)求baseUrl
1.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: {}, //默認(rèn)跨域配置為空
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',
// 項(xiàng)目名字改變時(shí)這里需要變化 原先為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目錄下創(chuàng)建一個(gè)文件 proxyConfig.js文件
module.exports={
proxy:{
'/':{ //將localhost:8081 映射為 /apis
target:'http://localhost:8080',//接口地址
changeOrigin: true,// 如果接口跨域,需要進(jìn)行這個(gè)參數(shù)配置
secure:false, //如果接口是HTTPS接口,需要設(shè)置成true
pathRewrite:{
'^/':''
}
}
}
}
三、封裝API 請(qǐng)求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'
}
}
四、main.js 引入
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) {
// 返回正確的處理
頁(yè)面跳轉(zhuǎn)
this.$events.emit('goto', 'edit');
} else {
// 返回錯(cuò)誤的處理
//alert("等待處理");
}
},err =>{
//console.log("正在處理"+err.response.status);
if(err.response.status=='400'){
//顯示用戶名或密碼錯(cuò)誤
this.$refs.username.focus();
this.$refs.hint.click();
}
})
}
以上這篇Vue axios獲取token臨時(shí)令牌封裝案例就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
- 基于axios請(qǐng)求封裝的vue應(yīng)用實(shí)例代碼
- Vue項(xiàng)目中如何封裝axios(統(tǒng)一管理http請(qǐng)求)
- Vue3+TypeScript封裝axios并進(jìn)行請(qǐng)求調(diào)用的實(shí)現(xiàn)
- vue中axios封裝使用的完整教程
- vue中封裝axios并實(shí)現(xiàn)api接口的統(tǒng)一管理
- Vue+axios封裝請(qǐng)求實(shí)現(xiàn)前后端分離
- 詳解Vue中Axios封裝API接口的思路及方法
- Vue封裝Axios請(qǐng)求和攔截器的步驟
- vue axios封裝httpjs,接口公用配置攔截操作
- vue+ts下對(duì)axios的封裝實(shí)現(xiàn)
- Vue簡(jiǎn)單封裝axios之解決post請(qǐng)求后端接收不到參數(shù)問(wèn)題
- 關(guān)于Vue中axios的封裝實(shí)例詳解
- vue封裝axios的幾種方法
相關(guān)文章
vue實(shí)現(xiàn)動(dòng)態(tài)控制表格列的顯示隱藏
這篇文章主要為大家詳細(xì)介紹了vue實(shí)現(xiàn)動(dòng)態(tài)控制表格列的顯示隱藏,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04
Vux+Axios攔截器增加loading的問(wèn)題及實(shí)現(xiàn)方法
這篇文章主要介紹了Vux+Axios攔截器增加loading的問(wèn)題及實(shí)現(xiàn)方法,文中通過(guò)實(shí)例代碼介紹了vue中使用axios的相關(guān)知識(shí),需要的朋友可以參考下2018-11-11
vue2?對(duì)全局自定義指令一次性進(jìn)行注冊(cè)的方法
這篇文章主要介紹了vue2?對(duì)全局自定義指令一次性進(jìn)行注冊(cè),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-06-06
Vue.js實(shí)現(xiàn)的計(jì)算器功能完整示例
這篇文章主要介紹了Vue.js實(shí)現(xiàn)的計(jì)算器功能,結(jié)合完整實(shí)例形式分析了vue.js響應(yīng)鼠標(biāo)事件實(shí)現(xiàn)基本的數(shù)值運(yùn)算相關(guān)操作技巧,可實(shí)現(xiàn)四則運(yùn)算及乘方、開(kāi)方等功能,需要的朋友可以參考下2018-07-07
vue 查看dist文件里的結(jié)構(gòu)(多種方式)
本文通過(guò)三種方式給大家介紹了vue 查看dist文件里的結(jié)構(gòu),非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-01-01

