From 7c51fa8a306d846fef525871cc14f3f20c1bb667 Mon Sep 17 00:00:00 2001 From: panxinying Date: Thu, 12 Nov 2026 15:55:56 +0800 Subject: [PATCH] fix: use target route workspaceId in permission guard --- ui/src/router/admin/index.ts | 19 +++++++++--------- ui/src/utils/UTILS_README.md | 2 ++ ui/src/utils/resource-context.ts | 34 ++++++++++++++++++++++++++++---- 3 files changed, 42 insertions(+), 13 deletions(-) diff --git a/ui/src/router/admin/index.ts b/ui/src/router/admin/index.ts index 3d3c5932981..ce345f1c774 100644 --- a/ui/src/router/admin/index.ts +++ b/ui/src/router/admin/index.ts @@ -12,6 +12,7 @@ import { systemRoutes } from './system' import { workspaceRoutes } from './workspace' import { workflowRoutes } from './workflow' import { getChildRouteList } from './utils' +import { withRouteWorkspace } from '@/utils/resource-context' const ADMIN_BASE_PATH = window.MaxKB?.prefix || import.meta.env.VITE_BASE_PATH || '/admin/' NProgress.configure({ minimum: 0.3, showSpinner: false, speed: 500, trickleSpeed: 200 }) @@ -62,17 +63,17 @@ router.beforeEach(async (to, from) => { } } -const workspaceList = user.userInfo?.workspace_list ?? [] -if (to.name === 'workspace-home' && workspaceList.length === 0) { - const hasSystemAccess = getChildRouteList('system').some((item) => (item.permission ? item.permission() : true)) - if (hasSystemAccess) { - return { name: 'system-home', replace: true } + const workspaceList = user.userInfo?.workspace_list ?? [] + if (to.name === 'workspace-home' && workspaceList.length === 0) { + const hasSystemAccess = getChildRouteList('system').some((item) => (item.permission ? item.permission() : true)) + if (hasSystemAccess) { + return { name: 'system-home', replace: true } + } + // 没有任何工作空间、也没有系统权限:不做额外处理,保持原有行为 } - // 没有任何工作空间、也没有系统权限:不做额外处理,保持原有行为 -} // 当前路由有权限,放行 - if (!to.meta?.permission || (to.meta.permission as (route: RouteLocationNormalizedLoaded) => boolean)(to)) { + if (!to.meta?.permission || withRouteWorkspace(to, () => (to.meta.permission as (route: RouteLocationNormalizedLoaded) => boolean)(to))) { return true } @@ -98,7 +99,7 @@ if (to.name === 'workspace-home' && workspaceList.length === 0) { const nextPermission = nextRoute.meta?.permission as ( route?: RouteLocationNormalized | RouteLocationResolvedGeneric | RouteLocationNormalizedLoaded, ) => boolean - if (!nextPermission || nextPermission(nextRoute)) { + if (!nextPermission || withRouteWorkspace(nextRoute, () => nextPermission(nextRoute))) { return { name: nextName, params: to.params, query: to.query, hash: to.hash, replace: true } } } diff --git a/ui/src/utils/UTILS_README.md b/ui/src/utils/UTILS_README.md index 9bc75c21508..2456ab65082 100644 --- a/ui/src/utils/UTILS_README.md +++ b/ui/src/utils/UTILS_README.md @@ -87,6 +87,8 @@ MkTable、MkFormList 的排序优先使用其公开属性;普通列表再直 资源范围定义见 [ROUTE_README.md](../router/ROUTE_README.md#资源范围判断)。 `resource-context.ts` 提供 getWorkspaceId、getResourceScope 和四种 isXxxResource 判断。 非组件代码通过 getWorkspaceId 获取工作空间;页面/组件通过现有判断读取当前资源范围。 +Routing 守卫评估目标路由权限时使用 `withRouteWorkspace(route, fn)` 将该路由的 workspaceId 作为 +判定上下文,此时 router.currentRoute 尚未更新为待跳转路由,不能直接读取。 isWorkspaceResource 仅匹配 workspace,不包含 workspace-shared;未配置范围时不推测来源。 ## VNode 插槽内容判断 diff --git a/ui/src/utils/resource-context.ts b/ui/src/utils/resource-context.ts index 94e4b8fd2ab..e2ba7f9a63b 100644 --- a/ui/src/utils/resource-context.ts +++ b/ui/src/utils/resource-context.ts @@ -1,14 +1,40 @@ /** 提供当前路由资源范围和工作空间上下文的读取函数。 */ import router from '@/router/admin' +import type { RouteLocationNormalized, RouteLocationNormalizedLoaded, RouteLocationResolvedGeneric } from 'vue-router' + +type WorkspaceRouteLike = RouteLocationNormalized | RouteLocationNormalizedLoaded | RouteLocationResolvedGeneric + +let hasRouteWorkspace = false +let routeWorkspaceId: string | undefined + +const readWorkspaceId = (route: WorkspaceRouteLike): string | undefined => { + const value = route.params.workspaceId + return Array.isArray(value) ? value[0] : value +} /** * 获取当前工作空间 id。 - * 当前实现取自路由参数;不在工作空间上下文下时返回 undefined。 + * 取自目标路由参数;不在工作空间上下文下时返回 undefined。 */ -export const getWorkspaceId = (): string | undefined => { - const value = router.currentRoute.value.params.workspaceId - return Array.isArray(value) ? value[0] : value +export const getWorkspaceId = (): string | undefined => (hasRouteWorkspace ? routeWorkspaceId : readWorkspaceId(router.currentRoute.value)) + +/** + * 在给定路由的工作空间上下文内执行回调,期间 getWorkspaceId 使用该路由的 workspaceId。 + * 用于路由守卫评估目标路由权限:此时 router.currentRoute 尚未更新为待跳转路由, + * 需要以被评估路由的参数为准,避免读到上一条(或初始 START)路由导致结果错误。 + */ +export const withRouteWorkspace = (route: WorkspaceRouteLike, fn: () => T): T => { + const prevHas = hasRouteWorkspace + const prevId = routeWorkspaceId + hasRouteWorkspace = true + routeWorkspaceId = readWorkspaceId(route) + try { + return fn() + } finally { + hasRouteWorkspace = prevHas + routeWorkspaceId = prevId + } } /** 判断当前路由是否属于工作空间资源。 */