前端实现token的无感刷新#记录

因为服务器的token一版不会设置太长,token过期后就需要重新登录,频繁的登录会造成体验不好的问题,因此,需要体验好的话,就需要定时去刷新token,并替换之前的token。以下是token失效的效果:

那么做到token的无感刷新,主要有3种方案:

方案一:

后端返回过期时间,前端每次请求就判断token的过期时间,快到过期时间,就去调用刷新token接口。

缺点:若本地时间被篡改,特别是本地时间比服务器时间慢时,拦截会失败,不建议采用。

方案二:

后端返回过期时间,前端写个定时器,然后定时刷新token接口。

缺点:浪费资源,消耗性能,不建议采用。

方案三: 

在请求响应拦截器中拦截,判断token 返回过期后,调用刷新token接口,推荐使用。

那么以下是方案三的实现步骤:

1、首次登录的时候会获取到两个token,把获取到的两个token存到前端缓存中。


   
   
  1. localStorage. setItem( 'refreshToken',xxx)
  2. localStorage. setItem( 'token', xxx)

2、token刷新基础写法


   
   
  1. import axios from "axios"
  2. let flag = false // 设置开关,防止多次请求
  3. let expiredList = [] //存储过期的请求
  4. // 把过期请求添加在数组中
  5. function addRequest( request) {
  6. expiredList. push(request)
  7. }
  8. // 创建axios实例
  9. const service = axios. create({
  10. baseURL: 'http://xxxxxxxxx', //后台请求接口路径
  11. timeout: 10000 // 请求超时时间
  12. })
  13. service. interceptors. request. use(
  14. config => {
  15. localStorage. getItem( 'token') && (config. headers. Authorization = localStorage. getItem( 'token'))
  16. config. headers[ 'Content-Type'] = config. headers[ 'Content-Type'] || 'application/json;charset=utf-8'
  17. return config
  18. },
  19. error => {
  20. Promise. reject(error)
  21. }
  22. )
  23. service. interceptors. response. use( res => {
  24. const code = res. data. code
  25. if (code === 70001 || code === 401) { //与后端约定相应的code以此判断是否过期
  26. // 把过期请求存储起来,用于请求到新的刷新token
  27. addRequest( () => resolve( http(config)))
  28. // 用刷新token去请求新的主token
  29. if (!flag) {
  30. flag = true;
  31. // 获取刷新token
  32. let refreshToken = localStorage. getItem( 'refreshToken');
  33. if (refreshToken) {
  34. // 判断刷新token是否过期, getRefreshToken这个是你封装的请求刷新token的接口
  35. getRefreshToken(refreshToken). then( res => {
  36. if (判断刷新token失效,退出登录) {
  37. flag = false
  38. // 移除刷新token
  39. localStorage. removeItem( 'refreshToken')
  40. localStorage. removeItem( 'token')
  41. location. href = '/' //跳转到你指定的页面
  42. } else if (res. code === 1000) {
  43. flag = false
  44. // 重新发送请求
  45. expiredList. forEach( (request) => request())
  46. expiredList = []
  47. }
  48. })
  49. }
  50. }
  51. } else if(code === 200){
  52. return res. data
  53. }
  54. },
  55. error => {
  56. return Promise. reject(error)
  57. }
  58. )
  59. export default service

3、token刷新进阶写法(包含防止多次刷新token、同时发起两个或者两个以上的请求时刷新token)


   
   
  1. import axios from 'axios'
  2. // 是否正在刷新的标记
  3. let isRefreshing = false
  4. //存储过期的请求
  5. let expiredList = []
  6. // 创建axios实例
  7. const service = axios. create({
  8. baseURL: 'http://xxxxxxxxx', //后台请求接口路径
  9. timeout: 10000 // 请求超时时间
  10. })
  11. service. interceptors. request. use(
  12. config => {
  13. localStorage. getItem( 'token') && (config. headers. Authorization = localStorage. getItem( 'token'))
  14. config. headers[ 'Content-Type'] = config. headers[ 'Content-Type'] || 'application/json;charset=utf-8'
  15. return config
  16. },
  17. error => {
  18. Promise. reject(error)
  19. }
  20. )
  21. service. interceptors. response. use(
  22. res => {
  23. //约定code 401 token 过期
  24. if (res. data. code === 401) {
  25. if (!isRefreshing) {
  26. isRefreshing = true
  27. //调用刷新token的接口
  28. return refreshToken({ refreshToken: localStorage. getItem( 'refreshToken'), token: localStorage. getItem( 'token') }). then( res => {
  29. const { token } = res. data
  30. // 替换token
  31. localStorage. setItem( 'token', token)
  32. res. headers. Authorization = `${token}`
  33. // token 刷新后将数组的方法重新执行
  34. expiredList. forEach( (cb) => cb(token))
  35. expiredList = [] // 重新请求完清空
  36. return service(res. config)
  37. }). catch( err => {
  38. localStorage. removeItem( 'refreshToken')
  39. localStorage. removeItem( 'token')
  40. location. href = '/' //跳转到你指定的页面
  41. return Promise. reject(err)
  42. }). finally( () => {
  43. isRefreshing = false
  44. })
  45. } else {
  46. // 返回未执行 resolve 的 Promise
  47. return new Promise( resolve => {
  48. // 用函数形式将 resolve 存入,等待刷新后再执行
  49. expiredList. push( token => {
  50. res. headers. Authorization = `${token}`
  51. resolve( service(res. config))
  52. })
  53. })
  54. }
  55. }
  56. return res && res. data
  57. },
  58. (error) => {
  59. return Promise. reject(error)
  60. }
  61. )

4、上述写法是方便在此文章中理解无感刷新token的代码,其中储存token到本地缓存中的代码可以提取出来,然后在使用到的地方导入使用即可。


   
   
  1. 例子:把需要的方法提取出来
  2. export function getRefreshTokenn( ) {
  3. return localStorage. getItem(refreshToken)
  4. }
  5. export function setRefreshToken( token) {
  6. return localStorage. setItem( 'refreshToken',token)
  7. }
  8. export function removeRefreshToken( ) {
  9. return localStorage. removeItem( 'refreshToken')
  10. }
  11. 使用方法:
  12. import { getRefreshTokenn, setRefreshToken, removeRefreshToken } from '@/utils/auth'
  13. localStorage. removeItem( 'refreshToken') 这段代码替换成 removeRefreshToken()

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这道题涉及到前端中的认证和授权问题,以及如何在前端中处理 token 刷新的问题。 认证和授权是指用户在访问系统资源时需要进行身份验证和权限验证。前端通常会在用户登录后返回一个 token,然后在每次请求时将 token 带上,服务端会通过 token 来判断用户是否有权限访问资源。 在使用 token 进行认证和授权时,由于 token 有一定的有效期限制,因此需要在 token 过期前进行刷新。在前端中可以通过定时器来定时检查 token 的有效期,当 token 即将过期时,发送一个刷新 token 的请求,获取新的 token,然后将新的 token 存储在本地,同时更新请求头中的 token。 以下是一个示例代码: ```javascript // 定义定时器,每隔一段时间检查 token 是否即将过期 let timer = setInterval(() => { let token = localStorage.getItem('token') let expiredTime = localStorage.getItem('expiredTime') if (new Date().getTime() > expiredTime - 60000) { // token 即将过期 refreshToken(token) } }, 1000) // 刷新 token 的函数 function refreshToken(token) { // 发送请求获取新的 token axios.post('/refreshToken', {token: token}) .then(res => { // 更新本地存储的 token 和过期时间 localStorage.setItem('token', res.data.token) localStorage.setItem('expiredTime', new Date().getTime() + res.data.expiresIn * 1000) // 更新请求头中的 token axios.defaults.headers.common['Authorization'] = 'Bearer ' + res.data.token }) } ``` 需要注意的是,当用户退出登录时,需要及时清空本地存储的 token 和过期时间,并停止定时器。 ```javascript function logout() { localStorage.removeItem('token') localStorage.removeItem('expiredTime') clearInterval(timer) } ``` 总之,前端刷新 token 的主要思路就是定时检查 token 的有效期,当即将过期时发送一个刷新 token 的请求,获取新的 token,然后更新本地存储的 token 和过期时间,并更新请求头中的 token
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值