一、RBAC 核心模型
RBAC 的核心思想是「用户关联角色,角色关联权限」,避免直接为用户分配大量细粒度权限。
| 实体 | 说明 | 示例 |
|---|---|---|
| 用户(User) | 系统登录账号 | zhangsan |
| 角色(Role) | 岗位或职责的抽象 | 销售经理、库管员 |
| 资源(Resource) | 受保护的实体或菜单 | 订单、库存报表 |
| 操作(Action) | 对资源的具体行为 | 查看、编辑、删除、导出 |
二、数据库表结构设计
采用「用户-角色多对多」与「角色-权限多对多」的经典设计,支持灵活的权限组合。
schema.sql
/* 用户表 */ CREATE TABLE users ( id BIGINT PRIMARY KEY, username VARCHAR(64) NOT NULL, dept_id BIGINT, /* 所属部门,用于数据范围 */ /* ... */ ); /* 角色表 */ CREATE TABLE roles ( id BIGINT PRIMARY KEY, name VARCHAR(64) NOT NULL, code VARCHAR(64) UNIQUE /* 程序内标识 */ ); /* 权限表:资源 + 操作 */ CREATE TABLE permissions ( id BIGINT PRIMARY KEY, resource VARCHAR(64) NOT NULL, /* 如 "order" */ action VARCHAR(32) NOT NULL, /* 如 "view", "edit" */ UNIQUE(resource, action) ); /* 关联表 */ CREATE TABLE user_roles (user_id BIGINT, role_id BIGINT, PRIMARY KEY(user_id, role_id)); CREATE TABLE role_permissions (role_id BIGINT, perm_id BIGINT, PRIMARY KEY(role_id, perm_id));
三、字段级权限:控制「能看到什么列」
在列表页与详情页中,某些敏感字段(如成本价、客户手机号)需要对特定角色隐藏。实现方式是在权限表中增加 field 维度。
field_permission.sql
/* 扩展权限表支持字段级控制 */ ALTER TABLE permissions ADD COLUMN field VARCHAR(64) DEFAULT '*'; /* 示例:销售角色只能看订单的部分字段 */ INSERT INTO permissions(resource, action, field) VALUES ('order', 'view', '*'), /* 全部字段 */ ('order', 'view', 'cost_price'), /* 单独控制 */ ('order', 'view', 'profit'); /* 查询时过滤字段 */ SELECT column_name FROM information_schema.columns WHERE table_name = 'orders' AND column_name NOT IN ( SELECT field FROM permissions p JOIN role_permissions rp ON p.id = rp.perm_id WHERE rp.role_id = ? AND p.resource = 'order' AND p.action = 'view' AND p.field != '*' );
实践建议: 字段级权限在应用层实现更灵活。后端返回完整数据,前端根据权限掩码决定渲染哪些列;或后端用 Jackson 的 @JsonView / 序列化过滤器动态裁剪。
四、数据范围隔离:控制「能看到什么行」
同一张表,不同用户能看到的数据范围不同。常见规则有:全部、本部门、本人、自定义。
data_scope.java(伪代码)
public enum DataScope { ALL, DEPT_ONLY, SELF, CUSTOM } public String buildDataScopeFilter(Long userId, String tableAlias) { User user = userService.getById(userId); DataScope scope = roleService.getMaxDataScope(userId); switch (scope) { case ALL: return "1=1"; case DEPT_ONLY: return alias + ".dept_id = " + user.getDeptId(); case SELF: return alias + ".created_by = " + userId; case CUSTOM: return customScopeService.getSql(userId, alias); default: return alias + ".created_by = " + userId; } }
五、审计日志:谁在什么时候做了什么
审计日志是等保合规与事后追溯的必备能力。记录「谁、何时、对哪张表、哪条记录、做了什么、变更前后值」。
audit_log.sql
CREATE TABLE audit_logs ( id BIGINT PRIMARY KEY AUTO_INCREMENT, user_id BIGINT NOT NULL, username VARCHAR(64), operation VARCHAR(32), /* CREATE / UPDATE / DELETE / LOGIN */ resource VARCHAR(64), /* 表名或模块名 */ record_id VARCHAR(64), /* 被操作记录主键 */ old_value JSON, /* 变更前(UPDATE/DELETE) */ new_value JSON, /* 变更后(CREATE/UPDATE) */ ip VARCHAR(40), user_agent VARCHAR(255), created_at DATETIME DEFAULT NOW() ); /* 按用户 + 时间范围查询,支撑审计报表 */ CREATE INDEX idx_audit_user_time ON audit_logs(user_id, created_at);
六、总结
RBAC 不是简单的「增删改查」开关,而是一套分层体系:菜单权限控制导航入口,操作权限控制按钮显示,字段权限控制列可见性,数据范围控制行过滤。四层叠加,才能满足企业级系统的复杂安全需求。