# 用户权限API设计方案 ## 1. 后端需要返回的数据结构 ### 方案A:返回权限键数组(推荐) **接口**: `GET /api/v3/users/{user_id}/permissions` 或集成在登录响应中 **响应示例**: ```json { "code": 0, "msg": "成功", "data": { "user_id": 123, "role_id": 5, "role_key": "provincial_admin", "role_name": "省级管理员", "permissions": [ "prompt_template:list:read", "prompt_template:detail:read", "prompt_template:create:write", "prompt_template:update:write", "prompt_template:delete:delete", "document:list:read", "document:create:write" ] } } ``` ### 方案B:返回权限对象数组(更详细) ```json { "code": 0, "msg": "成功", "data": { "user_id": 123, "role_id": 5, "role_key": "provincial_admin", "role_name": "省级管理员", "permissions": [ { "permission_key": "prompt_template:create:write", "module": "prompt_template", "resource": "create", "action": "write", "display_name": "创建提示词模板" }, { "permission_key": "prompt_template:update:write", "module": "prompt_template", "resource": "update", "action": "write", "display_name": "更新提示词模板" } ] } } ``` ## 2. 后端SQL查询示例 ```sql -- 获取用户的所有权限(通过角色关联) SELECT DISTINCT p.permission_key, p.module, p.resource, p.action, p.display_name FROM users u JOIN user_roles ur ON u.id = ur.user_id JOIN role_permissions rp ON ur.role_id = rp.role_id AND rp.grant_type = 'GRANT' JOIN permissions p ON rp.permission_id = p.id WHERE u.id = $1 AND u.deleted_at IS NULL AND ur.deleted_at IS NULL; ``` ## 3. 集成到现有认证流程 ### 选项1:集成到JWT Token中 - 优点:前端无需额外请求,直接从JWT解析 - 缺点:JWT体积变大,权限变更需要重新登录 - 适用场景:权限不经常变化,用户会话较短 ### 选项2:集成到Session中 - 优点:权限可以实时更新(每次loader都会调用getUserSession) - 缺点:每次页面加载都要查询数据库 - 适用场景:权限经常变化,需要实时控制 ### 选项3:混合方案(推荐) - JWT中存储基础权限(permission_key数组) - 重要操作时后端再次验证 - 权限变更后强制用户重新登录或刷新token ## 4. 前端权限检查工具 ### 创建权限Hook ```typescript // app/hooks/usePermission.ts import { useRouteLoaderData } from "@remix-run/react"; export function usePermission() { const rootData = useRouteLoaderData("root") as { permissions?: string[]; userRole: string; }; const permissions = rootData?.permissions || []; /** * 检查是否有指定权限 * @param permissionKey 权限键,如 "prompt_template:create:write" * @returns boolean */ const hasPermission = (permissionKey: string): boolean => { return permissions.includes(permissionKey); }; /** * 检查是否有指定模块的任意权限 * @param module 模块名,如 "prompt_template" * @returns boolean */ const hasModulePermission = (module: string): boolean => { return permissions.some(p => p.startsWith(`${module}:`)); }; /** * 检查是否有指定动作的权限 * @param module 模块名 * @param action 动作,如 "create", "update", "delete" * @returns boolean */ const hasActionPermission = (module: string, action: string): boolean => { return permissions.some(p => p.startsWith(`${module}:`) && p.includes(`:${action}`) ); }; /** * 批量检查权限(需要全部满足) * @param permissionKeys 权限键数组 * @returns boolean */ const hasAllPermissions = (permissionKeys: string[]): boolean => { return permissionKeys.every(key => permissions.includes(key)); }; /** * 批量检查权限(满足任意一个即可) * @param permissionKeys 权限键数组 * @returns boolean */ const hasAnyPermission = (permissionKeys: string[]): boolean => { return permissionKeys.some(key => permissions.includes(key)); }; return { permissions, hasPermission, hasModulePermission, hasActionPermission, hasAllPermissions, hasAnyPermission }; } ``` ### 在组件中使用 ```typescript // app/routes/prompts._index.tsx import { usePermission } from "~/hooks/usePermission"; export default function PromptsIndex() { const { hasPermission, hasActionPermission } = usePermission(); // 检查是否有创建权限 const canCreate = hasPermission("prompt_template:create:write"); // 检查是否有编辑权限 const canEdit = hasPermission("prompt_template:update:write"); // 检查是否有删除权限 const canDelete = hasPermission("prompt_template:delete:delete"); // 或者使用通用检查 const canManage = hasActionPermission("prompt_template", "write"); return (
{canCreate && ( )} {/* 表格操作列 */} {canEdit && } {canDelete && }
); } ``` ## 5. 后端验证(双重保险) 前端权限检查只是为了改善用户体验,**后端必须再次验证权限**: ```typescript // 后端API示例(Node.js/Express风格) app.post('/api/v3/prompt-templates', authenticate, async (req, res) => { const userId = req.user.id; // 验证用户是否有创建权限 const hasPermission = await checkUserPermission( userId, 'prompt_template:create:write' ); if (!hasPermission) { return res.status(403).json({ code: 403, msg: '权限不足:您没有创建提示词模板的权限' }); } // 执行创建逻辑... }); ``` ## 6. 实施步骤 ### 第一步:数据库准备 ```sql -- 确保用户角色关联表存在 -- user_roles: user_id <-> role_id -- 确保角色权限关联表存在 -- role_permissions: role_id <-> permission_id (已存在) -- 为现有用户分配角色和权限 ``` ### 第二步:后端API开发 1. 创建 `GET /api/v3/users/current/permissions` 接口 2. 在登录响应中包含权限列表 3. 在JWT payload中包含permissions字段(可选) ### 第三步:前端集成 1. 修改 `app/api/login/auth.server.ts`,在getUserSession中获取权限 2. 修改 `app/root.tsx` loader,将permissions传递给前端 3. 创建 `app/hooks/usePermission.ts` 4. 修改 `app/routes/prompts._index.tsx` 使用权限检查 ### 第四步:测试验证 1. 测试不同角色的用户看到的功能是否正确 2. 测试后端API权限验证是否生效 3. 测试权限变更后是否需要重新登录 ## 7. 注意事项 1. **安全性**:前端权限检查只是UI控制,不能替代后端验证 2. **性能**:权限列表应该缓存,避免每次请求都查询数据库 3. **同步性**:权限变更后,考虑强制用户重新登录或刷新token 4. **降级方案**:如果获取权限失败,应该有合理的降级策略(如只读模式) 5. **审计日志**:所有权限相关的操作应该记录审计日志