vue的token刷新处理的方法


Posted in Javascript onJuly 17, 2018

第一次接触token处理,初来乍到,说错的地方还请各位多多指教。

token身份验证机制

客户端登录请求成功后,服务器将用户信息(如用户id)使用特殊算法加密后作为验证的标志发送给用户(即token),当用户下次发起请求时,会将这个token捎带过来,服务器再将这个token通过解密后进行验证,通过的话,则向客户端返回请求的数据;反之,则请求失败。

token优点

它是无状态的,且服务器不用像传统的身份认证(session)那样需要保存会话信息,减轻了服务器的压力。

vue的token刷新处理

在对token身份验证机制进行一次简单介绍后,进入正文...

一般为了安全性,token都会设置一个过期时间,在过期之后就无法请求相关接口了,这时应该怎么办呢,是直接退出登录吗?

在目前公司的项目里,为了更好的用户体验,我们选择手动刷新token。登录请求成功后,会返回一个token和token过期时间,在每次请求api时,前端可以先判断一下token是否即将过期或已过期,如果是,则请求刷新token的接口,成功替换原来的token之后才可以重新发起请求。

下面,我们直接看代码,这是在vue的请求拦截器里进行的相关操作:

/*是否有请求正在刷新token*/
window.isRefreshing = false
/*被挂起的请求数组*/
let refreshSubscribers = []

/*获取刷新token请求的token*/
function getRefreshToken () {
 return JSON.parse(localStorage.auth).refresh_token
}

/*push所有请求到数组中*/
function subscribeTokenRefresh (cb) {
 refreshSubscribers.push(cb)
}

/*刷新请求(refreshSubscribers数组中的请求得到新的token之后会自执行,用新的token去请求数据)*/
function onRrefreshed (token) {
 refreshSubscribers.map(cb => cb(token))
}

/*请求拦截器*/
ajax.interceptors.request.use(
 config => {
  const authTmp = localStorage.auth
  /*判断是否已登录*/
  if (authTmp) {
   /*解析登录信息*/
   let auth = JSON.parse(authTmp)
   /*判断auth是否存在*/
   if (auth) {
    /*在请求头中添加token类型、token*/
    config.headers.Authorization = auth.token_type + ' ' + auth.token
    /*判断刷新token请求的refresh_token是否过期*/
    if (util.isRefreshTokenExpired()) {
     alert('刷新token过期,请重新登录')
     /*清除本地保存的auth*/
     localStorage.removeItem('auth')
     window.location.href = '#/login'
     return
    }
    /*判断token是否将要过期*/
    if (util.isTokenExpired() && config.url.indexOf('admin/auth/current') === -1) {
     /*判断是否正在刷新*/
     if (!window.isRefreshing) {
      /*将刷新token的标志置为true*/
      window.isRefreshing = true
      /*发起刷新token的请求*/
      apiList.refreshToken({refresh_token: getRefreshToken()}).then(res => {
       /*将标志置为false*/
       window.isRefreshing = false
       /*成功刷新token*/
       config.headers.Authorization = res.data.data.token_type + ' ' + res.data.data.token
       /*更新auth*/
       localStorage.setItem('auth', JSON.stringify(res.data.data))
       /*执行数组里的函数,重新发起被挂起的请求*/
       onRrefreshed(res.data.data.token)
       /*执行onRefreshed函数后清空数组中保存的请求*/
       refreshSubscribers = []
      }).catch(err => {
       alert(err.response.data.message)
       /*清除本地保存的auth*/
       // localStorage.removeItem('auth')
       window.location.href = '#/login'
      })
     }
     /*把请求(token)=>{....}都push到一个数组中*/
     let retry = new Promise((resolve, reject) => {
      /*(token) => {...}这个函数就是回调函数*/
      subscribeTokenRefresh((token) => {
       config.headers.Authorization = 'Bearer ' + token
       /*将请求挂起*/
       resolve(config)
      })
     })
     return retry
    }
   }
   return config

  } else {
   /*未登录直接返回配置信息*/
   return config
  }
 },
 /*错误操作*/
 err => {
  return Promise.reject(err)
 }
)

这里需要注意几点:

1、当token即将过期或者已过期时,原则上,我们只需要有一个接口去触发刷新token的请求即可,这里的isRefreshing 变量,就起到这样一个监控的作用,它相当于一把锁,当刷新token的操作被触发后,其他的触发操作就被排斥在外了。

window.isRefreshing = false

2、刷新token的接口,用到了一个另外的token(refresh_token),这也是出于安全性考虑的,并且它也有过期时间,不过这个过期时间一般都比普通token的过期时间要长,所以在上面代码中,会发现,我在请求拦截中优先判断了refresh_token是否过期,如果过期则直接退出登录,不再进行下一步的操作。

/*判断刷新token请求的refresh_token是否过期*/
if (util.isRefreshTokenExpired() && config.url.indexOf('admin/auth/current') === -1) {
 alert('刷新token过期,请重新登录')
 /*清除本地保存的auth*/
 localStorage.removeItem('auth')
 window.location.href = '#/login'
 return
}

3、在触发了刷新token的操作后,我们还需要先将其他的请求挂起,在获取新的token之后再重新发起这些请求。

/*把请求(token)=>{....}都push到一个数组中*/
let retry = new Promise((resolve, reject) => {
 /*(token) => {...}这个函数就是回调函数*/
 subscribeTokenRefresh((token) => {
  config.headers.Authorization = 'Bearer ' + token
  /*将请求挂起*/
  resolve(config)
 })
})
return retry

在刷新token请求的成功回调里执行下面代码,重新发起请求。

/*执行数组里的函数,重新发起被挂起的请求*/
 onRrefreshed(res.data.data.token)

4、因为有人在评论里问util文件,应该是想知道具体怎么判断token过期的,其实在获得token时,是有返回一个token过期时间 ,你可以先将它先保存起来,然后在需要时,拿出来与本地时间比较即可

/*判断token是否过期*/
function isTokenExpired() {
 /*从localStorage中取出token过期时间*/
 let expiredTime = new Date(JSON.parse(localStorage.auth).expired_at).getTime() / 1000
 /*获取本地时间*/
 let nowTime = new Date().getTime() / 1000
 /*获取校验时间差*/
 let diffTime = JSON.parse(sessionStorage.diffTime)
 /*校验本地时间*/
 nowTime -= diffTime
 /*如果 < 10分钟,则说明即将过期*/
 return (expiredTime - nowTime) < 10*60
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
动态创建的表格单元格中的事件实现代码
Dec 30 Javascript
php跨域调用json的例子
Nov 13 Javascript
javascript实时显示当天日期的方法
May 20 Javascript
jquery trigger函数执行两次的解决方法
Feb 29 Javascript
第一次接触神奇的Bootstrap
Oct 14 Javascript
微信小程序 wxapp视图容器 view详解
Oct 31 Javascript
微信小程序 表单Form实例详解(附源码)
Dec 22 Javascript
工作中常用的js、jquery自定义扩展函数代码片段汇总
Dec 22 Javascript
jQuery实现可编辑表格并生成json结果(实例代码)
Jul 19 jQuery
d3.js实现自定义多y轴折线图的示例代码
May 30 Javascript
js禁止查看源文件屏蔽Ctrl+u/s、F12、右键等兼容IE火狐chrome
Oct 01 Javascript
微信小程序入门之指南针
Oct 22 Javascript
vue 权限认证token的实现方法
Jul 17 #Javascript
vue 实现axios拦截、页面跳转和token 验证
Jul 17 #Javascript
详解webpack import()动态加载模块踩坑
Jul 17 #Javascript
vue-router中的hash和history两种模式的区别
Jul 17 #Javascript
JS使用tween.js动画库实现轮播图并且有切换功能
Jul 17 #Javascript
SVG实现时钟效果
Jul 17 #Javascript
JS中实现隐藏部分姓名或者电话号码的代码
Jul 17 #Javascript
You might like
MOTOROLA 摩托罗拉 MODEL 66-XI五灯中波收音机
2021/03/02 无线电
PHP IPV6正则表达式验证代码
2010/02/16 PHP
php遍历目录输出目录及其下的所有文件示例
2014/01/27 PHP
js 事件小结 表格区别
2007/08/13 Javascript
通过Jquery遍历Json的两种数据结构的实现代码
2011/01/19 Javascript
浅谈关于JavaScript API设计的一些建议和准则
2015/06/24 Javascript
jquery 全选、全不选、反选效果的实现代码【推荐】
2016/05/05 Javascript
JavaScript操作 url 中 search 部分方法函数
2016/06/15 Javascript
AngularJS表单详解及示例代码
2016/08/17 Javascript
任意Json转成无序列表的方法示例
2016/12/09 Javascript
JS实现touch 点击滑动轮播实例代码
2017/01/19 Javascript
JS实现利用两个队列表示一个栈的方法
2017/12/13 Javascript
关于Webpack dev server热加载失败的解决方法
2018/02/22 Javascript
Vue + better-scroll 实现移动端字母索引导航功能
2018/05/07 Javascript
JS求1到任意数之间的所有质数的方法详解
2019/05/20 Javascript
Node.js中console.log()输出彩色字体的方法示例
2019/12/01 Javascript
微信小程序顶部导航栏可滑动并选中放大
2019/12/05 Javascript
基于vue和websocket的多人在线聊天室
2020/02/01 Javascript
jQuery+css实现的点击图片放大缩小预览功能示例【图片预览 查看大图】
2020/05/29 jQuery
js实现简单的轮播图效果
2020/12/13 Javascript
[04:32]DOTA2著名解说配音敌法师 现场专访海涛怒切假腿
2013/12/20 DOTA
[01:18:21]EG vs TNC Supermajor小组赛B组败者组第一轮 BO3 第一场 6.2
2018/06/03 DOTA
Python实现监控键盘鼠标操作示例【基于pyHook与pythoncom模块】
2018/09/04 Python
使用pandas把某一列的字符值转换为数字的实例
2019/01/29 Python
python的setattr函数实例用法
2020/12/16 Python
瑞士男士时尚网上商店:Babista
2020/05/14 全球购物
laravel使用redis队列实例讲解
2021/03/23 PHP
四年大学生活的个人自我评价
2013/12/11 职场文书
优秀的毕业生的自我评价
2013/12/12 职场文书
母亲80寿诞答谢词
2014/01/16 职场文书
干部作风整顿个人剖析材料
2014/10/06 职场文书
党员群众路线个人整改措施思想汇报
2014/10/12 职场文书
党员反对四风思想汇报范文
2014/10/25 职场文书
师德承诺书
2015/01/20 职场文书
2016年安全生产先进个人事迹材料
2016/02/29 职场文书
详解redis分布式锁的这些坑
2021/05/19 Redis