fix:修复前端路由权限校验。修复交叉评查与普通评查结果的ai建议的替换效果不一致。

This commit is contained in:
2025-12-10 09:10:57 +08:00
parent ad3f244a1b
commit ba517d7b9c
4 changed files with 196 additions and 19 deletions
+71 -7
View File
@@ -73,22 +73,85 @@ function extractAllPaths(menuItems: MenuItem[]): string[] {
return paths;
}
// 辅助函数:检查路径是否在允许列表中
/**
* 检查路径段是否看起来像动态ID(允许访问)
*
* 动态ID的特征:
* - 纯数字:123、456
* - UUID格式:550e8400-e29b-41d4-a716-446655440000
* - 包含数字+特殊字符的混合IDabc-123、doc_456
*
* 固定路由的特征(需要在菜单中明确配置):
* - 纯英文单词:upload、edit、create、list
* - 多单词路由:create-task、edit-profile
*
* @param segment 路径段(例如:'123' 或 'upload'
* @returns true 表示是动态ID,允许访问;false 表示是固定路由,需要权限检查
*/
function isDynamicIdSegment(segment: string): boolean {
// 1. 纯数字(最常见的动态ID)
if (/^\d+$/.test(segment)) {
return true;
}
// 2. UUID格式(包含连字符的十六进制字符串)
if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(segment)) {
return true;
}
// 3. 包含数字的混合ID(如:doc-123、user_456、item123
// 但排除纯英文单词+连字符的组合(如:create-task、edit-profile
if (/\d/.test(segment) && !/^[a-z]+(-[a-z]+)*$/i.test(segment)) {
return true;
}
// 其他情况视为固定路由(需要在菜单中明确配置)
return false;
}
/**
* 辅助函数:检查路径是否在允许列表中
*
* 匹配规则:
* 1. 精确匹配:pathname 完全在 allowedPaths 中
* 2. 动态路由匹配:只允许看起来像动态ID的子路径
* - 允许:/documents/123(纯数字)
* - 允许:/documents/550e8400-e29b-41d4-a716-446655440000UUID
* - 拒绝:/documents/upload(固定子路由,需要在菜单中明确配置)
* 3. 根路径特殊处理:'/' 始终允许
*
* @param pathname 当前访问的路径
* @param allowedPaths 允许访问的路径列表(从菜单配置中提取)
* @returns true 表示允许访问,false 表示拒绝访问
*/
function isPathAllowed(pathname: string, allowedPaths: string[]): boolean {
// 精确匹配
// 1. 精确匹配
if (allowedPaths.includes(pathname)) {
return true;
}
// 前缀匹配(处理动态路由和子路
// 例如:allowedPaths 包含 '/documents',则 '/documents/123' 也应该被允许
// 2. 动态路由匹配(只允许看起来像ID的子路
for (const allowedPath of allowedPaths) {
if (pathname.startsWith(allowedPath + '/')) {
return true;
// 提取子路径部分(例如:'/documents/123' -> '123'
const subPath = pathname.substring(allowedPath.length + 1);
// 支持多级嵌套路由(例如:/documents/123/edit
const segments = subPath.split('/');
// 检查第一个路径段是否是动态ID
// 如果是动态ID,允许访问(后续路径段不再检查,因为通常是操作动作)
// 如果不是动态ID,则必须在 allowedPaths 中明确配置
const firstSegment = segments[0];
if (isDynamicIdSegment(firstSegment)) {
return true; // 动态ID路由,允许访问
}
// 如果不是动态ID,继续检查是否有精确匹配(已在第1步检查过)
}
}
// 根路径特殊处理(仅根路径 '/' 对所有已登录用户开放)
// 3. 根路径特殊处理(仅根路径 '/' 对所有已登录用户开放)
if (pathname === '/') {
return true; // 根路径重定向到首页,始终允许
}
@@ -166,7 +229,7 @@ export async function loader({ request }: LoaderFunctionArgs) {
if (routesResult.success && routesResult.data) {
// 从菜单数据中提取所有允许的路径
allowedPaths = extractAllPaths(routesResult.data);
// console.log("🔑 [Root Loader] 用户允许的路由:", allowedPaths);
console.log("🔑 [Root Loader] 用户允许的路由:", allowedPaths);
// ✅ 保存权限映射表
if (routesResult.permissionMap) {
@@ -175,6 +238,7 @@ export async function loader({ request }: LoaderFunctionArgs) {
}
// 检查当前路径是否在允许列表中
console.log("🔑 [Root Loader] 检查当前路径是否在允许列表中...", pathname, allowedPaths);
const isAllowedPath = isPathAllowed(pathname, allowedPaths);
if (!isAllowedPath) {