Vue axios获取token临时令牌封装案例
前言
为什么非要写这个博客呢?因为这件事让我有一种蛋蛋的优疼。剩下的都别问,反正问我也不会说。因为流程图我都不想(懒得)画。
开发架构
前端页面: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:从本地取已存储token
checktoken:检查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;
补充知识:vue+ axios+token 封装axios 封装接口url,带token请求,token失效刷新
一、封装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) } } ) } }
二、配置axios 跨域,以及请求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: {}, //默认跨域配置为空 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:{ '^/':'' } } } }
三、封装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' } }
四、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) { // 返回正确的处理 页面跳转 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临时令牌封装案例就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。