feat(types): 添加express.d.ts类型引用 style: 格式化express.d.ts中的接口定义 refactor: 移除未使用的AntFC类型导入 chore: 删除自动生成的.umi-production文件 feat: 添加店铺管理相关表和初始化脚本 docs: 更新安全规则和交互指南文档 refactor: 统一使用FC类型替代React.FC perf: 优化图表组件导入方式 style: 添加.prettierrc配置文件 refactor: 调整组件导入顺序和结构 feat: 添加平台库存管理路由 fix: 修复订单同步时的库存检查逻辑 docs: 更新RBAC设计和租户管理文档 refactor: 优化部门控制器代码
13 KiB
13 KiB
权限系统设计文档 (Crawlful Hub)
定位:Crawlful Hub 权限系统设计文档 - 定义基于角色的访问控制(RBAC)和数据级权限。 更新日期: 2026-03-18 最高优先级参考: 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 权限分配
示例:
// 角色权限映射
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过滤 - 确保跨租户操作的安全性
示例:
// 数据访问层
class ProductRepository {
async findAll(tenantId: string, params?: any): Promise<Product[]> {
return await Product.findAll({
where: {
tenantId,
...params
}
});
}
async findById(id: string, tenantId: string): Promise<Product | null> {
return await Product.findOne({
where: {
id,
tenantId
}
});
}
}
3.2.2 店铺隔离
定义:同一租户下不同店铺的数据相互隔离
实现:
- 在相关数据表中添加
shopId字段 - 在查询中根据用户的店铺权限过滤
- 确保用户只能访问自己有权限的店铺数据
示例:
// 权限服务
class RBACService {
async checkShopAccess(userId: string, shopId: string): Promise<boolean> {
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<Order[]> {
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 字段级权限
定义:基于字段的访问控制
实现:
- 定义字段级权限规则
- 在返回数据时过滤敏感字段
- 确保用户只能看到和修改自己有权限的字段
示例:
// 字段级权限
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 路由权限验证
实现:
- 创建权限验证中间件
- 在路由中应用中间件
- 验证用户是否有权限访问资源
示例:
// 权限中间件
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 服务层验证
适用场景:业务逻辑权限验证
实现:
- 在服务层检查权限
- 验证用户是否有权限操作资源
- 验证用户是否有权限访问数据
示例:
async updateProduct(productId: string, updates: Partial<Product>, userId: string): Promise<Product> {
// 检查操作权限
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. 相关文档
本文档基于服务设计文档,最后更新: 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 数据范围实现
interface DataScopeContext {
userId: string;
tenantId: string;
orgId: string;
scopeType: 'SELF' | 'TEAM' | 'DEPT' | 'ORG' | 'ALL';
}
class DataScopeService {
async buildScopeQuery(ctx: DataScopeContext): Promise<WhereClause> {
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 权限校验中间件
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();
};
};