京东11.11大促主会场领京享红包更优惠

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 9232|回复: 0

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

[复制链接]

30

主题

0

回帖

10

积分

新手上路

积分
10
发表于 2020-9-18 19:29:58 | 显示全部楼层 |阅读模式 来自 中国
前言5 M- }/ j$ A% K1 |. O
为什么非要写这个博客呢?因为这件事让我有一种蛋蛋的优疼。剩下的都别问,反正问我也不会说。因为流程图我都不想(懒得)画。
7 C* Y7 d. {  h6 ~+ {* p开发架构
+ U' U3 Q2 Z" \6 o5 Y. e* \前端页面:Vue
- |7 P8 b0 f0 u" U7 E  Q9 S网络请求:Axios;方式:vue add axios
, x" c) L" i4 j! R  W, ]缓存方案" A7 k+ J5 C; F. |7 L
全局变量:Vuex  ]) j2 X$ U# ]5 |
本地缓存:LocalStorage
, ?7 Y( h& o: E4 x% d) L3 w技术依赖4 i3 E5 f& e5 T" o+ }  u) l6 [
你猜?- z0 V2 u: C' a2 U" |( w
背景/ J; ^2 i& k! V8 Y1 j4 b
公司开发一个嵌入App的Web页面,安全方面使用老套路:App通过URL传参给前端(包含签名),前端把参数透传给H5后端验签,完事儿之后前端再决定用户是否合法。另外定义了N个JS方法前端根据固定GET参数判断是安卓还是苹果来调用。
0 @$ n( ]1 {0 H7 c" u初步设想
0 s1 V. H- ]# Y0 Y% p4 ?关于token设计方案的初步设想是这样的:第一次进入的时候获取token,后端检查签名是否通过。不通过则弹框请从合法途径进入页面并且不消失。- }' F5 e7 n. F& s
否则就可以让用户继续后续操作,直到后端返回token过期特定状态码回来前端在用户无感的情况下调用JS方法重新获取URL参数请求token,完事儿之后继续用户的请求操作。(为避免用户使用旧token在其他地方操作数据,每次获取token都重新从App中获取并验证,而不是在接口中刷新并返回新的token)
5 ]% n* U( @( u3 J% t, S蛋疼事项! {9 W8 E1 b  [# A
一期的时候定义URL参数时没有版本控制,导致二期新增JS方法迭代版本时前端新增页面调用了未知方法页面毫无反应;埋点数据也不知道是几期的…( l$ K/ _7 e, \) l% g4 e! c1 v8 s. O1 W
为尽量避免请求过程中出现token过期导致的1次请求变3次请求现象每次调用请求之前需要先检查token时效的异步方法(如果token过期则调用getToken获取新的token并存储在本地)导致block嵌套。7 S$ [/ p3 ^- J; U3 m
后面又封装了N个方法就不说了…" K- x: S, M( d" Z" H4 \
升级设想4 ]9 B; _' l' i0 p
版本什么的这个先不说,就这个token问题我总不能每次新增一个请求就复制粘贴复制粘贴的吧?能烦死人!那我只能在axios请求之前判断token时效性啦。+ v4 ]0 ?3 F( Z/ v# g. v: Z
直奔主题; T) U4 _5 `5 H1 Q# q$ J' I9 J1 L
函数声明9 Y4 o% g6 F7 o# e  P6 \
getToken:从本地取已存储token; k4 h- S- Q9 t3 _  }, [, \% t
checkToken:检查token时效,失效调用refreshToken函数成功则存储本地,否则返回错误原因
) B4 y" s5 z8 h1 t1 y6 V# Y& grefreshToken:调用JS方法从App获取签名参数重新请求token$ _3 I1 A* x/ J2 a
注意事项4 q& N; k3 x: Y  U
在checkToken过程中token过期时,先移除本地已过期token缓存数据。
  1. /* 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-varsconst 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失效刷新$ `* d! L  Z8 U5 U, \9 _
一、封装axios
  1. 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
% L; T' K$ n( U) N1.config-->index.js
  1. ''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文件
  1. module.exports={  proxy:{    '/':{ //将localhost:8081 映射为 /apis      target:'http://localhost:8080',//接口地址      changeOrigin: true,// 如果接口跨域,需要进行这个参数配置      secure:false, //如果接口是HTTPS接口,需要设置成true      pathRewrite:{        '^/':''      }    }  }}
复制代码
三、封装API 请求Url port.js
  1. 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 引入
  1. import http from './plugins/http.js'import ports from './plugins/ports'Vue.prototype.http = httpVue.prototype.ports = ports
复制代码
五、使用
: k- l/ H1 E9 m8 ]! Z% _login.vue中使用
  1. 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临时令牌封装案例就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持脚本之家。
& ~' S! d! k) j+ ?. {% D( U* e% f
# e9 S* z0 R/ Z% D- y来源:http://www.jb51.net/article/195457.htm
* Y1 H3 k: j2 \( u免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

帖子地址: 

梦想之都-俊月星空 优酷自频道欢迎您 http://i.youku.com/zhaojun917
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

站长推荐上一条 /6 下一条

QQ|手机版|小黑屋|梦想之都-俊月星空 ( 粤ICP备18056059号 )

GMT+8, 2025-2-25 20:16 , Processed in 0.035502 second(s), 22 queries .

Powered by Mxzdjyxk! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表