前端判断Token是否失效,并更新Token

用户登录系统后,都会赋予用户一个Token,这样用户在操作系统时,只需要将Token传给后端就可以了,无需对用户重复进行身份验证

既然Token如此重要,为了安全起见,Token的有效期也不会设置过长。

那么将由前后端谁来判断Token是否失效呢?只能说各有优劣

前后端优缺点

// 简单说一下主要几点
// 前端
优点:体验好,减少服务端的负担;
缺点:不安全,依赖本地时间,可以被伪造或篡改,容易被攻击者利用;
// 后端
优点:安全性高,只有服务端知道Token是否失效,可以防止客户端伪造或篡改信息;还能简化前端处理逻辑;
缺点:增加了服务端的负担,每次请求都需要进行验证,可能对性能有影响

前端处理逻辑

本期主要重点讲 前端是如何处理Token失效逻辑的。

为了不频繁更新Token,前端应该设置一个更新时间失效时间

分析

场景和逻辑很简单:

  • 1、用户登录系统,后端返回前端Token有效期时间(3天);
    那么更新时间设为2天半后失效时间设为3天后

  • 2、用户在3天后访问系统判断其Token失效,去往登录页重新登录

  • 3、用户在2天半前访问系统,则不需要特殊处理,使用本地储存的Token就可以了;
    更新时间不变,但是失效时间应该改为 当前时间 + 3天

  • 4、用户在2天半 — 3天这个时间段访问系统,则需要请求后端返回新Token
    更新时间和失效时间 则需要执行步骤1的逻辑

代码逻辑

主要有两处地方需要添加逻辑:
1、项目中 pinia/vuex 状态管理库,需要对时间进行储存和判断
2、接口请求时,公共api层是否需要更新token

pinia状态管理

// 不同项目,不同开发习惯,目录结构都不一样; 这里主要凸显出这是一个状态管理库文件
// store/modules/global.js 
// 使用pinia储存: 更新时间、失效时间、Token

import { defineStore } from 'pinia'

export const glStore = defineStore('glStore', {
  persist: {
    storage: {
      setItem(key, value) {
        uni.setStorageSync(key, value)
      },
      getItem(key) {
        return uni.getStorageSync(key)
      }
    }
  },
  state: () => {
    return {
      token: '',
      userInfo: {},
      // 失效时间
      expirationTime: 0,
      // 更新时间
      updateTime: 0
    }
  },

  actions: {
    setToken(value) {
      this.token = value
    },
    setUserInfo(userInfo) {
      if (userInfo.token) {
        this.setToken(userInfo.token)
        delete userInfo.token
      }
      this.userInfo = userInfo
    },
    getDate() {
      // 3天
      const threshold = 3 * 24 * 60 * 60 * 1000
      // 失效前半天
      const interval = 12 * 60 * 60 * 1000
			
      const now = new Date()
      // 当前时间
      const currentTime = now.getTime()
			
      // 失效时间
      const failure = currentTime + threshold
			
      // 更新时间
      const update = threshold + currentTime - interval
			
      console.log(update, this.formatTime(update), '--------2天半后')
      console.log(failure, this.formatTime(failure), '--------3天后')
      console.log(currentTime, this.formatTime(currentTime), '--------当前时间')
			
      // 1:更新Token;
      // 2:更新失效期;
      // 3:Token失效了;
      // 4:未知
      if (!this.expirationTime) {
        this.expirationTime = failure
        this.updateTime = update
        return '1'
      } else {
        // 当前时间 小于 更新时间
        if (currentTime < this.updateTime) {
          // 更新失效时间为  最新当前时间的3天后
          this.expirationTime = failure
          return '2'
        } else if (currentTime > this.updateTime && currentTime > this.expirationTime) {
          // 更新Token--- 重复最开始操作
          this.expirationTime = failure
          this.updateTime = update
          return '1'
        } else if (currentTime > this.updateTime) {
          // 当前时间 大于  失效时间 == Token失效
          return '3'
        } else {
          return '4'
        }
      }
    },
    formatTime(timestamp) {
      const date = new Date(timestamp)
      const formattedDate = date.toLocaleString()
			
      return formattedDate
    }
  }
})

API 请求

// 接口域名
import { host } from '$api/baseUrl.js'
// 设置请求头
import { setHeader } from './sign'
// pinia
import { glStore } from '@/store/modules/global.js'
// 请求后拦截
function responseFn(res, resolve, reject) {
  if (res.code === 0) {
    resolve(res)
  } else if (res.code === 401300) {
    // token过期
    uni.showModal({
      // 弹出提示框
      title: '重新登录',
      content: '登录过期,请重新登录!',
      success(res) {
        if (res.confirm) {
          // 用户点击确定按钮
          uni.navigateTo({ url: '/pages/login/index' })
        }
      },
      fail(e) {
        console.log(e, '确认取消弹出未弹出')
      }
    })
  } else {
    uni.showToast({
      title: res.msg,
      icon: 'none',
      duration: 1000
    })
    reject(res)
  }
}

async function fetch(url, data = {}, method = 'POST') {
  const store = glStore()
  let type = store.getDate()
  // 不需要Token的接口
  let urls = ['xxx/code', 'xxx/login']
	
  if (type === '1' && !urls.includes(url)) {
    let res = await $ajax('url---token', data, method)
    store.setUserInfo(res.data)
  }
  return $ajax(url, data, method)
}

async function $ajax(url, data = {}, method = 'POST') {
  return new Promise((resolve, reject) => {
    uni.request({
      url: host + url,
      header: setHeader({
        url,
        method,
        data: data || {}
      }),
      data,
      method,
      success(res) {
        responseFn(res.data, resolve, reject)
      },
      fail: function(err) {
        uni.hideLoading()
        reject(err)
      }
    })
  })
}

const $get = (url = '', data = {}) => {
  return fetch(
    url,
    data,
    'get'
  )
}

const $post = (url = '', data = {}) => {
  return fetch(
    url,
    data
  )
}

const $delete = (url = '', data = {}) => {
  return fetch(
    url,
    data,
    'delete'
  )
}

const $put = (url = '', data = {}) => {
  return fetch(
    url,
    data,
    'put'
  )
}

export { $get, $post, $delete, $put }

你可能感兴趣的:(Token,vue)