退出登录方案实现
对于退出登录而言, 触发时机一般有两种:
- 用户主动退出
主动退出指:用户点击登录按钮之后退出 - 用户被动退出
被动退出指:token
过期或被 其他人”挤下来“ 时进行强制退出
那么无论那种退出方式, 在用户退出时, 执行的操作都是固定的 :
- 清理掉当前用户缓存数据
- 清理掉权限相关配置
- 返回到登录页
主动退出
现在理清了退出方案的思路, 就用代码来实现用户主动退出
用户点击退出登录执行
logout() {
commit('SET_TOKEN', '') // 删除token
commit('SET_USERINFO', {}) // 删除用户信息
removeAllItem() // 删除本地所有
router.push('/login') // 回到登录页
}
为退出登录按钮添加点击事件,触发 logout
的 action
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 时效的处理中
。 从而保证用户信息的更加安全性。
那么对应到我们代码中的实现方案为:
- 在用户登录时, 记录当前
登录时间
- 制定一个
失效时长
- 在接口调用时, 根据
当前时间
对比登录时间
, 看是否超过了时效时长- 如果未超过, 则正常进行后续操作
- 如果超过, 测进行
退出登录
操作
实现对应代码
- 建立常量
// token 时间戳
export const TIME_STAMP = 'timeStamp'
// 超时时长(毫秒) 两小时
export const TOKEN_TIMEOUT_VALUE = 2 * 3600 * 1000
- 设置存储时间&判断
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
}
- 登录成功之后设置存储当前时间
import { setTimeStamp } from '@/utils/auth'
login(context, userInfo) {
return new Promise((resolve, reject) => {
.then(data => {
// 保存登录时间
setTimeStamp()
resolve()
})
})
},
- 在axios请求拦截器中
主动介入
// 请求拦截器
service.interceptors.request.use(
config => {
// 在这个位置需要统一的去注入token
if (store.getters.token) {
if (isCheckTimeout()) { // 判断token是否失效
// 失效 删除所有信息 回到登录
store.dispatch('user/logout')
// 抛出异常
return Promise.reject(new Error('token失效'))
}
// 如果token存在 注入token
config.headers.Authorization = `Bearer ${store.getters.token}`
}
return config // 必须返回配置
},
error => {
return Promise.reject(error)
}
)
那么至此我们就完成了 主动处理 对应的业务逻辑
被动之被动处理
被动处理有两种业务场景
-
token过期
- 我们知道对于 token 而言,本身就是具备时效的,这个是在服务端生成 token 时就已经确定的。
而此时我们所谓的 token 过期指的就是:
服务端生成的 token 超过 服务端指定时效 的过程
- 我们知道对于 token 而言,本身就是具备时效的,这个是在服务端生成 token 时就已经确定的。
单点登录
当用户 A 登录之后,token 过期之前。
用户 A 的账号在其他的设备中进行了二次登录,导致第一次登录的 A 账号被 “顶下来” 的过程。
即:同一账户仅可以在一个设备中保持在线状态
那么明确好了对应的背景之后,接下来我们来看对应的业务处理场景:
从背景中我们知道,以上的两种情况,都是在 服务端进行判断的
,而对于前端而言其实是 服务端通知前端的一个过程
。
- 服务端返回数据时,会通过特定的状态码通知前端
- 当前端接收到特定状态码时,表示遇到了特定状态:
token 时效
或单点登录
- 此时进行
退出登录
处理
假设此时单点登录 & token失效 为同一错误状态码为401
实现
// 响应拦截器
service.interceptors.response.use(
// 请求成功
(response) => {
const { success, message, data } = response.data;
// 需要判断当前请求是否成功;
if (success) {
// 成功返回解析后的数据
return data;
} else {
// 失败(请求成功, 业务失败), 消息提示
ElMessage.error(message);
return Promise.reject(new Error(message));
}
},
(error) => {
if (error.response?.data?.code === 401) {
// token超时
store.dispatch('user/logout')
}
ElMessage.error(error.message); // 提示错误信息
return Promise.reject(error);
}
);