简述
对于有些需要权限的页面,需要登录才能访问。登录判断,不同需求不同做法。最常见的一种就是重定向登录页
。这种比较简单,可以使用路由守卫,对登录状态做判断,无登录则重定向到登录页。另一种相对于重定向页面的效果,用户体验感会更好,就是登录弹窗
。自己在项目中也是使用了登录弹窗的方式。下面介绍不同需求不同的做法
路由拦截
通过路由守卫,在路由解析之前,对当前登录状态做判断。
-
beforeEach
该方法有三个参数,分别:to、from、next 1. to:要跳转的路由对象(Object) 2. from:离开的路由对象(Object) 3. next:路由通过的方法(function)
/*判断to路由是否需要权限,是否需要登录
一般需要登录才能访问的路由,可以在该路由的meta对象添加判断
如:
*/
{
name:"layout",
components:()=>"./views/layout.vue",
meta:{
needLogin:true
}
}
router.beforeEach((to,from,next) => {
if(to.meta.needLogin){
//在这里可以进行一些登录提示
next(false)
}else{
next()
}
})
//以下处理情况
//1、没有登录,则跳转到之前的跳转前的页面
router.beforeEach((to,from,next) => {
if(to.meta.needLogin){
next(form.name)
}else{
next()
}
})
/*2、没有登录,则弹出登录框
*编写登录弹窗组卷login.vue
*触发条件,本次例子的触发变量设置在store,showLogin:false(默认为false)
*/
router.beforeEach((to,from,next) => {
if(to.meta.needLogin){
store.showLogin = true
next(false)
}else{
next()
}
})
对于需要打开新窗口页面,访问需要权限的页面,比较普遍的是调用
const routeUrl = router.resolve({
name: 'layout',
params: { id: 123123}
});
window.open(routeUrl.href, '_blank');
对于这种情况,如果使用以上方法进行拦截,是会出现空白页。因为该操作是执行了
window.open
才对路由进行解析拦截,所以vue组件并未创建,就会出现空白页。那对于这种情况,该如何拦截呢
?
解决:不在局部方法里面调用window.open,而是在路由解析时beforeEach进行判断调用,
/*
*路由参数判断该页面是否需要新窗口弹出,open:true,
*设置一个变量控制open的值,因为重新打开一个新窗口,Vue对象重新创建,所有变量为变成初始值,
*可通过
*/
{
name:"layout",
components:()=>"./views/layout.vue",
meta:{
needLogin:true,
open:true
}
}
//路由跳转
const goLayout = () => {
sessionStorage.setItem('open',true)//用于触发新页面跳转判断,如果没有一个全局变量作判断,容易进入si循环
router.push({
name: "layout",
params: { id: 123123}
})
}
//路由拦截
router.beforeEach((to,from,next) => {
if(to.meta.needLogin){
if("已登录"){
if(sessionStorage.getItem('open')=='true'){
sessionStorage.setItem('open',false)
if(to.meta.open){
window.open(to.href, "_blank");
next(false)
}else{
next()
}
}else{
next()
}else{//没有登录
store.showLogin = true
next(false)
}
})
Api拦截+Token刷新
如果接口需要带
token
,可以在封装好的axios,进行登录判断,如果接口返回状态码为401,则弹出登录弹窗
因为平时开发vue项目占多,所以本次例子也是以vue为基础,使用axios。
axios封装,一般使用请求拦截器interceptors.request.use
,响应拦截器interceptors.response.use
//创建axios
const service = axios.create({
baseURL: import.meta.env.VITE_APP_API_URL,
timeout: 20000,
});
/*
*因为主要对接口返回的状态进行逻辑判断,所以此处主要对响应拦截器的进行详细的逻辑操作,
*/
//http request 拦截器
service.interceptors.request.use(
(config) => {
if (config['needAuthenticated']) {//需要token校验的接口,接口添加needAuthenticated(boolean类型)
config.headers = {
"Content-Type": "application/json",
"Authorization": token.value||localStorage.getItem('zhenti_au'),
};
}
)
//http request 拦截器
service.interceptors.request.use(
(resp)=>{
//接口请求成功逻辑操作,如状态码200
},
async (error) => {//请求失败逻辑操作,如状态码500,401,400
/*
该处只要简述状态码为401时的操作,因为接口返回status=401,代表无权限访问
需要携带正确有效的userToken
一般开发中,需要两个token值,一个请求权限接口需要携带的userToken,
一个用来更新userToken的refreshToken。
refreshToken的作用是提高用户体验感,减少登录次数。
场景:当用户在已登录的状态操作权限请求的时候,userToken过期,会提示用户进行登录,并且跳转到登录页,
这种用户体验感差,所以这时需要使用refreshToken去请求更新userToken重新发送请求。
只有当userToken和refreshToken同时过期失效才提示用户进行登录
*/
const {user} = useStore()//获取store的用户user对象,user对象里面需要有个属性记录用户登录状态
const status = error.response.status;//请求带权限的接口
if(status === 401) {//userToken过期
const refresh_token = user.refresh_token;
if(refresh_token){//存在refresh_token
try{
const res = await refInstance({
url: "",
method: "POST",
data:{
refreshToken:refresh_token
}
});
if(res.data.code){//请求更新userToken成功
user.setToken(res.data.data.token)
user.setRefreshToken(res.data.data.refresh_token)
if(error.config.data){
//这一步很关键,数据转化(具体看接口参数请求要求)
error.config.data=JSON.parse(error.config.data)
}
return service(error.config);//重新发送请求
}else{//请求更新userToken失败、则调到首页,清空用户数据,弹出登录弹窗
router.push({ name: 'layout'});
/*
status = 1 用户未登录
status = 0 用户已登录
在登录弹窗组件监听store的用户变量status属性,为1则显示登录弹窗
*/
user.login.status = 1
user.clean()//清空用户数据
}
}catch(err){//存在refresh_token,但是以过期,则调到首页,清空用户数据,弹出登录弹窗
router.push({ name: 'layout'});
user.login.status = 1
user.clean()//清空用户数据
}
}else{//不存在refresh_token,如主动退出登录
user.login.status = 1
}
}
)
总结
以上是简述开发当中常用的全局登录操作,如有更好的补充和方法,欢迎留言指点💖💖💖