37 路由拦截和白名单 - udo-bit/naive_admin_pro GitHub Wiki

目标

  1. 完成未授权路由拦截
  2. 完成授权路由的白名单开发

开发

上节课我们已经完成了登录部分的业务逻辑的开发,接下来我们需要实现的就是,当我们没有登录的情况下,我们需要控制我们的路由只能跳转我们的白名单页面,其他的页面一律自动跳转至登录页,然后在我们登录成功后,我针对性的跳转到对应的重定向地址部分。接下来我们一起来实现一下这一部分。 打开项目在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
  }
}
⚠️ **GitHub.com Fallback** ⚠️