fix: enforce permission-aware navigation

This commit is contained in:
jxxghp
2026-06-09 21:45:51 +08:00
parent d0cac34d08
commit 4691d12faa
42 changed files with 483 additions and 239 deletions

View File

@@ -1,9 +1,9 @@
import { useGlobalSettingsStore } from '@/stores'
import type { NavMenuTabItem } from '@/@layouts/types'
import type { NavMenu, NavMenuTabItem } from '@/@layouts/types'
import type { Composer } from 'vue-i18n'
// 构建路由菜单,每次调用时使用当前的语言环境
export function getNavMenus(t: Composer['t']) {
export function getNavMenus(t: Composer['t']): NavMenu[] {
const globalSettingsStore = useGlobalSettingsStore()
// 检查是否为高级模式
@@ -17,7 +17,7 @@ export function getNavMenus(t: Composer['t']) {
header: t('menu.start'),
admin: false,
footer: true,
permission: 'manage',
permission: 'admin',
},
{
title: t('navItems.searchResult'),
@@ -119,7 +119,7 @@ export function getNavMenus(t: Composer['t']) {
to: '/plugins',
header: t('menu.system'),
admin: true,
permission: 'manage',
permission: 'admin',
tabs: getPluginTabs(t),
},
{
@@ -148,7 +148,7 @@ export function getNavMenus(t: Composer['t']) {
admin: true,
permission: 'admin',
tabs: getSettingTabs(t),
},
} as NavMenu,
]
: []),
]

View File

@@ -1,7 +1,8 @@
import { createRouter, createWebHashHistory } from 'vue-router'
import { configureNProgress } from '@/api/nprogress'
import { useAuthStore } from '@/stores'
import { useAuthStore, usePluginSidebarNavStore, useUserStore } from '@/stores'
import { setNavigatingState as setRequestNavigatingState } from '@/utils/requestOptimizer'
import { buildUserPermissionContext, hasPermission, type UserPermissionKey } from '@/utils/permission'
// Nprogress
configureNProgress()
@@ -15,7 +16,15 @@ const router = createRouter({
return { top: 0 }
},
routes: [
{ path: '/', redirect: '/dashboard' },
{
path: '/',
redirect: () => {
const authStore = useAuthStore()
const userStore = useUserStore()
if (!authStore.token) return '/login'
return userStore.superUser ? '/dashboard' : '/apps'
},
},
{
path: '/',
component: () => import('../layouts/default.vue'),
@@ -26,6 +35,7 @@ const router = createRouter({
meta: {
keepAlive: true,
requiresAuth: true,
permission: 'admin',
},
},
{
@@ -34,6 +44,7 @@ const router = createRouter({
meta: {
keepAlive: true,
requiresAuth: true,
permission: 'discovery',
},
},
{
@@ -42,6 +53,7 @@ const router = createRouter({
meta: {
keepAlive: true,
requiresAuth: true,
permission: 'discovery',
},
},
{
@@ -50,6 +62,7 @@ const router = createRouter({
meta: {
keepAlive: true,
requiresAuth: true,
permission: 'search',
},
},
{
@@ -59,6 +72,7 @@ const router = createRouter({
keepAlive: true,
keepAliveKey: 'subscribe-movie',
requiresAuth: true,
permission: 'subscribe',
subType: '电影',
},
},
@@ -69,6 +83,7 @@ const router = createRouter({
keepAlive: true,
keepAliveKey: 'subscribe-tv',
requiresAuth: true,
permission: 'subscribe',
subType: '电视剧',
},
},
@@ -77,6 +92,7 @@ const router = createRouter({
component: () => import('../pages/subscribe-share.vue'),
meta: {
requiresAuth: true,
permission: 'subscribe',
},
},
{
@@ -85,6 +101,7 @@ const router = createRouter({
meta: {
keepAlive: true,
requiresAuth: true,
permission: 'manage',
},
},
{
@@ -93,6 +110,7 @@ const router = createRouter({
meta: {
keepAlive: true,
requiresAuth: true,
permission: 'subscribe',
},
},
{
@@ -101,6 +119,7 @@ const router = createRouter({
meta: {
keepAlive: true,
requiresAuth: true,
permission: 'manage',
},
},
{
@@ -109,6 +128,7 @@ const router = createRouter({
meta: {
keepAlive: true,
requiresAuth: true,
permission: 'manage',
hideFooter: true,
},
},
@@ -118,6 +138,7 @@ const router = createRouter({
meta: {
keepAlive: true,
requiresAuth: true,
permission: 'manage',
},
},
{
@@ -126,6 +147,7 @@ const router = createRouter({
meta: {
keepAlive: true,
requiresAuth: true,
permission: 'admin',
},
},
{
@@ -142,6 +164,7 @@ const router = createRouter({
meta: {
keepAlive: true,
requiresAuth: true,
permission: 'admin',
},
},
{
@@ -158,6 +181,7 @@ const router = createRouter({
meta: {
keepAlive: true,
requiresAuth: true,
permission: 'admin',
},
},
{
@@ -166,6 +190,7 @@ const router = createRouter({
props: true,
meta: {
requiresAuth: true,
permission: 'discovery',
},
},
{
@@ -189,6 +214,7 @@ const router = createRouter({
component: () => import('../pages/media.vue'),
meta: {
requiresAuth: true,
permission: 'discovery',
},
},
{
@@ -197,6 +223,7 @@ const router = createRouter({
meta: {
keepAlive: true,
requiresAuth: true,
permission: 'manage',
hideFooter: true,
},
},
@@ -223,6 +250,7 @@ const router = createRouter({
component: () => import('../pages/setup.vue'),
meta: {
requiresAuth: true,
permission: 'admin',
},
},
{
@@ -234,6 +262,24 @@ const router = createRouter({
],
})
async function getRoutePermission(to: any): Promise<UserPermissionKey | undefined> {
if (to.meta.permission) {
return to.meta.permission as UserPermissionKey
}
if (to.name !== 'plugin-app') {
return undefined
}
const pluginId = String(to.params.pluginId || '')
const navKey = String(to.params.navKey || 'main')
const pluginSidebarNavStore = usePluginSidebarNavStore()
await pluginSidebarNavStore.ensureSidebarNav()
const navItem = pluginSidebarNavStore.items.find(item => item.plugin_id === pluginId && item.nav_key === navKey)
return (navItem?.permission || undefined) as UserPermissionKey | undefined
}
// 路由导航守卫
router.beforeEach(async (to: any, from: any, next: any) => {
// 设置导航状态 - 同时中断API请求
@@ -249,6 +295,24 @@ router.beforeEach(async (to: any, from: any, next: any) => {
// 用户未登录,重定向到登录页
setRequestNavigatingState(false)
next('/login')
} else if (to.meta.requiresAuth) {
const routePermission = await getRoutePermission(to)
if (!routePermission) {
next()
return
}
const userStore = useUserStore()
const allowed = hasPermission(
buildUserPermissionContext(userStore.superUser, userStore.permissions),
routePermission,
)
if (!allowed) {
setRequestNavigatingState(false)
next('/apps')
return
}
next()
} else {
next()
}