添加 VITE_AUTH_ROUTE_FORCE_LOGIN 环境变量以控制是否强制用户登录 当设置为 Y 时,未登录用户访问非白名单路由将被重定向到登录页 同时更新开发环境配置并添加相应的 TypeScript 类型定义
201 lines
5.4 KiB
TypeScript
201 lines
5.4 KiB
TypeScript
import type { RouteKey, RoutePath } from '@elegant-router/types'
|
|
import type {
|
|
LocationQueryRaw,
|
|
NavigationGuardNext,
|
|
RouteLocationNormalized,
|
|
RouteLocationRaw,
|
|
Router,
|
|
} from 'vue-router'
|
|
import { getRouteName } from '@/router/elegant/transform'
|
|
import { useAuthStore } from '@/store/modules/auth'
|
|
import { useRouteStore } from '@/store/modules/route'
|
|
import { localStg } from '@/utils/storage'
|
|
|
|
/**
|
|
* 创建路由守卫
|
|
*
|
|
* @param router 路由实例
|
|
*/
|
|
export function createRouteGuard(router: Router) {
|
|
router.beforeEach(async (to, from, next) => {
|
|
const location = await initRoute(to)
|
|
|
|
if (location) {
|
|
next(location)
|
|
return
|
|
}
|
|
|
|
const authStore = useAuthStore()
|
|
|
|
const rootRoute: RouteKey = 'root'
|
|
const loginRoute: RouteKey = 'login'
|
|
const noAuthorizationRoute: RouteKey = '403'
|
|
|
|
const isLogin = Boolean(localStg.get('token'))
|
|
const needLogin = !to.meta.constant
|
|
const routeRoles = to.meta.roles || []
|
|
|
|
const hasRole = authStore.userInfo.roles.some(role => routeRoles.includes(role))
|
|
const hasAuth = authStore.isStaticSuper || !routeRoles.length || hasRole
|
|
|
|
// 如果已登录且访问的是登录页,则跳转到根页面
|
|
if (to.name === loginRoute && isLogin) {
|
|
next({ name: rootRoute })
|
|
return
|
|
}
|
|
|
|
// 如果路由不需要登录,则直接允许访问
|
|
if (!needLogin) {
|
|
handleRouteSwitch(to, from, next)
|
|
return
|
|
}
|
|
|
|
// 如果路由需要登录但用户未登录,则跳转到登录页
|
|
const isAuthRouteForceLogin = import.meta.env.VITE_AUTH_ROUTE_FORCE_LOGIN === 'Y'
|
|
const whiteList: string[] = ['user']
|
|
const isInWhiteList = whiteList.includes(to.name as string)
|
|
|
|
if (isAuthRouteForceLogin && !isLogin && !isInWhiteList) {
|
|
next({ name: loginRoute, query: { redirect: to.fullPath } })
|
|
return
|
|
}
|
|
|
|
// 如果用户已登录但没有权限,则跳转到 403 页面
|
|
if (!hasAuth) {
|
|
next({ name: noAuthorizationRoute })
|
|
return
|
|
}
|
|
|
|
// 正常跳转路由
|
|
handleRouteSwitch(to, from, next)
|
|
})
|
|
}
|
|
|
|
/**
|
|
* 初始化路由
|
|
*
|
|
* @param to 目标路由
|
|
*/
|
|
async function initRoute(to: RouteLocationNormalized): Promise<RouteLocationRaw | null> {
|
|
const routeStore = useRouteStore()
|
|
|
|
const notFoundRoute: RouteKey = 'not-found'
|
|
const isNotFoundRoute = to.name === notFoundRoute
|
|
|
|
// 如果常量路由未初始化,则初始化常量路由
|
|
if (!routeStore.isInitConstantRoute) {
|
|
await routeStore.initConstantRoute()
|
|
|
|
// 由于常量路由未初始化,路由被“未找到”路由捕获
|
|
// 常量路由初始化后,重定向到原始路由
|
|
const path = to.fullPath
|
|
const location: RouteLocationRaw = {
|
|
path,
|
|
replace: true,
|
|
query: to.query,
|
|
hash: to.hash,
|
|
}
|
|
|
|
return location
|
|
}
|
|
|
|
const isLogin = Boolean(localStg.get('token'))
|
|
|
|
const isAuthRouteForceLogin = import.meta.env.VITE_AUTH_ROUTE_FORCE_LOGIN === 'Y'
|
|
const whiteList: string[] = ['user']
|
|
const isInWhiteList = whiteList.includes(to.name as string)
|
|
|
|
if (isAuthRouteForceLogin && !isLogin && !isInWhiteList) {
|
|
// 如果用户未登录且路由是常量路由但不是“未找到”路由,则允许访问。
|
|
if (to.meta.constant && !isNotFoundRoute) {
|
|
routeStore.onRouteSwitchWhenNotLoggedIn()
|
|
|
|
return null
|
|
}
|
|
|
|
// 如果用户未登录,则跳转到登录页
|
|
const loginRoute: RouteKey = 'login'
|
|
const query = getRouteQueryOfLoginRoute(to, routeStore.routeHome)
|
|
|
|
const location: RouteLocationRaw = {
|
|
name: loginRoute,
|
|
query,
|
|
}
|
|
|
|
return location
|
|
}
|
|
|
|
if (!routeStore.isInitAuthRoute) {
|
|
// 初始化权限路由
|
|
await routeStore.initAuthRoute()
|
|
|
|
// 由于权限路由未初始化,路由被“未找到”路由捕获
|
|
// 权限路由初始化后,重定向到原始路由
|
|
if (isNotFoundRoute) {
|
|
const rootRoute: RouteKey = 'root'
|
|
const path = to.redirectedFrom?.name === rootRoute ? '/' : to.fullPath
|
|
|
|
const location: RouteLocationRaw = {
|
|
path,
|
|
replace: true,
|
|
query: to.query,
|
|
hash: to.hash,
|
|
}
|
|
|
|
return location
|
|
}
|
|
}
|
|
|
|
routeStore.onRouteSwitchWhenLoggedIn()
|
|
|
|
// 权限路由已初始化
|
|
// 如果不是“未找到”路由,则允许访问
|
|
if (!isNotFoundRoute) {
|
|
return null
|
|
}
|
|
|
|
// 如果被“未找到”路由捕获,则检查路由是否存在
|
|
const exist = await routeStore.getIsAuthRouteExist(to.path as RoutePath)
|
|
const noPermissionRoute: RouteKey = '403'
|
|
|
|
if (exist) {
|
|
const location: RouteLocationRaw = {
|
|
name: noPermissionRoute,
|
|
}
|
|
|
|
return location
|
|
}
|
|
|
|
return null
|
|
}
|
|
|
|
function handleRouteSwitch(to: RouteLocationNormalized, from: RouteLocationNormalized, next: NavigationGuardNext) {
|
|
// 外链路由
|
|
if (to.meta.href) {
|
|
window.open(to.meta.href, '_blank')
|
|
|
|
next({ path: from.fullPath, replace: true, query: from.query, hash: to.hash })
|
|
|
|
return
|
|
}
|
|
|
|
next()
|
|
}
|
|
|
|
function getRouteQueryOfLoginRoute(to: RouteLocationNormalized, routeHome: RouteKey) {
|
|
const loginRoute: RouteKey = 'login'
|
|
const redirect = to.fullPath
|
|
const [redirectPath, redirectQuery] = redirect.split('?')
|
|
const redirectName = getRouteName(redirectPath as RoutePath)
|
|
|
|
const isRedirectHome = routeHome === redirectName
|
|
|
|
const query: LocationQueryRaw = to.name !== loginRoute && !isRedirectHome ? { redirect } : {}
|
|
|
|
if (isRedirectHome && redirectQuery) {
|
|
query.redirect = `/?${redirectQuery}`
|
|
}
|
|
|
|
return query
|
|
}
|