token过期了怎么办?

token过期了怎么办?一般做法是重复第一次获取token的过程(比如登录,扫描授权等) ,这样做的缺点是用户体验不好,每一小时强制登录一次几乎是无法忍受的。那应该怎么办呢?其实这是一个老生常谈的问题,但是最近发现很多人并不清楚,所以今天就一次讲清这个问题!

token 过期处理

没有绝对的安全, 所谓的安全处理, 就是提高攻击者攻击的难度, 对他造成了一定的麻烦, 我们这个网站就是安全的! 网站安全性就是高的! 所以: token 必须要有过期时间!

token 过期问题

目标: 了解token过期问题的存在, 学习token过期的解决思路

现象:

你登陆成功之后,接口会返回一个token值,这个值在后续请求时带上(就像是开门钥匙)。

但是,这个值一般会有有效期(具体是多长,是由后端决定),在我们的项目中,这个有效期是2小时。

如果,上午8点登陆成功,到了10:01分,则token就会失效,再去发请求时,就会报401错误。

思考:

  1. token需要过期时间吗 ?

    token即是获取受保护资源的凭证,当然必须有过期时间。否则一次登录便可永久使用,认证功能就失去了其意义。非但必须有个过期时间,而且过期时间还不能太长,

    参考各个主流网站的token过期时间,一般1小时左右

    token一旦过期, 一定要处理, 不处理, 用户没法进行一些需要授权页面的使用了

  2. token过期该怎么办?

    token过期,就要重新获取。

    那么重新获取有两种方式,一是重复第一次获取token的过程(比如登录,扫描授权等) ,这样做的缺点是用户体验不好,每一小时强制登录一次几乎是无法忍受的。

    那么还剩第二种方法,那就是主动去刷新token. 主动刷新token的凭证是refresh token,也是加密字符串,并且和token是相关联的。相比可以获取各种资源的token,refresh token的作用仅仅是获取新的token,因此其作用和安全性要求都大为降低,所以其过期时间也可以设置得长一些。

目标效果 - 保证每一小时, 都是一个不同的token

第一次请求 9:00 用的是 token1第二次请求 12:00 用的是 token2

当用户登陆成功之后,返回的token中有两个值,说明如下:

  • token:

    • 作用:在访问一些接口时,需要传入token,就是它。
    • 有效期:2小时。
  • refresh_token

    • 作用: 当token的有效期过了之后,可以使用它去请求一个特殊接口(这个接口也是后端指定的,明确需要传入refresh_token),并返回一个新的token回来(有效期还是2小时),以替换过期的那个token。
    • 有效期:14天。(最理想的情况下,一次登陆可以持续14天。)

对于 某次请求A 的响应,如果是401错误

  • 有refresh_token,用refresh_token去请求回新的token

    • 新token请求成功

      • 更新本地token
      • 再发一次请求A
    • 新token请求失败

      • 清空vuex中的token
      • 携带请求地址,跳转到登陆页
  • 没有refresh_token

    • 清空vuex中的token
    • 携带请求地址,跳转到登陆页

对于一个请求的响应 401, 要这么处理, 对于十个请求的响应 401, 也要这么处理,

我们可以统一将这个token过期处理放在响应拦截器中

请求拦截器: 所有的请求, 在真正被发送出去之前, 都会先经过请求拦截器 (可以携带token)

响应拦截器: 所有的响应, 在真正被(.then.catch await)处理之前, 都会先经过响应拦截器, 可以在这个响应拦截器中统一对响应做判断

响应拦截器处理token

目标: 通过 axios 响应拦截器来处理 token 过期的问题

响应拦截器: www.kancloud.cn/yunye/axios...

  1. 没有 refresh_token 拦截到登录页, 清除无效的token

测试: {"token":"123.123.123"}

php 复制代码
// 添加响应拦截器
http.interceptors.response.use(function (response) {
  // 对响应数据做点什么 (成功响应) response 就是成功的响应 res
  return response
}, function (error) {
  // 对响应错误做点什么 (失败响应) 处理401错误
  // console.dir(error)
  if (error.response.status === 401) {
    console.log('token过期了, 一小时过去了, 需要通过refresh_token去刷新token')
    // 获取 refresh_token, 判断是否存在, 存在就去刷新token
    const refreshToken = store.state.tokenInfo.refresh_token
    if (refreshToken) {
      console.log('存在refreshToken, 需要进行刷新token操作')
    } else {
      // 没有refreshToken, 直接去登录, 将来还能跳回来
      // router.currentRoute 指向当前路由信息对象 === 等价于之前页面中用的 this.$route
      // 清除本地token, 跳转登录 (无意义的本地token内容, 要清除)
      store.commit('removeToken')
      router.push({
        path: '/login',
        query: {
          backto: router.currentRoute.fullPath
        }
      })
    }
  }
  return Promise.reject(error)
})

提供清除token的mutation

scss 复制代码
// 移出tokenInfo的信息, 恢复成空对象
removeToken (state) {
  state.tokenInfo = {}
  // 更新到本地, 本地可以清掉token信息
  removeToken()
},
  1. 有 refresh_token 发送请求, 刷新token

测试操作: 将 token 修改成 xyz, 模拟 token 过期, 而有 refresh_token 发现401, 会自动帮你刷新token

{"refresh_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2MDYzNTcyODcsInVzZXJfaWQiOjExMDI0OTA1MjI4Mjk3MTc1MDQsInJlZnJlc2giOnRydWV9.2A81gpjxP_wWOjclv0fzSh1wzNm6lNy0iXM5G5l7TQ4","token":"xyz"}

php 复制代码
const refreshToken = store.state.tokenInfo.refresh_token
if (refreshToken) {
  console.log('存在refreshToken, 需要进行刷新token操作')
  // (1) 发送请求, 进行刷新token操作, 获取新的token
  // 注意: 这边发请求, 不用http实例, 用它会自动在请求前帮你携带token(会覆盖你的refresh_token)
  // 这边, 直接用 axios 发送请求
  const res = await axios({
    method: 'put',
    url: 'http://ttapi.research.itcast.cn/app/v1_0/authorizations',
    // 请求头中携带refresh_token信息
    headers: {
      Authorization: `Bearer ${refreshToken}`
    }
  })
  const newToken = res.data.data.token
  // (2) 将新token更新到vuex中
  store.commit('setTokenInfo', {
    refresh_token: refreshToken,
    token: newToken
  })
} 
  1. 刷新token后, 应该重新发送刚才的请求 (让用户刷新token无感知)
lua 复制代码
return http(error.config)
  1. 那万一 refresh_token 也过期了, 是真正的用户登录过期了 (一定要让用户重新登录的)

测试: {"refresh_token":"123.123","token":"123.123.123"} 修改后, 修改的是本地, 记得刷新一下

从哪拦走的, 就回到哪去

php 复制代码
// 添加响应拦截器
http.interceptors.response.use(function (response) {
  // 对响应数据做点什么 (成功响应) response 就是成功的响应 res
  return response
}, async function (error) {
  // 对响应错误做点什么 (失败响应) 处理401错误
  // console.dir(error)
  if (error.response.status === 401) {
    console.log('token过期了, 一小时过去了, 需要通过refresh_token去刷新token')
    // 获取 refresh_token, 判断是否存在, 存在就去刷新token
    const refreshToken = store.state.tokenInfo.refresh_token
    if (refreshToken) {
      try {
        console.log('存在refreshToken, 需要进行刷新token操作')
        // (1) 发送请求, 进行刷新token操作, 获取新的token
        // 注意: 这边发请求, 不用http实例, 用它会自动在请求前帮你携带token(会覆盖你的refresh_token)
        // 这边, 直接用 axios 发送请求
        const res = await axios({
          method: 'put',
          url: 'http://ttapi.research.itcast.cn/app/v1_0/authorizations',
          // 请求头中携带refresh_token信息
          headers: {
            Authorization: `Bearer ${refreshToken}`
          }
        })
        const newToken = res.data.data.token
        // (2) 将新token更新到vuex中
        store.commit('setTokenInfo', {
          refresh_token: refreshToken,
          token: newToken
        })
        // (3) 重新发送刚才的请求, http, 自动携带token (携带的是新token)
        //     error.config就是之前用于请求的配置对象, 可以直接给http使用
        return http(error.config)
      } catch {
        // refresh_token 过期了, 跳转到登录页
        // 清除过期的token对象
        store.commit('removeToken')
        // 跳转到登录页, 跳转完, 将来跳回来
        router.push({
          path: '/login',
          query: {
            backto: router.currentRoute.fullPath
          }
        })
      }
    } else {
      // 没有refreshToken, 直接去登录, 将来还能跳回来
      // router.currentRoute 指向当前路由信息对象 === 等价于之前页面中用的 this.$route
      // 清除本地token, 跳转登录 (无意义的本地token内容, 要清除)
      store.commit('removeToken')
      router.push({
        path: '/login',
        query: {
          backto: router.currentRoute.fullPath
        }
      })
    }
  }
  return Promise.reject(error)
})

注意点:

  1. 响应拦截器要加在axios实例 http 上。
  2. 用refresh_token请求新token时,要用axios,不要用实例 http (需要: 手动用 refresh_token 请求)
  3. 得到新token之后,再发请求时,要用 http 实例 (用token请求)
  4. 过期的 token 可以用 refresh_token 再次更新获取新token, 但是过期的 refresh_token 就应该从清除了
相关推荐
undefined&&懒洋洋12 分钟前
Web和UE5像素流送、通信教程
前端·ue5
大前端爱好者2 小时前
React 19 新特性详解
前端
随云6322 小时前
WebGL编程指南之着色器语言GLSL ES(入门GLSL ES这篇就够了)
前端·webgl
随云6322 小时前
WebGL编程指南之进入三维世界
前端·webgl
寻找09之夏3 小时前
【Vue3实战】:用导航守卫拦截未保存的编辑,提升用户体验
前端·vue.js
多多米10054 小时前
初学Vue(2)
前端·javascript·vue.js
柏箱4 小时前
PHP基本语法总结
开发语言·前端·html·php
新缸中之脑4 小时前
Llama 3.2 安卓手机安装教程
前端·人工智能·算法
hmz8564 小时前
最新网课搜题答案查询小程序源码/题库多接口微信小程序源码+自带流量主
前端·微信小程序·小程序
看到请催我学习4 小时前
内存缓存和硬盘缓存
开发语言·前端·javascript·vue.js·缓存·ecmascript