数据权限怎么设计:看自己/看团队/看全量(附字段级权限清单)
前言
数据权限是权限设计的核心。很多系统只做了功能权限(能不能操作),没做数据权限(能看哪些数据),导致数据泄露。这篇给你数据权限的完整设计方法。
一、数据权限3个层级
| 层级 | 范围 | SQL实现 | 适用场景 |
|---|---|---|---|
| 个人级 | 只能看自己的数据 | WHERE owner_id = 当前用户id | 普通用户 |
| 团队级 | 能看本团队的数据 | WHERE team_id = 当前用户team_id | 部门负责人 |
| 全量级 | 能看所有数据 | 无WHERE条件 | 管理员 |
二、字段级权限清单
| 字段 | 管理员 | 部门负责人 | 普通用户 |
|---|---|---|---|
| 用户姓名 | 明文 | 明文 | 明文 |
| 手机号 | 明文 | 脱敏(138****1234) | 不可见 |
| 身份证号 | 明文 | 不可见 | 不可见 |
| 银行卡号 | 脱敏 | 不可见 | 不可见 |
| 订单金额 | 明文 | 明文 | 明文 |
三、数据权限实现步骤
步骤1:确定数据范围
根据业务需求,确定每个角色能看到哪些数据。
示例:订单系统
- 普通用户:只能看自己的订单(WHERE user_id = 当前用户id)
- 客服:能看所有订单(无WHERE条件,但字段脱敏)
- 管理员:能看所有订单(无WHERE条件,字段明文)
步骤2:设计数据权限字段
在数据表中添加权限相关字段,用于数据过滤。
订单表(orders):
- id:订单ID
- user_id:订单所属用户(个人级权限)
- team_id:订单所属团队(团队级权限)
- department_id:订单所属部门(部门级权限)
- created_at:创建时间
查询逻辑:
- 个人级:WHERE user_id = 当前用户id
- 团队级:WHERE team_id = 当前用户team_id
- 部门级:WHERE department_id = 当前用户department_id
- 全量级:无WHERE条件
步骤3:实现数据权限过滤
在查询接口中,根据用户角色自动添加WHERE条件。
伪代码示例:
function getOrders(user) {
let query = "SELECT * FROM orders";
let where = [];
// 根据角色添加WHERE条件
if (user.role === '普通用户') {
where.push("user_id = " + user.id);
} else if (user.role === '团队负责人') {
where.push("team_id = " + user.team_id);
} else if (user.role === '部门负责人') {
where.push("department_id = " + user.department_id);
}
// 管理员不添加WHERE条件
if (where.length > 0) {
query += " WHERE " + where.join(" AND ");
}
return executeQuery(query);
}
步骤4:实现字段级权限
在接口返回前,根据角色对敏感字段进行脱敏或隐藏。
伪代码示例:
function formatOrder(order, user) {
// 根据角色处理敏感字段
if (user.role !== '管理员') {
// 手机号脱敏
if (order.phone) {
order.phone = maskPhone(order.phone);
}
// 身份证隐藏
if (user.role === '普通用户') {
delete order.id_card;
}
}
return order;
}
四、脱敏策略
脱敏规则
手机号脱敏:
- 原始:13812341234
- 脱敏:138****1234
- 规则:保留前3位和后4位
身份证号脱敏:
- 原始:110101199001011234
- 脱敏:110101********1234
- 规则:保留前6位和后4位
银行卡号脱敏:
- 原始:6222021234567890123
- 脱敏:6222 **** **** 0123
- 规则:保留前4位和后4位
姓名脱敏:
- 原始:张三
- 脱敏:张*
- 规则:保留姓氏
邮箱脱敏:
- 原始:zhangsan@qq.com
- 脱敏:z***n@qq.com
- 规则:保留首尾字符
脱敏实现
JavaScript示例:
function maskPhone(phone) {
if (!phone || phone.length < 7) return phone;
return phone.substring(0, 3) + '****' + phone.substring(phone.length - 4);
}
function maskIdCard(idCard) {
if (!idCard || idCard.length < 10) return idCard;
return idCard.substring(0, 6) + '********' + idCard.substring(idCard.length - 4);
}
function maskName(name) {
if (!name || name.length < 2) return name;
return name[0] + '*';
}
function maskEmail(email) {
if (!email || !email.includes('@')) return email;
const [local, domain] = email.split('@');
if (local.length <= 2) return email;
return local[0] + '***' + local[local.length - 1] + '@' + domain;
}
五、实际案例
案例1:电商订单系统
业务场景:用户下单后,不同角色需要看到不同的订单信息。
数据权限设计:
1. 数据范围:
- 买家:只能看自己的订单(WHERE buyer_id = 当前用户id)
- 卖家:只能看自己店铺的订单(WHERE shop_id = 当前用户shop_id)
- 平台客服:能看所有订单(无WHERE条件)
- 平台管理员:能看所有订单(无WHERE条件)
2. 字段权限:
| 字段 | 买家 | 卖家 | 客服 | 管理员 |
|------|------|------|------|--------|
| 订单号 | 明文 | 明文 | 明文 | 明文 |
| 买家姓名 | 明文 | 明文 | 明文 | 明文 |
| 买家手机 | 明文 | 脱敏 | 脱敏 | 明文 |
| 买家地址 | 明文 | 脱敏 | 脱敏 | 明文 |
| 订单金额 | 明文 | 明文 | 明文 | 明文 |
| 卖家信息 | 明文 | 明文 | 明文 | 明文 |
3. 导出权限:
- 买家:不可导出
- 卖家:可导出自己店铺订单(最多1000条,敏感字段脱敏)
- 客服:可导出所有订单(最多5000条,敏感字段脱敏)
- 管理员:可导出所有订单(最多10000条,敏感字段脱敏)
案例2:CRM客户管理系统
业务场景:销售团队管理客户,不同级别看到不同范围的客户数据。
数据权限设计:
1. 数据范围:
- 销售:只能看自己负责的客户(WHERE owner_id = 当前用户id)
- 销售经理:能看本团队的客户(WHERE team_id = 当前用户team_id)
- 销售总监:能看本部门的客户(WHERE department_id = 当前用户department_id)
- 管理员:能看所有客户(无WHERE条件)
2. 字段权限:
| 字段 | 销售 | 销售经理 | 销售总监 | 管理员 |
|------|------|---------|---------|--------|
| 客户名称 | 明文 | 明文 | 明文 | 明文 |
| 联系人 | 明文 | 明文 | 明文 | 明文 |
| 手机号 | 明文 | 脱敏 | 脱敏 | 明文 |
| 邮箱 | 明文 | 脱敏 | 脱敏 | 明文 |
| 公司地址 | 明文 | 明文 | 明文 | 明文 |
| 年收入 | 明文 | 明文 | 明文 | 明文 |
| 备注 | 明文 | 明文 | 明文 | 明文 |
3. 导出权限:
- 销售:可导出自己的客户(最多500条)
- 销售经理:可导出团队的客户(最多2000条)
- 销售总监:可导出部门的客户(最多5000条)
- 管理员:可导出所有客户(最多10000条)
案例3:在线教育平台
业务场景:学生、老师、管理员看到不同的课程和学习数据。
数据权限设计:
1. 数据范围:
- 学生:只能看自己的学习记录(WHERE student_id = 当前用户id)
- 老师:能看自己教授的课程的学生数据(WHERE teacher_id = 当前用户id)
- 机构管理员:能看本机构的所有数据(WHERE org_id = 当前用户org_id)
- 平台管理员:能看所有数据(无WHERE条件)
2. 字段权限:
| 字段 | 学生 | 老师 | 机构管理员 | 平台管理员 |
|------|------|------|-----------|-----------|
| 学生姓名 | 明文 | 明文 | 明文 | 明文 |
| 学生手机 | 明文 | 脱敏 | 脱敏 | 明文 |
| 学习进度 | 明文 | 明文 | 明文 | 明文 |
| 考试成绩 | 明文 | 明文 | 明文 | 明文 |
| 家长信息 | 明文 | 不可见 | 脱敏 | 明文 |
3. 导出权限:
- 学生:不可导出
- 老师:可导出自己课程的学生数据(最多1000条,敏感字段脱敏)
- 机构管理员:可导出本机构数据(最多5000条,敏感字段脱敏)
- 平台管理员:可导出所有数据(最多20000条,敏感字段脱敏)
六、PRD写法模板
完整PRD模板
数据权限设计:
1. 数据范围:
- 管理员:全量数据(无WHERE条件)
- 部门负责人:team_id = 当前用户team_id
- 普通用户:owner_id = 当前用户id
2. 字段权限:
| 字段 | 管理员 | 部门负责人 | 普通用户 |
|------|-------|-----------|---------|
| 手机号 | 明文 | 脱敏(138****1234) | 不可见 |
| 身份证 | 明文 | 不可见 | 不可见 |
| 银行卡 | 脱敏 | 不可见 | 不可见 |
| 姓名 | 明文 | 明文 | 明文 |
| 邮箱 | 明文 | 脱敏 | 脱敏 |
3. 导出权限:
- 管理员:可导出全量数据(最多10000条,敏感字段脱敏)
- 部门负责人:可导出本团队数据(最多5000条,敏感字段脱敏)
- 普通用户:不可导出
4. 权限校验:
- 查询时自动添加WHERE条件(后端统一处理)
- 导出时检查导出权限(接口校验)
- 敏感字段自动脱敏(接口返回前处理)
- 记录导出日志(谁、何时、导出多少条)
5. 技术实现:
- 数据权限:在数据访问层(DAO)统一处理
- 字段脱敏:在接口返回前统一处理
- 导出限制:接口层校验+限流
字段权限矩阵模板
字段权限矩阵(示例):
| 字段名称 | 字段类型 | 管理员 | 部门负责人 | 普通用户 | 备注 |
|---------|---------|-------|-----------|---------|------|
| 用户ID | 数字 | 明文 | 明文 | 明文 | 非敏感 |
| 用户姓名 | 文本 | 明文 | 明文 | 明文 | 非敏感 |
| 手机号 | 文本 | 明文 | 脱敏 | 不可见 | 敏感字段 |
| 身份证号 | 文本 | 明文 | 不可见 | 不可见 | 敏感字段 |
| 银行卡号 | 文本 | 脱敏 | 不可见 | 不可见 | 敏感字段 |
| 邮箱 | 文本 | 明文 | 脱敏 | 脱敏 | 敏感字段 |
| 地址 | 文本 | 明文 | 脱敏 | 不可见 | 敏感字段 |
| 创建时间 | 日期 | 明文 | 明文 | 明文 | 非敏感 |
| 更新时间 | 日期 | 明文 | 明文 | 明文 | 非敏感 |
七、常见错误
错误1:只做功能权限,不做数据权限
问题:用户能看到"订单列表"页面,但能看到所有订单,导致数据泄露。
❌ 错误示例:
用户A登录后,访问订单列表,能看到所有用户的订单。
✅ 正确示例:
用户A登录后,访问订单列表,只能看到自己的订单(WHERE user_id = A)。
错误2:前端控制数据权限
问题:只在前端过滤数据,后端接口返回全量数据,可以通过接口直接获取。
❌ 错误示例:
前端:只显示自己的订单
后端:返回所有订单(前端过滤)
问题:用户可以直接调用接口获取所有订单。
✅ 正确示例:
前端:显示订单列表
后端:根据用户角色自动过滤(WHERE user_id = 当前用户id)
错误3:敏感字段不脱敏
问题:所有角色都能看到敏感字段的明文,导致隐私泄露。
❌ 错误示例:
客服查看订单,能看到买家的完整手机号、身份证号。
✅ 正确示例:
客服查看订单,手机号显示为138****1234,身份证号不显示。
错误4:导出不限制
问题:导出功能没有权限校验和数量限制,可以导出全量数据。
❌ 错误示例:
普通用户也可以导出订单,且没有数量限制。
✅ 正确示例:
- 普通用户:不可导出
- 管理员:可导出,但最多10000条,且敏感字段脱敏
错误5:数据权限字段缺失
问题:数据表中没有权限相关字段(如owner_id、team_id),无法实现数据权限。
❌ 错误示例:
订单表只有id、amount、status,没有user_id字段,无法区分订单归属。
✅ 正确示例:
订单表包含id、user_id、team_id、amount、status等字段,支持多层级数据权限。
错误6:脱敏规则不一致
问题:不同接口、不同页面对同一字段的脱敏规则不一致。
❌ 错误示例:
订单列表:手机号显示138****1234
订单详情:手机号显示13812341234(不一致)
✅ 正确示例:
所有接口统一使用脱敏函数,确保脱敏规则一致。
八、最佳实践
实践1:后端统一处理
数据权限和字段脱敏都在后端统一处理,前端只负责展示。
✓ 正确做法:
- 数据权限:在数据访问层(DAO)统一添加WHERE条件
- 字段脱敏:在接口返回前统一处理
- 前端:只负责展示,不做权限判断
✗ 错误做法:
- 前端过滤数据
- 前端判断权限
- 前端做脱敏处理
实践2:权限配置化
将数据权限规则配置化,不要硬编码在代码中。
✓ 正确做法:
配置文件(permission.yaml):
data_permission:
admin: "*" # 全量
manager: "team_id = :team_id" # 团队级
user: "owner_id = :user_id" # 个人级
✗ 错误做法:
代码中硬编码:
if (user.role === 'admin') {
// 全量
} else if (user.role === 'manager') {
// 团队级
}
实践3:记录操作日志
记录所有数据访问和导出操作,便于审计和追溯。
日志记录内容:
- 操作人:用户ID、用户名、角色
- 操作时间:精确到秒
- 操作类型:查询、导出、删除等
- 操作对象:表名、数据ID、数据范围
- 操作结果:成功、失败、原因
实践4:定期权限审计
定期检查权限配置,确保权限设置正确,没有越权问题。
审计检查项:
- 数据权限WHERE条件是否正确
- 字段脱敏规则是否生效
- 导出权限是否限制
- 是否有越权访问日志
- 敏感字段是否泄露
实践5:性能优化
数据权限查询要优化性能,避免全表扫描。
性能优化建议:
- 在权限字段上建立索引(如user_id、team_id)
- 避免复杂的子查询
- 使用分页查询,避免一次查询大量数据
- 缓存用户权限信息,减少数据库查询
九、FAQ
Q1:数据权限在哪里实现?
答:建议在后端统一实现。前端只负责展示,不要依赖前端做权限控制。
实现位置:
- 数据访问层(DAO):在查询时自动添加WHERE条件
- 服务层(Service):在业务逻辑中校验数据权限
- 接口层(Controller):在接口返回前处理字段脱敏
为什么不能在前端实现:
- 前端代码可以被修改,无法保证安全性
- 用户可以直接调用接口,绕过前端限制
- 前端只负责用户体验优化,不能作为安全控制
Q2:脱敏在哪里做?
答:建议在接口返回前脱敏。数据库存明文,接口返回时根据角色脱敏。
脱敏时机:
- 数据库:存储明文,便于查询和统计
- 接口返回:根据角色脱敏,保护隐私
- 日志记录:敏感字段脱敏,防止日志泄露
为什么不在数据库脱敏:
- 数据库存储明文,便于数据分析和查询
- 脱敏在接口层统一处理,灵活可控
- 不同角色可能需要不同的脱敏规则
Q3:导出时要脱敏吗?
答:建议脱敏。即使是管理员,导出的敏感字段也应该脱敏,防止数据泄露。
导出脱敏规则:
- 管理员导出:敏感字段脱敏,防止文件泄露
- 普通用户导出:敏感字段不导出或脱敏
- 导出日志:记录导出人、时间、数量,便于追溯
导出限制:
- 限制导出数量(如最多10000条)
- 限制导出频率(如每天最多导出3次)
- 导出文件加密或设置密码
Q4:数据权限字段怎么设计?
答:根据业务需求,在数据表中添加权限相关字段。
常见字段:
- owner_id:数据所有者(个人级权限)
- team_id:所属团队(团队级权限)
- department_id:所属部门(部门级权限)
- org_id:所属组织(组织级权限)
设计原则:
- 根据业务需求选择字段,不要过度设计
- 在权限字段上建立索引,提高查询性能
- 字段命名要清晰,便于理解和维护
Q5:多层级数据权限怎么实现?
答:使用多字段组合,支持多层级权限控制。
实现方式:
- 字段组合:使用owner_id、team_id、department_id等字段组合
- 权限继承:上级可以看到下级的数据(如部门负责人可以看到团队数据)
- 权限合并:用户有多个角色时,权限取并集
示例:订单表
- owner_id:订单所有者(个人级)
- team_id:订单所属团队(团队级)
- department_id:订单所属部门(部门级)
查询逻辑:
- 个人级:WHERE owner_id = 当前用户id
- 团队级:WHERE team_id = 当前用户team_id OR owner_id = 当前用户id
- 部门级:WHERE department_id = 当前用户department_id OR team_id = 当前用户team_id OR owner_id = 当前用户id
Q6:数据权限会影响性能吗?
答:可能会,但可以通过优化避免。
性能优化方法:
- 索引优化:在权限字段上建立索引(如user_id、team_id)
- 查询优化:避免复杂的子查询,使用JOIN代替
- 缓存优化:缓存用户权限信息,减少数据库查询
- 分页查询:使用分页,避免一次查询大量数据
性能监控:
- 监控查询耗时,发现慢查询及时优化
- 监控数据库连接数,避免连接池耗尽
- 定期分析查询日志,优化高频查询
Q7:如何测试数据权限?
答:使用不同角色的账号测试,验证数据权限是否正确。
测试用例:
- 个人级权限:普通用户只能看到自己的数据
- 团队级权限:团队负责人能看到团队的数据
- 全量级权限:管理员能看到所有数据
- 越权测试:尝试访问无权限的数据,应该被拒绝
- 字段脱敏:验证敏感字段是否正确脱敏
测试工具:
- 使用Postman、JMeter等工具测试接口
- 使用不同角色的测试账号
- 检查接口返回的数据是否符合权限规则
工具入口
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐
所有评论(0)