(一)将 token 存储到 cookie 中
核心实现方式: 使用 js-cookie 插件将 token 存储到 cookie 中
1、通过 js-cookie 中的 getToken() 获取存储在 cookie 中的 token
(-- @/store/modules/user.js;state 配置项)
token: getToken()
2、通过 js-cookie 中的 setToken() 将 token 存储到 cookie 中 && 将 token 存储到 state 中
(-- 同上;mutations 配置项)
setToken(state, token) {
state.token = token
setToken(token)
}
(-- 同上;actions 配置项)
async login(context, data) {
const result = await login(data)
context.commit('setToken', result)
}
(-- @/views/login/index.vue)
import { mapActions } from 'vuex'
methods() {
...mapActions(['user/login'])
}
async login() { // 登录
-- 增
await this.$store.dispatch('user/login', this.loginForm)
--
this.$router.push({
name: 'Dashboard'
})
}
(二)每次发送请求时携带 token
核心实现方式: 在请求拦截器中的请求头中携带 token
(-- @/utils/request.js)
service.interceptors.request.use(config => { // 请求拦截器
-- 增
if (store.getters.token) { // 在请求头中携带 token
config.headers['Authorization'] = `${store.getters.token}`
}
--
return config
})
(三)token 过期时的处理
前端实现
核心实现方式: 获取 token 时,将当前时间戳一起存储到缓存中。每当发起请求时,判断(当前时间戳 - 缓存中的时间戳)/ 1000 如果大于过期时间,表示 token 过期,清空 token,跳转到登录页,抛出时间戳过期提示
1、定义时间戳的 key && 定义获取时间戳的方法 && 定义设置时间戳的方法
(-- @/utils/auth.js)
const timeKey = 'hrsaas-timestamp-key' // 时间戳
export function getTimeStamp() { // 获取 时间戳
return Cookies.get(timeKey)
}
export function setTimeStamp() { // 设置 时间戳
Cookies.set(timeKey, Date.now())
}
2、获取 token 的时候将当前时间戳存到缓存中
(-- @/views/login/index.vue)
-- 增
import { setTimeStamp } from '@/utils/auth'
--
async login(content, data) { // 登录
...
context.commit('setToken', result)
-- 增
setTimeStamp()
--
}
3、每次请求时,调用 判断 token 是否过期的方法,如果过期则清空 token,跳转到登录页,并抛出错误
(-- @/utils/request.js)
-- 增
import { getTimeStamp } from '@/utils/auto'
import router from '@/router'
const TimeOut = 3600 // token 过期时间。3600 毫秒 = 一小时
function IsCheckTimeOut() { // 判断 token 是否过期
var currentTime = Date.now() // 当前时间戳
var timeStamp = getTimeStamp() // 缓存时间戳
return (currentTime - timeStamp) / 1000 > TimeOut // 判断 token 是否过期 // 公式:【(当前时间戳 - 缓存中的时间戳)/ 1000 如果大于过期时间,表示 token 过期】。这里的除 1000 是将毫秒转化成秒。
}
--
// 请求拦截器
if (store.getters.token) {
-- 增
if (IsCheckTimeOut()) { // 判断 token 是否过期
store.dispatch('user/logout') // 调用登出方法
router.push('/login')
return Promise.reject(new Error('token 超时了'))
}
--
}
后端实现(推荐)
核心实现方式: 在响应拦截器中判断后端响应的状态码,如果是 401 则表示 token 过期,然后清空 token、和 vuex 中的 token 即可
(-- @/utils/request.js)
-- 增
import store from '@/store'
import router from '@/router'
--
// 响应拦截器
service.interceptors.response.use(response => {
...
}, error => {
-- 增
if (error.response && error.response.data && error.response.data.code === 10002) { // 判断 token 是否过期
// 调用登出方法
store.dispatch('user/logout')
router.push('/login')
} else {
-- 改(新)
Message.error(error.message) // 提示错误信息
--
}
return Promise.reject(error)
--
-- 改(旧)
Message.error(error.message) // 提示错误信息
})
(四)退出功能
核心实现方式: 1、清空 state 中的 token 2、清空 cookie 中的 token 3、清空 用户信息
(五)设置路由白名单和有 token 的情况下访问登录页直接跳转到首页
核心实现方式: 导航守卫。在前置守卫中(判断是否有 token(如果有 token 在通过前置守卫的第一个参数判断是否访问的是 登录页,如果是登录页则调用前置守卫的第三个参数跳转到首页))
(-- @/新建 permission.js)
import router from '@/router'
import nprogress from 'nprogress'
import 'nprogress/nprogress.css' // 引入进度条样式
import store from '@/store'
const whiteList = ['/login', '/404', '/demo'] // 白名单
router.beforeEach((to, from, next) => { // 前置守卫 // (参数一:将要 访问 路由的信息对象)(参数二:将要 离开 路由的信息对象)(参数三:是一个函数,表示放行,允许这次路由导航)
nprogress.start() // 开启进度条
if (store.getters.token) { // 判断 是否有 token
if (to.path === '/login') { // 判断 是否前往 登录页
next('/')
} else {
next()
}
} else {
if (whiteList.includes(to.path)) { // 判断 前往的地址是否在白名单中
next()
} else {
next('/login')
}
}
nprogress.done() // 解决手动切换地址时,进度条不关闭的问题
})
router.afterEach(() => { // 后置守卫
nprogress.done() // 关闭进度条
})