Appearance
退出方案解析
退出登录 一直是一个通用的前端实现方案,对于退出登录而言,它的触发时机一般有两种:
- 用户主动退出
 - 用户被动退出
 
其中:
- 主动退出指:用户点击登录按钮之后退出
 - 被动退出指:
token过期或被 其他人”顶下来“ 时退出 
那么无论是什么退出方式,在用户退出时,所需要执行的操作都是固定的:
- 清理掉当前用户缓存数据
 - 清理掉权限相关配置
 - 返回到登录页
 
那么明确好了对应的方案之后,接下来咱们就先来实现 用户主动退出的对应策略
在 store/modules/user.js 中,添加对应 action
js
import router from '@/router'
logout() {
    this.commit('user/setToken', '')
    this.commit('user/setUserInfo', {})
    removeAllItem()
    router.push('/login')
}
为退出登录按钮添加点击事件,触发 logout 的 action
js
import { useStore } from 'vuex'
const store = useStore()
const logout = () => {
  store.dispatch('user/logout')
}
那么至此,我们就完成了 用户主动退出 对应的实现。
用户被动退出方案解析
在上一节我们实现了 用户主动退出 场景,同时也提到 用户被动退出 的场景主要有两个:
token失效- 单用户登录:其他人登录该账号被 “顶下来”
 
那么这两种场景下,在前端对应的处理方案一共也分为两种,共分为 主动处理 、被动处理 两种 :
- 主动处理:主要应对 
token失效 - 被动处理:同时应对 
token失效 与 单用户登录 
那么这两种方案基本上就覆盖了用户被动推出时的主要业务场景了
那么这一小节内容比较少,但是东西还是挺重要的。因为我们主要分析了 用户被动退出 的场景,那么从下一小节开始,我们分别来实现这两种处理方案。
用户被动退出解决方案之主动处理
想要搞明白 主动处理 方案,那么首先我们得先去搞明白对应的 背景 以及 业务逻辑 。
那么首先我们先明确一下对应的 背景:
我们知道
token表示了一个用户的身份令牌,对 服务端 而言,它是只认令牌不认人的。所以说一旦其他人获取到了你的token,那么就可以伪装成你,来获取对应的敏感数据。所以为了保证用户的信息安全,那么对于
token而言就被制定了很多的安全策略,比如:
- 动态
 token(可变token)- 刷新
 token- 时效
 token- ...
 这些方案各有利弊,没有绝对的完美的策略。
而我们此时所选择的方案就是 时效 token
对于 token 本身是拥有时效的,这个大家都知道。但是通常情况下,这个时效都是在服务端进行处理。而此时我们要在 服务端处理 token 时效的同时,在前端主动介入 token 时效的处理中。 从而保证用户信息的更加安全性。
那么对应到我们代码中的实现方案为:
- 在用户登陆时,记录当前 登录时间
 - 制定一个 失效时长
 - 在接口调用时,根据 当前时间 对比 登录时间 ,看是否超过了 时效时长
- 如果未超过,则正常进行后续操作
 - 如果超过,则进行 退出登录 操作
 
 
那么明确好了对应的方案之后,接下来我们就去实现对应代码
创建 utils/auth.js 文件,并写入以下代码:
js
import { TIME_STAMP, TOKEN_TIMEOUT_VALUE } from '@/constant'
import { setItem, getItem } from '@/utils/storage'
/**
 * 获取时间戳
 */
export function getTimeStamp() {
  return getItem(TIME_STAMP)
}
/**
 * 设置时间戳
 */
export function setTimeStamp() {
  setItem(TIME_STAMP, Date.now())
}
/**
 * 是否超时
 */
export function isCheckTimeout() {
  // 当前时间戳
  var currentTime = Date.now()
  // 缓存时间戳
  var timeStamp = getTimeStamp()
  return currentTime - timeStamp > TOKEN_TIMEOUT_VALUE
}
在 constant 中声明对应常量:
js
// token 时间戳
export const TIME_STAMP = 'timeStamp'
// 超时时长(毫秒) 两小时
export const TOKEN_TIMEOUT_VALUE = 2 * 3600 * 1000
在用户登录成功之后去设置时间,到 store/user.js 的 login 中:
js
import { setTimeStamp } from '@/utils/auth'
login(context, userInfo) {
      ...
      return new Promise((resolve, reject) => {
        ...
          .then(data => {
            ...
            // 保存登录时间
            setTimeStamp()
            resolve()
          })
      })
    },
在 utils/request 对应的请求拦截器中进行 主动介入
js
import { isCheckTimeout } from '@/utils/auth'
if (store.getters.token) {
      if (isCheckTimeout()) {
        // 登出操作
        store.dispatch('user/logout')
        return Promise.reject(new Error('token 失效'))
      }
      ...
    }
那么至此我们就完成了 主动处理 对应的业务逻辑
用户被动退出解决方案之被动处理
上一节我们处理了 用户被动退出时的主动处理 ,那么在这一小节我们去处理 用户被动退出时的被动处理 。
还是和上一小节一样,我们还是先明确背景,然后再来明确业务逻辑。
背景:
首先我们需要先明确 被动处理 需要应对两种业务场景:
token过期- 单用户登录
 
然后我们一个一个来去看,首先是 token 过期
我们知道对于
token而言,本身就是具备时效的,这个是在服务端生成token时就已经确定的。而此时我们所谓的
token过期指的就是:服务端生成的
token超过 服务端指定时效 的过程
而对于 单用户登录 而言,指的是:
当用户 A 登录之后,
token过期之前。用户 A 的账号在其他的设备中进行了二次登录,导致第一次登录的 A 账号被 “顶下来” 的过程。
即:同一账户仅可以在一个设备中保持在线状态
那么明确好了对应的背景之后,接下来我们来看对应的业务处理场景:
从背景中我们知道,以上的两种情况,都是在 服务端进行判断的,而对于前端而言其实是 服务端通知前端的一个过程。
所以说对于其业务处理,将遵循以下逻辑:
- 服务端返回数据时,会通过特定的状态码通知前端
 - 当前端接收到特定状态码时,表示遇到了特定状态:
token时效 或 单用户登录 - 此时进行 退出登录 处理
 
那么明确好了业务之后,接下来我们来实现对应代码:
在 utils/request 的响应拦截器中,增加以下逻辑:
js
// 响应拦截器
service.interceptors.response.use(
  response => {
    ...
  },
  error => {
    // 处理 token 超时问题
    if (
      error.response &&
      error.response.data &&
      error.response.data.code === 401
    ) {
      // token超时
      store.dispatch('user/logout')
    }
    ElMessage.error(error.message) // 提示错误信息
    return Promise.reject(error)
  }
)
那么至此,我们就已经完成了 整个用户退出 方案。