# 权限系统设计文档 (Crawlful Hub) > **定位**:Crawlful Hub 权限系统设计文档 - 定义基于角色的访问控制(RBAC)和数据级权限。 > **更新日期**: 2026-03-18 > **最高优先级参考**: [Service_Design.md](./Service_Design.md) --- ## 1. 权限系统概述 ### 1.1 核心概念 - **角色(Role)**:一组权限的集合 - **权限(Permission)**:对资源的操作许可 - **用户(User)**:系统的使用者 - **资源(Resource)**:系统中的对象,如商品、订单、用户等 - **数据级权限**:基于数据属性的访问控制 ### 1.2 重要性 良好的权限系统可以: - 保护系统安全 - 确保数据隐私 - 实现职责分离 - 满足合规要求 - 提供可审计性 --- ## 2. RBAC 模型 ### 2.1 角色定义 | 角色 | 描述 | 权限范围 | |------|------|----------| | **ADMIN** | 系统管理员 | 所有资源的所有操作 | | **MANAGER** | 运营主管 | 大部分资源的管理操作 | | **OPERATOR** | 运营专员 | 基础运营操作 | | **FINANCE** | 财务主管 | 财务相关操作 | | **SOURCING** | 采购专家 | 采购相关操作 | | **LOGISTICS** | 物流专家 | 物流相关操作 | | **ANALYST** | 数据分析师 | 数据分析相关操作 | ### 2.2 权限定义 | 权限 | 描述 | 适用资源 | |------|------|----------| | **CREATE** | 创建资源 | 所有资源 | | **READ** | 读取资源 | 所有资源 | | **UPDATE** | 更新资源 | 所有资源 | | **DELETE** | 删除资源 | 所有资源 | | **APPROVE** | 审批操作 | 订单、结算等 | | **EXPORT** | 导出数据 | 报表、数据等 | | **IMPORT** | 导入数据 | 商品、用户等 | ### 2.3 权限分配 **示例**: ```typescript // 角色权限映射 const rolePermissions = { ADMIN: ['*'], // 所有权限 MANAGER: [ 'CREATE:product', 'READ:product', 'UPDATE:product', 'DELETE:product', 'CREATE:order', 'READ:order', 'UPDATE:order', 'APPROVE:order', 'READ:report', 'EXPORT:report' ], OPERATOR: [ 'READ:product', 'UPDATE:product', 'READ:order', 'UPDATE:order' ], FINANCE: [ 'READ:order', 'READ:settlement', 'APPROVE:settlement', 'READ:report', 'EXPORT:report' ], SOURCING: [ 'CREATE:product', 'READ:product', 'UPDATE:product', 'READ:supplier', 'CREATE:supplier', 'UPDATE:supplier' ], LOGISTICS: [ 'READ:order', 'UPDATE:order', 'READ:inventory', 'UPDATE:inventory' ], ANALYST: [ 'READ:report', 'EXPORT:report' ] }; ``` --- ## 3. 数据级权限 ### 3.1 定义 数据级权限是指基于数据属性的访问控制,确保用户只能访问和操作与自己相关的数据。 ### 3.2 实现方法 #### 3.2.1 租户隔离 **定义**:不同租户的数据相互隔离 **实现**: - 在所有数据表中添加 `tenantId` 字段 - 在所有查询中默认带上 `tenantId` 过滤 - 确保跨租户操作的安全性 **示例**: ```typescript // 数据访问层 class ProductRepository { async findAll(tenantId: string, params?: any): Promise { return await Product.findAll({ where: { tenantId, ...params } }); } async findById(id: string, tenantId: string): Promise { return await Product.findOne({ where: { id, tenantId } }); } } ``` #### 3.2.2 店铺隔离 **定义**:同一租户下不同店铺的数据相互隔离 **实现**: - 在相关数据表中添加 `shopId` 字段 - 在查询中根据用户的店铺权限过滤 - 确保用户只能访问自己有权限的店铺数据 **示例**: ```typescript // 权限服务 class RBACService { async checkShopAccess(userId: string, shopId: string): Promise { const user = await this.userRepository.findById(userId); const userShops = await this.userShopRepository.findByUserId(userId); return userShops.some(us => us.shopId === shopId) || user.role === 'ADMIN'; } } // 订单服务 class OrderService { async getOrders(userId: string, params?: any): Promise { const user = await this.userRepository.findById(userId); if (user.role === 'ADMIN') { return await this.orderRepository.findAll(params); } const userShops = await this.userShopRepository.findByUserId(userId); const shopIds = userShops.map(us => us.shopId); return await this.orderRepository.findAll({ ...params, shopId: shopIds }); } } ``` #### 3.2.3 字段级权限 **定义**:基于字段的访问控制 **实现**: - 定义字段级权限规则 - 在返回数据时过滤敏感字段 - 确保用户只能看到和修改自己有权限的字段 **示例**: ```typescript // 字段级权限 const fieldPermissions = { ADMIN: { product: ['*'], // 所有字段 user: ['*'] }, MANAGER: { product: ['id', 'name', 'price', 'stock'], user: ['id', 'name', 'email'] }, OPERATOR: { product: ['id', 'name', 'price'], user: ['id', 'name'] } }; // 数据过滤 function filterFields(data: any, fields: string[]): any { if (fields.includes('*')) { return data; } const filtered: any = {}; fields.forEach(field => { if (data[field] !== undefined) { filtered[field] = data[field]; } }); return filtered; } ``` ### 3.3 最佳实践 - **默认带租户ID**:所有查询默认带 `tenantId` 过滤 - **权限检查前置**:在业务逻辑开始前检查权限 - **统一权限服务**:使用统一的权限服务处理权限检查 - **缓存权限**:缓存用户权限,提高性能 - **审计日志**:记录权限相关操作的审计日志 --- ## 4. 权限验证 ### 4.1 实现方法 #### 4.1.1 中间件验证 **适用场景**:API 路由权限验证 **实现**: - 创建权限验证中间件 - 在路由中应用中间件 - 验证用户是否有权限访问资源 **示例**: ```typescript // 权限中间件 const authorize = (requiredPermission: string) => { return (req, res, next) => { const user = req.user; if (!user) { return res.status(401).json({ error: 'Unauthorized' }); } if (!user.permissions.includes(requiredPermission) && user.role !== 'ADMIN') { return res.status(403).json({ error: 'Forbidden' }); } next(); }; }; // 使用 app.get('/products', authorize('READ:product'), productController.getProducts); app.post('/products', authorize('CREATE:product'), productController.createProduct); ``` #### 4.1.2 服务层验证 **适用场景**:业务逻辑权限验证 **实现**: - 在服务层检查权限 - 验证用户是否有权限操作资源 - 验证用户是否有权限访问数据 **示例**: ```typescript async updateProduct(productId: string, updates: Partial, userId: string): Promise { // 检查操作权限 if (!this.rbacService.hasPermission(userId, 'UPDATE:product')) { throw new ForbiddenException('You do not have permission to update products'); } // 获取商品 const product = await this.productRepository.findById(productId); if (!product) { throw new NotFoundException('Product not found'); } // 检查数据权限 if (!this.rbacService.hasDataAccess(userId, product.tenantId, product.shopId)) { throw new ForbiddenException('You do not have access to this product'); } // 更新商品 Object.assign(product, updates); return await this.productRepository.save(product); } ``` ### 4.2 最佳实践 - **多层验证**:在中间件和服务层都进行权限验证 - **明确的错误信息**:返回清晰的权限错误信息 - **权限缓存**:缓存用户权限,提高验证性能 - **定期权限检查**:定期检查权限配置的正确性 --- ## 5. 权限管理 ### 5.1 角色管理 - **创建角色**:定义新角色 - **修改角色**:修改角色的权限 - **删除角色**:删除不需要的角色 - **分配角色**:为用户分配角色 - **自定义角色**:允许租户管理员创建自定义角色和权限组合 - **权限细粒度控制**:更细致的功能权限划分 - **权限继承可视化**:清晰展示权限继承关系 - **权限审计**:记录权限变更历史 ### 5.2 权限管理 - **定义权限**:定义新的权限 - **分配权限**:为角色分配权限 - **回收权限**:从角色中回收权限 ### 5.3 用户管理 - **创建用户**:创建新用户并分配角色 - **修改用户**:修改用户的角色和权限 - **删除用户**:删除用户 - **禁用用户**:暂时禁用用户 - **批量操作支持**:批量添加、更新用户 ### 5.4 部门管理 - **部门层级管理**:支持多级部门结构的创建和管理 - **部门负责人设置**:自动为部门负责人分配管理权限 - **部门转移**:支持用户在不同部门间的转移 - **部门统计**:按部门统计用户和店铺数量 --- ## 6. 安全考虑 ### 6.1 防止权限提升 - **最小权限原则**:用户只获得必要的权限 - **权限审计**:定期审计权限配置 - **权限边界**:明确权限的边界 ### 6.2 防止数据泄露 - **数据加密**:加密敏感数据 - **访问控制**:严格控制数据访问 - **审计日志**:记录数据访问日志 ### 6.3 防止暴力破解 - **密码策略**:强密码要求 - **登录限制**:限制登录尝试次数 - **验证码**:使用验证码防止暴力破解 --- ## 7. 测试策略 ### 7.1 单元测试 - 测试权限验证逻辑 - 测试角色权限映射 - 测试数据级权限过滤 ### 7.2 集成测试 - 测试完整的权限流程 - 测试不同角色的访问控制 - 测试数据级权限的有效性 ### 7.3 安全测试 - 测试权限提升攻击 - 测试数据泄露风险 - 测试暴力破解防护 --- ## 8. 相关文档 - [Service_Design.md](./Service_Design.md) - [Data_Consistency.md](./Data_Consistency.md) - [API_Specs](./API_Specs/) --- *本文档基于服务设计文档,最后更新: 2026-03-18* --- ## 9. 组织层级与数据范围(Organization & Data Scope) ### 9.1 组织层级模型 **核心原则**: > ❗ 权限 = 能做什么(Permission) > ❗ 数据范围 = 能操作谁的数据(Scope) > 👉 两者必须同时存在 **多租户层级结构**: ``` 平台(Platform) ↓ 商户/公司(Tenant/Organization) ↓ 组织结构(Org Tree) ├── 部门A(主管) │ ├── 组A1(组长) │ │ ├── 员工1 │ │ └── 员工2 │ └── 组A2(组长) │ └── 员工3 └── 部门B(主管) └── ... ``` ### 9.2 数据范围定义 | 范围类型 | 英文 | 说明 | SQL过滤条件 | |---------|------|------|------------| | **SELF** | Self | 只看自己 | `WHERE created_by = {userId}` | | **TEAM** | Team | 看自己组 | `WHERE team_id IN ({userTeams})` | | **DEPT** | Department | 看自己部门 | `WHERE dept_id IN ({userDepts})` | | **ORG** | Organization | 看整个公司 | `WHERE tenant_id = {tenantId}` | | **ALL** | All | 全平台(超管) | 无过滤 | ### 9.3 数据范围实现 ```typescript interface DataScopeContext { userId: string; tenantId: string; orgId: string; scopeType: 'SELF' | 'TEAM' | 'DEPT' | 'ORG' | 'ALL'; } class DataScopeService { async buildScopeQuery(ctx: DataScopeContext): Promise { switch (ctx.scopeType) { case 'SELF': return { created_by: ctx.userId }; case 'TEAM': const teams = await this.getUserTeams(ctx.userId); return { team_id: { in: teams } }; case 'DEPT': const depts = await this.getUserDepts(ctx.userId); return { dept_id: { in: depts } }; case 'ORG': return { tenant_id: ctx.tenantId }; case 'ALL': return {}; } } } ``` ### 9.4 店铺角色权限 | 角色 | 描述 | 权限范围 | |------|------|----------| | **owner** | 拥有者 | 删除店铺、管理授权、管理成员、所有权限 | | **admin** | 管理员 | 管理商品、管理价格、管理订单、不可删除店铺 | | **operator** | 运营 | 刊登、改价、查看数据 | | **viewer** | 只读 | 查看数据、不可操作 | ### 9.5 授权模型 **核心原则**: > ❗ 授权属于店铺,不属于用户 > ❗ 店铺属于主体(Owner),用户只是被授权使用 **店铺授权结构**: ``` Organization(公司) ↓ Shop(店铺) ↓ Auth(授权) ↑ User(使用者) ``` **授权类型**: | 类型 | 适用平台 | 存储内容 | |------|----------|----------| | **API授权** | Shopify、Amazon | access_token, refresh_token, expire_time | | **Agent授权** | TikTok、Shopee | cookies, proxy, device_id, user_agent | ### 9.6 权限校验中间件 ```typescript const authorize = (requiredPermission: string) => { return async (req, res, next) => { const user = req.user; if (!user) { return res.status(401).json({ error: 'Unauthorized' }); } const hasPermission = await rbacService.checkPermission( user.id, requiredPermission, req.params.shopId ); if (!hasPermission) { return res.status(403).json({ error: 'Forbidden' }); } const scopeQuery = await dataScopeService.buildScopeQuery({ userId: user.id, tenantId: user.tenantId, orgId: user.orgId, scopeType: user.dataScope }); req.scopeQuery = scopeQuery; next(); }; }; ```