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


Posted in Javascript onSeptember 11, 2020

前言

为什么非要写这个博客呢?因为这件事让我有一种蛋蛋的优疼。剩下的都别问,反正问我也不会说。因为流程图我都不想(懒得)画。

开发架构

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

Javascript 相关文章推荐
关于JavaScript的gzip静态压缩方法
Jan 05 Javascript
深入理解Javascript动态方法调用与参数修改的问题
Dec 10 Javascript
Javascript实现简单二级下拉菜单实例
Jun 15 Javascript
jQuery中replaceAll()方法用法实例
Jan 16 Javascript
javascript如何操作HTML下拉列表标签
Aug 20 Javascript
使用jquery实现鼠标滑过弹出更多相关信息层附源码下载
Nov 23 Javascript
jQuery模仿阿里云购买服务器选择购买时间长度的代码
Apr 29 Javascript
JS及PHP代码编写八大排序算法
Jul 12 Javascript
JavaScript初学者必看“new”
Jun 12 Javascript
jQuery制作全屏宽度固定高度轮播图(实例讲解)
Jul 08 jQuery
从零开始在vue-cli4配置自适应vw布局的实现
Jun 08 Javascript
Javascript中window.name属性详解
Nov 19 Javascript
Vue-cli4 配置 element-ui 按需引入操作
Sep 11 #Javascript
vue 子组件和父组件传值的示例
Sep 11 #Javascript
jQuery实现朋友圈查看图片
Sep 11 #jQuery
详解webpack的文件监听实现(热更新)
Sep 11 #Javascript
js代码编写无缝轮播图
Sep 13 #Javascript
vue-cli脚手架的.babelrc文件用法说明
Sep 11 #Javascript
解决vue与node模版引擎的渲染标记{{}}(双花括号)冲突问题
Sep 11 #Javascript
You might like
PHP自定义函数获取URL中一级域名的方法
2016/08/23 PHP
YII2框架使用控制台命令的方法分析
2020/03/18 PHP
php模拟实现斗地主发牌
2020/04/22 PHP
仿校内登陆框,精美,给那些很厉害但是没有设计天才的程序员
2008/11/24 Javascript
jQuery解决iframe高度自适应代码
2009/12/20 Javascript
用js判断页面刷新或关闭的方法(onbeforeunload与onunload事件)
2012/06/22 Javascript
基于jquery自定义图片热区效果
2012/07/21 Javascript
JavaScript实现自动变换表格边框颜色
2015/05/08 Javascript
javascript鼠标滑动评分控件完整实例
2015/05/13 Javascript
AngularJS 使用ng-repeat报错 [ngRepeat:dupes]
2017/01/19 Javascript
mui 打开新窗口的方式总结及注意事项
2017/08/20 Javascript
微信小程序picker组件下拉框选择input输入框的实例
2017/09/20 Javascript
JS实现灯泡开关特效
2020/03/30 Javascript
vue调用微信JSDK 扫一扫,相册等需要注意的事项
2021/01/03 Vue.js
vue3弹出层V3Popup实例详解
2021/01/04 Vue.js
[01:38:19]夜魇凡尔赛茶话会 第五期
2021/03/11 DOTA
python生成器generator用法实例分析
2015/06/04 Python
Python计算已经过去多少个周末的方法
2015/07/25 Python
详解Python发送邮件实例
2016/01/10 Python
Python入门_条件控制(详解)
2017/05/16 Python
python 获取文件下所有文件或目录os.walk()的实例
2018/04/23 Python
python 实现求解字符串集的最长公共前缀方法
2018/07/20 Python
python3读取csv文件任意行列代码实例
2020/01/13 Python
sklearn+python:线性回归案例
2020/02/24 Python
python多线程和多进程关系详解
2020/12/14 Python
使用Django的JsonResponse返回数据的实现
2021/01/15 Python
Expedia加拿大官方网站:加拿大最大的在线旅游提供商
2017/12/31 全球购物
Shopty西班牙:缝纫机在线销售
2018/01/26 全球购物
一名毕业生的自我鉴定
2013/12/04 职场文书
单位活动策划方案
2014/08/17 职场文书
2014年应急工作总结
2014/12/11 职场文书
买卖合同纠纷代理词
2015/05/25 职场文书
毕业生政审意见范文
2015/06/04 职场文书
初中班主任培训心得体会
2016/01/07 职场文书
银行岗位培训心得体会
2016/01/09 职场文书
Python基于百度AI实现抓取表情包
2021/06/27 Python