用户的需求:
1. 页面多开的情况下,登录状态必须一致
2. 当处于登录状态时,但页面处于登录页,要求刷新之后从login跳转到首页,并且登陆状态不允许直接跳转到login页面
3. 当token无效时,调用任意接口直接退出登录
4. 页面关闭自动退出登录
思路:
1. 由于第一点,所以必须使用localstorage 如果使用sessionStorage或者cookie无法保证新开的页面也存储了token参考(sessionStorage在不同页签中的数据是否共享问题及解决思路_sessionstorage 不同标签_道长道长IOT的博客-CSDN博客)
2. 使用路由守卫
- 首先判断是否有token,若没有直接跳转到页面
- 其次判断token是否有效,若有效,且访问的是/ 或/login 则next到首页
- 若token无效则跳转到/login页(此时清空掉现有的无效token最好,不清除也可以)
router.beforeEach(async (to, from, next) => { // 存储上个地址 用于返回按钮(直接使用back 会导致在新标签页打开时返回到新建标签页) store.commit('control/setPreRoute', from) if (localStorage.getItem('token')) { if (!store.state.user.menu.length) { // 解决首次刷新路由没有添加的问题 // token是否有效 try { await store.dispatch('user/fetchUserProfile') //token有效 // 获取到路由之后 才添加 await store.dispatch('user/fetchMenu') if (['/','/login'].includes(to.path)) { next('/dashboard') } else { next({ ...to, reolace: true }) } } catch (e) { // token无效 ElMessage({ message: e, type: "error", }); next('/login') } } else { next() } } else { next() } })
3. 在axios响应拦截器中,配置如果响应401且当前不在login页 就调用登出方法(跳到login并清除token)
-
instance.interceptors.response.use((res) => { let { status, data,headers } = res if (status >= 200 && status < 300) { if(headers['x-pagination']){ return { data, pagination:JSON.parse(headers['x-pagination']) } }else{ return data; } } else { return Promise.reject(data); } }, err => { if((err.response.status === 401) && ( router.currentRoute.value.name !='login')){ ElMessage({ message: '登录过期,请重新登陆', type: 'warning' }) store.commit('user/logout') } return Promise.reject(err); });
4. 由于是使用localStorage,在浏览器关闭页面后存储在localStorage中的token并不会消失,所以一开始想到了在App销毁时清除localStorage,但此时会造成已登录的用户在刷新页面后跳转到登录页面的问题,所以不能直接采用这种方法,需要前端想办法区分用户的关闭和刷新操作。
理解:当用户直接关闭浏览器时 beforeunload 与unload时间间隔是很短的,但如果刷新两者时间间隔会很长(尤其是在beforeunload 加了以下代码后:会唤醒一个弹窗)
e.preventDefault();
e.returnValue = "";
到此,上述的四个要求基本满足,但仍存在一个问题,当我直接在浏览器地址栏输入其它非自己项目的地址(如 百度...之类的),此时再跳转回来,仍然是已登录状态,与用户希望离开此页面就取消登录相违背。希望能收到大家的解决方法。