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临时令牌封装案例就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。

Vue axios获取token临时令牌封装案例

郑重声明:本网站发布的内容(图片、视频和文字)以及用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服

发表评论

登录后才能评论