37 路由拦截和白名单 - udo-bit/naive_admin_pro GitHub Wiki
- 完成未授权路由拦截
- 完成授权路由的白名单开发
上节课我们已经完成了登录部分的业务逻辑的开发,接下来我们需要实现的就是,当我们没有登录的情况下,我们需要控制我们的路由只能跳转我们的白名单页面,其他的页面一律自动跳转至登录页,然后在我们登录成功后,我针对性的跳转到对应的重定向地址部分。接下来我们一起来实现一下这一部分。 打开项目在routes下创建一个router-guard.ts的文件。 然后实现基础的路由守卫部分的逻辑如下:
import router from '~/routes/index'
router.beforeEach(async (to, from, next) => {
next()
})
然后我们在main.ts中导入使用:
import router from '~/routes'
import '~/routes/router-guard'
确保我们权限部分的路由在导入router的下方引用。 接下来我们先实现一下我们的路由拦截部分,如下:
import router from '~/routes/index'
export const allowRoutes = ['/404', '/401', '/500', '/403', '/error']
export const loginRoute = '/login'
export const allowRouteHasLoign = [...allowRoutes, loginRoute]
router.beforeEach(async (to, from, next) => {
/**
* 1. 判断项目中是否存在token
* 2. 如果token不存在且当前的路由不是白名单中的路由,那么就跳转到登录页面
* 3. 如果token存在,那么就需要判断用户信息是否存在
* 4. 如果用户信息不存在,那么就需要获取用户信息
*/
const token = useAuthorization()
const userStore = useUserStore()
if (!token.value) {
if (!allowRouteHasLoign.includes(to.path)) {
next({
path: '/login',
query: {
redirect: to.path,
},
})
return
}
}
else {
if (!userStore.userInfo) {
// 如果用户信息不存在,那么就需要获取用户信息
}
}
next()
})
在获取用户信息部分,我们需要先完成我们获取用户信息的接口如下: 在api/user.ts中增加:
export const userGetInfoUrl = '/user/info'
export const userGetInfoApi = () => {
return useGet<any, UserInfo>(userGetInfoUrl)
}
然后在stores/user.ts中调用
const getUserInfo = async () => {
const { data } = await userGetInfoApi()
if (data)
setUserInfo(data)
}
记得要暴露出去,然后再router-guard.ts中使用,如下:
if (!userStore.userInfo && !allowRoutes.includes(to.path)) {
try {
// 如果用户信息不存在,那么就需要获取用户信息
await userStore.getUserInfo()
// 判断当前是不是登录页面,如果是登录页面,那么就跳转到首页
if (to.path === loginRoute) {
// 跳转至首页
next({
path: '/',
})
return
}
}
catch (e) {
// 如果获取用户信息失败,那么我们直接阻止用户跳转并同时跳转到Error页面
next({
path: '/error',
query: {
redirect: to.path,
},
})
return
}
}
然后测试是否可以正常进行跳转 接下来我们需要完善一下error的页面: 在pages下创建一个error的目录并同时创建一个error.vue的文件如下:
<script lang="ts" setup>
</script>
<template>
<div>
Error
</div>
</template>
<style scoped>
</style>
在routes中的index.ts中添加对应的路由信息
{
path: '/error',
name: 'error',
component: () => import('~/pages/error/error.vue'),
},
接下来我们还可以添加一个跳转进度条的效果(可选)如下:
router.beforeEach(async (to, from, next) => {
+ const { loadingBar } = useGlobalConfig()
loadingBar?.start()
})
接下来我们来实现一下后置的钩子,我们目前可以在后置钩子中添加当前页面的标题如下并完成我们loadingBar的加载结束效果:
router.afterEach((to) => {
const appStore = useAppStore()
const { loadingBar } = useGlobalConfig()
const title = to.meta?.title
loadingBar?.finish()
if (title) { document.title = `${title} - ${appStore.layout.title}` }
else {
if (appStore.layout.title)
document.title = appStore.layout.title
}
})
最后查看实现的效果
之前我们已经完成了错误拦截,那么我们也可以在我们的token过期的时候增加一个重定向地址: 在utils下的request.ts中当状态码为401的时候
token.value = null
router.replace({
path: '/login',
query: {
redirect: router?.currentRoute.value?.path,
},
}).then(() => {})
最后调整路由守卫:
if (!userStore.userInfo && !allowRoutes.includes(to.path)) {
try {
// 如果用户信息不存在,那么就需要获取用户信息
await userStore.getUserInfo()
// 判断当前是不是登录页面,如果是登录页面,那么就跳转到首页
if (to.path === loginRoute) {
// 跳转至首页
next({
path: '/',
})
return
}
}
catch (e) {
// 判断当前如果是401的错误让给请求拦截处理
if (e instanceof AxiosError) {
if (e.response?.status === 401)
return
}
// 如果获取用户信息失败,那么我们直接阻止用户跳转即可
next({
path: '/error',
query: {
redirect: to.path,
},
})
return
}
}