企业级权限系统怎么设计二:权限点模型

85 阅读3分钟

企业级权限系统怎么设计二:权限点模型

这篇文章也是我在实施项目中的一些思考和复盘。

上回说到,我们将审批相关的权限逻辑从主权限模型中独立出来。那接下来的问题是:角色和功能之间的权限关系,应该怎么设计?

我们目前是通过“功能”来控制权限,但“功能”到底该定义到什么粒度?应该存哪些信息?这类问题,网上有很多权限模型可以参考,都挺清晰,但一旦需求复杂起来,如果没有足够的前瞻性设计,盲目“抄模板”或者硬造轮子,就容易出问题:

  • 权限控制变得难以扩展、难以维护;
  • 无法覆盖实际业务;
  • 甚至上线后还可能出生产事故。

一、功能权限的颗粒度不足

在上一篇文章《企业级权限系统怎么设计 —— 优秀领域模型的价值》中我们提到,部分业务场景下需要更细的权限控制,比如“查询”、“经办”、“复核”等。

我们当时的方案是直接在功能权限表中加标志位:

can_view、can_execute、can_review

这种方式虽然简单,但本质上是直接用业务结构建模,缺乏抽象,导致几个问题:

  • 无法控制到按钮或 API 的级别
  • 策略配置变得受限(如不同渠道的差异化权限)

二、抽象:从功能标志位到权限点

我们思考一下,需求的关键点是:“权限”是为了控制,而“菜单”只是为了展示。所以我们需要将这两者分离,让控制逻辑独立于前端结构。

于是我们引入了一个新的抽象层 —— 权限点(Permission Point)

概念厘清

  1. 功能:指为用户提供的业务能力,通常对应一个完整的流程或操作入口。
  2. 菜单:是功能的前端表现形式,可以是导航菜单、页面按钮,或其他 UI 元素。
  3. 权限点:是权限控制的最小单位,通常对应一个具体的动作,比如“创建转账订单”、“复核转账订单”等。

抽象后的设计原则

  • 前端:根据权限点决定菜单和按钮是否展示;
  • 后端:根据权限点决定请求是否放行;
  • 功能:作为权限点的组织容器,方便管理和启用;
  • 菜单:仅作为展示载体,通过权限点绑定控制可见性。

三、权限点模型设计

设计模型如下:

erDiagram
    CUSTOMER ||--o{ USER : "拥有用户"
    CUSTOMER ||--o{ ACCOUNT : "拥有账户"
    CUSTOMER ||--o{ ROLE : "拥有角色"
    CUSTOMER ||--o{ CUSTOMER_FUNCTION : "开通功能"

    USER ||--o{ USER_ROLE : "绑定角色"
    USER ||--o{ USER_ACCOUNT : "管理账户"

    ACCOUNT ||--o{ USER_ACCOUNT : ""
    
    ROLE ||--o{ USER_ROLE : ""
    ROLE ||--o{ ROLE_PERMISSION : "绑定权限点"

    FUNCTION ||--o{ PERMISSION_POINT : "包含权限点"
    FUNCTION ||--o{ CUSTOMER_FUNCTION : ""

    PERMISSION_POINT ||--o{ ROLE_PERMISSION : ""
    PERMISSION_POINT ||--o{ MENU_PERMISSION : ""

    MENU ||--o{ MENU_PERMISSION : ""
带字段模型
erDiagram
    %% 客户实体
    CUSTOMER {
        int id PK "客户ID"
        string name "客户名称"
    }

    %% 用户实体
    USER {
        int id PK "用户ID"
        int customer_id FK "所属客户ID"
        string username "用户名"
        string password "密码"
    }

    %% 账户实体
    ACCOUNT {
        int id PK "账户ID"
        int customer_id FK "所属客户ID"
        string account_number "账户编号"
    }

    %% 角色实体
    ROLE {
        int id PK "角色ID"
        int customer_id FK "所属客户ID"
        string name "角色名称"
    }

    %% 功能实体(功能点集合容器)
    FUNCTION {
        int id PK "功能ID"
        string name "功能名称"
        string function_type "功能类型:基础管理/业务功能"
    }

    %% 客户功能开通(功能点约束范围)
    CUSTOMER_FUNCTION {
        int id PK "客户功能ID"
        int customer_id FK "所属客户ID"
        int function_id FK "功能ID"
    }

    %% 权限点实体(最小控制单位)
    PERMISSION_POINT {
        int id PK "权限点ID"
        string code "权限点编码"
        string name "权限点名称"
        int function_id FK "所属功能"
        string operation_type "操作类型(查询/经办/复核)"
        string channel "适用渠道"
        boolean is_sensitive "是否敏感操作"
        boolean requires_approval "是否需要审批"
    }

    %% 角色-权限点关系(授权)
    ROLE_PERMISSION {
        int role_id FK "角色ID"
        int permission_point_id FK "权限点ID"
    }

    %% 用户-角色关系
    USER_ROLE {
        int user_id FK "用户ID"
        int role_id FK "角色ID"
    }

    %% 用户-账户关系
    USER_ACCOUNT {
        int user_id FK "用户ID"
        int account_id FK "账户ID"
    }

    %% 菜单实体(用于前端控制)
    MENU {
        int id PK "菜单ID"
        string name "菜单名称"
        int parent_id "父菜单ID"
        string route "路由路径"
    }

    %% 菜单绑定权限点
    MENU_PERMISSION {
        int menu_id FK "菜单ID"
        int permission_point_id FK "权限点ID"
    }

    %% 实体关系定义
    CUSTOMER ||--o{ USER : "拥有用户"
    CUSTOMER ||--o{ ACCOUNT : "拥有账户"
    CUSTOMER ||--o{ ROLE : "拥有角色"
    CUSTOMER ||--o{ CUSTOMER_FUNCTION : "开通功能"

    USER ||--o{ USER_ROLE : "绑定角色"
    USER ||--o{ USER_ACCOUNT : "管理账户"

    ACCOUNT ||--o{ USER_ACCOUNT : ""
    
    ROLE ||--o{ USER_ROLE : ""
    ROLE ||--o{ ROLE_PERMISSION : "绑定权限点"

    FUNCTION ||--o{ PERMISSION_POINT : "包含权限点"
    FUNCTION ||--o{ CUSTOMER_FUNCTION : ""

    PERMISSION_POINT ||--o{ ROLE_PERMISSION : ""
    PERMISSION_POINT ||--o{ MENU_PERMISSION : ""

    MENU ||--o{ MENU_PERMISSION : ""
    

核心表说明

表名说明
PERMISSION_POINT权限点,最小控制单位,如:TRANSFER_QUERY、TRANSFER_CREATE、TRANSFER_APPROVE
ROLE_PERMISSION角色与权限点的绑定关系
CUSTOMER_FUNCTION控制某客户是否启用某功能(影响其下属权限点是否生效)
MENU_PERMISSION菜单与权限点的绑定关系,决定前端菜单是否展示

四、模型优势与实践建议

✦ 多渠道适配

权限点中加上 channel 字段,可区分不同渠道(如 PC / H5 / App)下的权限差异。

✦ 敏感控制支持

支持通过 is_sensitiverequires_approval 字段,控制是否为敏感操作、是否需要审批等策略扩展。

✦ 降低客户配置复杂度

为客户预设默认角色和权限包,不必手动分配每一个权限点,提升开通效率。

✦ 菜单渲染统一化

前端登录后根据权限点统一返回菜单结构,可减少各渠道重复开发。

✦ 权限校验后移至后端

前端仅做展示控制,实际访问逻辑必须依赖后端验证权限点,避免“靠 UI 控制安全”的假象。