用vuex对token/refresh_token 进行管理以及处理token过期问题

news2024/9/23 11:15:02

这里介绍对token的处理
问题1:token数据或者其他数据,存在vuex仓库中,刷新会丢失状态 。
问题2:数据只存在本地,数据变化了,相关的视图并不会更新。 Vuex 容器中的数据只是为了方便在其他任何地方能方便的获取登录状态数据, 但是页面刷新还是会丢失数据状态,所以我们还要把 数据进行持久化 以防止页面刷新丢失状态的问题。两种方式配合达到存储+响应式的功能。
前端持久化常见的方式:Cookie,localstorage本地存储
基本思路就是:
用户登录成功之后,把token保存到本地的localstorage或者cookie中,在vuex中也存一份
在vuex容器初始化的时候,优先使用本地存储的值
使用vuex管理token
在之前,我们在我们是在登录页面中发送的登录请求之后,获取到token,然后通过提交mutation的方式将token存到了vuex中,然后这里我们希望将登录token的异步操作,也封装成action,封装到vuex中,集中管理关于token操作。
vue封装action存token,我们在这里存俩份,一份存进vuex的user.js分支中的state里面,一份存进本地的cookie中或者localstorage里面都行
store/modules/user.js 中准备状态,用来存放获取到的token信息,创建设置状态的mutations方法以及异步actions方法

const state = {
             token: '' // token字符串
               }
const mutations = {
   // 保存token的方法
   setTokenFn(state, newToken) {
     // 获取到token的时候,设置给token的同时也让setToken存进cookie中
     state.token = newToken
     setToken(newToken)
   },
}
const actions = {
   // 登录
   async login(context, loginForm) {
     const { data } = await reqLogin(loginForm)
     if (data.success) context.commit('setTokenFn', data.data)
   },
}
const getters = {}

export default {
 namespaced: true,
 state,
 mutations,
 getters
}

在登录页面中调用

    handleLogin() {
  // 预校验
  this.$refs.loginForm.validate(async valid => {
    if (valid) {
      // 开启登录按钮的loading
      this.loading = true
      try {
        // 发送请求
        await this.$store.dispatch('user/login', this.loginForm)
        console.log('请求成功')
        this.$router.push('/')
      } catch (error) {
        console.log(error)
      }
      // 无论成功还是失败都要把loading关闭
      this.loading = false
    }
  })
}

添加token的getters
为了更好的让其他模块和组件获取到token数据和其他类似公共的数据的值,我们可以把这些数据添加到getters里,便于将来访问。可能你会说为什么不都写在一个store文件里,这是为了各模块之间的独立,使代码更清晰,所以都抽离出来了,在store/index.js中统一进行了挂载。

store/getters.js

const getters = {
  sidebar: state => state.app.sidebar,
  device: state => state.app.device,
  token: state => state.user.token, // 把token提到getters中,方便调用
  name: state => state.user.userInfo.username, // 用户名字的映射
  staffPhoto: state => state.user.userInfo.staffPhoto
}
export default getters

store/index.js

import Vue from 'vue'
import Vuex from 'vuex'
import getters from './getters'
import app from './modules/app'
import settings from './modules/settings'
import user from './modules/user'
import permission from './modules/permission'

Vue.use(Vuex)

const store = new Vuex.Store({
  modules: {
    app,
    settings,
    user,
    permission
  },
  getters // 这里是把getters挂载到全局,更加方便,如果放在modules里面,则在使用的时候还要加模块名
})

export default store

vuex持久化
刚刚在登录时,已经可以成功的将token存到vuex中,但是vuex刷新会丢失,所以我们需要结合web存储实现持久化。
我们可以使用localstorage或者cookie进行存储,然后在utils文件夹中新建auth.js或者storage.js或者cookie.js,意思就是本地存储的文件,把本地存储数据的方法写进去
utils/auth.js

import Cookies from 'js-cookie'

const TokenKey = 'qm-token'
const ThemeKey = 'qm-theme'

export function getToken() {
  return Cookies.get(TokenKey)
}

export function setToken(token) {
  return Cookies.set(TokenKey, token)
}

export function removeToken() {
  return Cookies.remove(TokenKey)
}

export function getTheme() {
  return Cookies.get(ThemeKey)
}

export function setTheme(Theme) {
  return Cookies.set(ThemeKey, Theme)
}

export function removeTheme() {
  return Cookies.remove(ThemeKey)
}

store/user.js 把token信息改为getToken(),一进来先从本地拿

const state = {
  // 一进来优先从缓存中取
  token: getToken() // token字符串
}

在存token的时候,本地也存一份

const mutations = {
  // 设置token
  setToken(state, newToken) {
    state.token = newToken
    // 设置了 token 的同时, 同步到本地cookies中
    setToken(newToken)
  }
}

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

token的过期问题 你登陆成功之后,接口会返回一个token值,这个值在后续请求时带上(就像是开门钥匙)。 但是,这个值一般会有有效期(具体是多长,是由后端决定),在我们的项目中,这个有效期是2小时。 如果,上午8点登陆成功,到了10:01分,则token就会失效,再去发请求时,就会报401错误。 思考:

token需要过期时间吗 ?

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

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

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

token过期该怎么办?

token过期,就要重新获取。

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

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

目标效果 - 保证每一小时, 都是一个不同的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

1、没有 refresh_token 拦截到登录页, 清除无效的token
utils/request.js

// 添加响应拦截器
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('user/removeToken')
      router.push({
        path: '/login',
        query: {
          backto: router.currentRoute.fullPath
        }
      })
    }
  }
  return Promise.reject(error)
})

2、提供清除token的mutation
因为不仅要清空本地的token,还要清空vuex的token信息

store/user.js

  mutations: {
    setTokenFn (state, data) {
      state.tokenInfo = data
      console.log('存', data)
      setToken(data)
      const res = getToken()
      console.log('取', res)
    },
    // 移出tokenInfo的信息, 恢复成空对象
    removeToken (state) {
      state.tokenInfo = {}
      // 更新到本地, 本地可以清掉token信息
      removeToken()
    }
  },

3、如果有refresh_token,那就发送请求,刷新token
注意: 这边发请求, 不要用上面二次处理好的request实例, 用它会自动在请求前帮你携带token(会覆盖你的refresh_token),要用原生的axios
注意:刷新完token,拿到最新的token并存好之后,应该重新发送刚刚的请求,做到让用户无感知

utils/request.js

const refreshToken = store.state.user.tokenInfo.refresh_token
if (refreshToken) {
  console.log('存在refreshToken, 需要进行刷新token操作')
  // (1) 发送请求, 进行刷新token操作, 获取新的token
  // 注意: 这边发请求, 不用request实例, 用它会自动在请求前帮你携带token(会覆盖你的refresh_token)
  // 这边, 直接用 axios 发送请求
  const res = await axios({
    method: 'put',
    url: 'http://toutiao.itheima.net/v1_0/authorizations',
    // 请求头中携带refresh_token信息
    headers: {
      Authorization: `Bearer ${refreshToken}`
    }
  })
  const newToken = res.data.data.token
  // (2) 将新token更新到vuex中
  store.commit('user/setTokenInfo', {
    refresh_token: refreshToken,
    token: newToken
  })
} 

4、refresh_token也过期了
那就是真正的用户过期了,直接重复路由守卫里面的操作,清除掉本地token信息,带着目标路径前往login页面,登录完之后返回刚刚要前往的目标页面。
额,发现文章顺序写错了,这里提一下路由守卫里做了什么事,在07里面有。就是用户在进入白名单页面时,比如说首页或者其他不需要个人信息页面时,是不需要登录的,如果在没有登录的情况下,想要进入类似于个人中心,会员中心这种页面时,那么就需要让用户去登录页面登录一下,登录完成之后再返回刚刚要去的个人中心页面,这时候就是在路由跳转的时候,带上刚刚页面的路由路径,可以在登录完成之后原路返回。

// 添加请求拦截器
request.interceptors.request.use(function (config) {
  // 在发送请求之前做些什么
  const token = store.state.user.tokenInfo.token
  console.log(token)
  if (token) {
    config.headers.Authorization = `Bearer ${token}`
  }
  return config
}, function (error) {
  // 对请求错误做些什么
  console.log(error)
  return Promise.reject(error)
})

// 添加响应拦截器
request.interceptors.response.use(function (response) {
  // 对响应数据做点什么
  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.user.tokenInfo.refresh_token
    console.log(refreshToken)
    if (refreshToken) {
      try {
        console.log('存在refreshToken, 需要进行刷新token操作')
        // (1) 发送请求, 进行刷新token操作, 获取新的token
        // 注意: 这边发请求, 不用request实例, 用它会自动在请求前帮你携带token(会覆盖你的refresh_token)
        // 这边, 直接用 axios 发送请求
        const res = await axios({
          method: 'put',
          url: 'http://toutiao.itheima.net/v1_0/authorizations',
          // 请求头中携带refresh_token信息
          headers: {
            Authorization: `Bearer ${refreshToken}`
          }
        })
        const newToken = res.data.data.token
        // (2) 将新token更新到vuex中
        store.commit('user/setTokenFn', {
          refresh_token: refreshToken,
          token: newToken
        })
        console.log('>>>>>>>>>>>>>>>>>>>.', error.config)
        // 刷新token后,应该重新发送刚才的请求,让用户刷新token无感知
        return request(error.config)
      } catch (error) {
        console.log('使用refresh_token获取新token失败')
        // 清除本地token跳转路由
        store.commit('user/removeTokenInfo')
        // 路由跳转, 进入登录页
        router.push({
          path: '/login',
          query: {
            backto: router.currentRoute.fullPath
          }
        })
      }
    } else {
      // 没有refreshToken, 直接去登录, 将来还能跳回来
      // router.currentRoute 指向当前路由信息对象 === 等价于之前页面中用的 this.$route
      // 清除本地token, 跳转登录 (无意义的本地token内容, 要清除)
      store.commit('user/removeToken')
      router.push({
        path: '/login',
        query: {
          backto: router.currentRoute.fullPath
        }
      })
    }
  }
  return Promise.reject(error)
})

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/12457.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【EC200U】 基站定位

EC200U 基站定位什么是基站基站定位cellLocator - 基站定位调用获取坐标token 秘钥申请运行测试我们之前玩了GPS了【EC200U】GPS定位 GPS 精度高,但比较费电,首次搜索卫星定位时间比较长,当卫星信号覆盖不好,比如室内,…

gdb-dashboard的简单使用

问题描述 众所周知,随着IDE的普及,越来越多的人不习惯命令行调试,但是Linux开发又绕不开GDB,所以有大佬开发了程序用来显示常见的一些调试参数,gdb-dashboard是比较热门的程序之一。附链接。 使用参考 神仙GDB调试工…

Golang入门笔记(7)—— 函数 func

函数是什么? 函数:为完成某一功能的程序的语句指令的集合。 感觉重头戏逐渐到来了,让我们好好的,认真对待它吧!为什么要使用函数? 为了提高代码的复用,减少代码的冗余,提高代码维护性…

前端框架 React 学习总结

目录 一、React在HTML里的运用 二、React框架的常用操作 1、JSX基础语法规则 2、state数据的使用 3、生命周期 4、数据的双向绑定与Ref 5、PropTypes验证规则 6、React里的插槽 7、错误边界 8、直接操作refs元素 9、高阶组件的运用案例 10、性能优化 11、Hook生命…

Linux中安装mysql8

一、下载MySQL到 /usr/local/src 1、打开官网下载界面 MySQL :: Download MySQL Community Server (Archived Versions) 2、选择某个版本的下载地址 以https://downloads.mysql.com/archives/get/p/23/file/mysql-8.0.30-linux-glibc2.12-x86_64.tar.xz这个地址作为例子 …

Kubernetes 系统化学习之 持久存储篇(五)

1. ConfigMap ConfigMap 是一种 API 对象,用来将非机密性的数据保存到键值对中。使用时,Pods 可以将其用作环境变量、命令行参数或者存储卷中的配置文件。ConfigMap 的主要作用就是为了让镜像和配置文件解耦,以便实现镜像的可移植性和可复用性…

day07 Elasticsearch搜索引擎3

day07 Elasticsearch搜索引擎3 1、数据聚合 聚合(aggregations)可以让我们极其方便的实现对文档数据的统计、分析、运算。例如: 什么品牌的手机最受欢迎?这些手机的平均价格、最高价格、最低价格?这些手机每月的销售…

java设计模式之观察者模式

一:观察者模式 1.什么是观察者模式? 观察者模式是一种行为设计模式, 允许你定义一种订阅机制, 可在对象事件发生时通知多个 “观察” 该对象的其他对象。 观察者模式的基本介绍 又被称为发布-订阅(Publish/Subscribe&#xff09…

从阿里云容器攻防矩阵API安全生命周期,看如何构建金融安全云原生平台

【编者按】云原生技术正在助力银行通过差异化业务进行创新,却也带来了由于研发/运维人员对新架构不熟悉所导致的基础设施风险、业务风险及数据暴露风险。如何在飞速更迭的技术环境下保持业务持续发展,同时保证业务整体的安全性,满足不断增强的…

设计模式-装饰器模式

装饰器模式也称为包装模式是指在不改变原有对象的基础上,将功能附加到对象上,提供比继承更有弹性的替代方案(扩展原有对象的功能),属于结构型模式 装饰器模式的核心是功能扩展,使用装饰器模式可以透明且动态的扩展类的功能 装饰器模式通用UML类图 主要角…

培训学校管理系统之家校管理

近年来,伴随着信息技术的进步。除了不少学校开展了数字化校园的建设,一些培训机构也在使用数字化系统进行管理。同时,由于招生数量的不断增长,如何解决学生、教师、家长三者之间的联系沟通问题,促进教学管理任务的有效…

排序(详解)

排序排序的概念常见的排序算法直接插入排序希尔排序(缩小增量排序)选择排序堆排序冒泡排序快排(递归)霍尔版本挖坑法前后指针法快排(非递归)归并排序(递归)归并排序(非递…

区块链动态化监管方案

前言 监控运维模块是区块链BaaS的核心模块之一,我们针对联盟链、主机和系统等多个监控对象提供丰富的监控指标。通过BaaS提供的综合监控大屏,用户可直观洞悉区块链业务全局,实现7*24小时监控全覆盖。 但随着BaaS业务的扩展,对监…

jsp教师教学信息管理系统Myeclipse开发sqlserver数据库web结构java编程计算机网页项目

一、源码特点 JSP 教师教学信息管理系统 是一套完善的web设计系统,对理解JSP java编程开发语言有帮助,系统具有完整的源代码和数据库文,系统主要采用B/S模式开发。开发环境为 TOMCAT7.0,Myeclipse8.5开发,数据库为sqlserver2008…

微服务拆分总结(一)

微服务要解决的问题: 1、可以快速迭代; 2、解决三高问题(高并发,高可用,高性能) 什么时候拆分微服务,拆分的时机是什么? 提交频繁代码冲突; 模块之间耦合严重&#…

RocketMQ 重试机制详解及最佳实践

作者:斜阳 引言 本文主要介绍在使用 RocketMQ 时为什么需要重试与兜底机制,生产者与消费者触发重试的条件和具体行为,如何在 RocketMQ 中合理使用重试机制,帮助构建弹性,高可用系统的最佳实践。 RocketMQ 的重试机制…

[附源码]java毕业设计静谧空间自习室预订系统

项目运行 环境配置: Jdk1.8 Tomcat7.0 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术: SSM mybatis Maven Vue 等等组成,B/S模式 M…

服务器部署Vue2脚手架的PIXI游戏项目-知识点注意

文章目录安装PIXI框架方式一(安装)方式二(引入)javaScript代码位置initPixi方法组件挂载完毕后调用注意文件导入使用import一个个导入并命名使用setTimeout()方法使用一般方法调用表达式使用安装PIXI框架 方式一(安装…

高通量筛选检测方法-分子篇

分子水平的筛选更多的是检测酶/受体功能的改变或探针/蛋白质结合的抑制,或是检测蛋白质-配体结合的结构、动力学和亲和度。 下面将介绍了荧光偏振、荧光共振能量转移、酶联免疫吸附、表面等离子共振和核磁共振技术几种方法。 ■ 荧光偏振 荧光偏振是一项在高通量筛…

2-STM32GPIO输入之按键

文章目录1-硬件设计1.1 按键消斗1.1.1 RS触发器1.1.2 电容滤波2 按键电路设计2.1 软件消斗2.2 硬件消斗2.3 检测原理2-软件设计2.1 软件消斗2.1.1原理2.1.2 编程要点2.1.3 步骤2.2 代码编写2.2.1 主程序2.2.2 按键初始化2.2.2 按键扫描本章讲述GPIO输入的应用,使用独…