一、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 不是简单的「增删改查」开关,而是一套分层体系:菜单权限控制导航入口,操作权限控制按钮显示,字段权限控制列可见性,数据范围控制行过滤。四层叠加,才能满足企业级系统的复杂安全需求。