当前位置: 移动技术网 > IT编程>脚本编程>vue.js > Vue axios获取token临时令牌封装案例

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

2020年09月11日  | 移动技术网IT编程  | 我要评论
前言为什么非要写这个博客呢?因为这件事让我有一种蛋蛋的优疼。剩下的都别问,反正问我也不会说。因为流程图我都不想(懒得)画。开发架构前端页面:vue网络请求:axios;方式:vue add axios

前言

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

开发架构

前端页面: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: path.resolve(__dirname, '../dist/'),
 
 
    // 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临时令牌封装案例就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持移动技术网。

如您对本文有疑问或者有任何想说的,请点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网