在字节跳动这样的大型企业,中后台系统数据权限设计直接决定了系统的安全性与可维护性。从部门隔离、角色授权,到字段级别的精细控制,一个好的权限系统不仅要灵活,更要高性能、易扩展。本篇将从设计思路出发,用 Node.js 演示一个支持“部门 + 角色 + 字段级”的数据权限系统原型。
🧠 一、数据权限问题的典型场景
| 场景 | 示例 |
|---|
| 按部门隔离 | 只能看到自己部门创建的数据 |
| 按角色控制 | 主管能审批,普通员工只能查看 |
| 按字段限制 | 财务字段只能财务角色可见 |
| 动态条件限制 | 只能看“自己创建”或“状态=进行中”的数据 |
🔍 二、字节跳动数据权限系统设计理念
| 模块 | 设计策略 |
|---|
| 权限数据模型 | 用户 → 角色 → 权限策略(表达式) |
| 策略类型 | Row 级(数据行)+ Field 级(字段)权限 |
| 多维条件组合 | 支持组织架构、岗位级别、自定义字段 |
| 动态 SQL 拼接 | 动态生成 WHERE + SELECT 列过滤 |
| 可配置化引擎 | 支持运营界面定义权限表达式,无需研发介入 |
⚙️ 三、代码实战:Node.js 实现简易数据权限校验
1. 模拟权限策略定义
const userRoles = {
u1001: ['employee'],
u1002: ['leader', 'finance'],
};
const permissionRules = {
employee: {
dataFilter: (row, userId) => row.creatorId === userId,
fields: ['id', 'title', 'status'],
},
leader: {
dataFilter: () => true,
fields: ['id', 'title', 'status', 'creatorId'],
},
finance: {
fields: ['id', 'title', 'cost'],
},
};
2. 权限合并策略
function mergeUserPermission(userId) {
const roles = userRoles[userId] || [];
const merged = {
dataFilter: (row) => roles.some(r => permissionRules[r]?.dataFilter?.(row, userId)),
fields: Array.from(new Set(roles.flatMap(r => permissionRules[r]?.fields || []))),
};
return merged;
}
3. 查询数据并应用权限控制
const dataSource = [
{ id: 1, title: '预算申请', status: '进行中', cost: 8000, creatorId: 'u1001' },
{ id: 2, title: '发票审批', status: '已完成', cost: 5000, creatorId: 'u1002' },
]
function getUserData(userId) {
const perm = mergeUserPermission(userId)
return dataSource
.filter(row => perm.dataFilter(row))
.map(row => {
const filtered = {}
perm.fields.forEach(f => filtered[f] = row[f])
return filtered
})
}
4. 使用示例
console.log('u1001 可见数据:', getUserData('u1001'));
console.log('u1002 可见数据:', getUserData('u1002'));
🧩 四、工程化扩展建议
| 功能 | 推荐实现方式 |
|---|
| 可视化策略配置 | 用表达式解析器支持逻辑表达式:如“部门=销售 && 状态=审批中” |
| 字段脱敏支持 | 定义字段脱敏规则:如显示手机号仅保留前三后四位 |
| 数据隔离下发 | 查询前拼接 WHERE,查询后按字段裁剪 |
| 高性能控制 | 将权限规则预编译为 SQL Fragment 缓存 |
✍️ 五、总结与思考
- 数据权限不是简单的“角色能看”,而是动态、多维、可组合的规则系统
- 字节跳动将权限能力沉淀为“策略引擎 + 权限模型”,支持任意系统接入
- 你可以从简单字段过滤开始,逐步升级到策略表达式 + 动态 SQL 拼接
🎁 拓展推荐