如何搭建类AWS IAM的权限模型?求通用实现方案及细节
参照IAM模型自研权限系统的通用实现指南
我之前在自研企业系统时,也遇到过和你一样的问题——搜IAM相关内容全是AWS官方教程,找不到通用落地的技术细节。结合实际落地经验,给你梳理下通用术语、数据库设计和核心实现要点:
一、通用权限模型术语(对应AWS IAM概念)
先把AWS的概念映射到通用场景,避免被云服务绑定:
- Principals(主体):通用叫「权限主体」,指能发起操作的实体,包括:
- 终端用户(User)
- 角色(Role):可被多个主体继承的权限集合(比如「管理员」「编辑者」)
- 服务账号(Service Account):系统内部服务/脚本发起请求的身份
- Resources(资源):通用叫「受保护资源」,是系统中需要控制访问的对象,比如订单、文档、API接口、菜单等
- Actions(操作):通用叫「资源操作」,是主体对资源能执行的具体动作,比如「创建订单」「读取文档」「删除用户」
- Policy(权限策略):定义「主体-资源-操作」关系的规则集合,核心是**允许(Allow)/拒绝(Deny)**的判断逻辑
- Assignment(权限绑定):将策略/角色关联到主体的关系(比如把「编辑者」角色绑定给用户张三)
二、具体实现细节
1. 数据库结构设计
推荐用关系型数据库(MySQL/PostgreSQL)实现,核心表结构如下:
(1)主体表 principals
CREATE TABLE principals ( id INT PRIMARY KEY AUTO_INCREMENT, type ENUM('user', 'role', 'service') NOT NULL, -- 区分主体类型 identifier VARCHAR(100) UNIQUE NOT NULL, -- 用户ID/角色名称/服务账号ID name VARCHAR(100) NOT NULL, -- 显示名称 description TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP );
(2)资源表 resources
支持层级资源(比如文件夹下的文档):
CREATE TABLE resources ( id INT PRIMARY KEY AUTO_INCREMENT, resource_type VARCHAR(50) NOT NULL, -- 资源类型:order/document/menu resource_id VARCHAR(100) UNIQUE NOT NULL, -- 资源唯一标识(比如订单ID) parent_resource_id INT NULL, -- 父资源ID(用于层级结构) owner_principal_id INT NULL, -- 资源所属主体ID attributes JSON, -- 资源扩展属性(比如订单状态、文档大小) created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (parent_resource_id) REFERENCES resources(id), FOREIGN KEY (owner_principal_id) REFERENCES principals(id) );
(3)操作表 actions
统一管理所有允许的操作,避免硬编码:
CREATE TABLE actions ( id INT PRIMARY KEY AUTO_INCREMENT, action_key VARCHAR(100) UNIQUE NOT NULL, -- 操作标识:比如 'order:create' 'document:read' resource_type VARCHAR(50) NOT NULL, -- 关联资源类型 name VARCHAR(100) NOT NULL, -- 操作名称:「创建订单」「读取文档」 description TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP );
(4)策略表 policies 和策略语句表 policy_statements
策略是多个权限规则的集合,用拆分表避免冗余:
CREATE TABLE policies ( id INT PRIMARY KEY AUTO_INCREMENT, policy_name VARCHAR(100) UNIQUE NOT NULL, description TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE policy_statements ( id INT PRIMARY KEY AUTO_INCREMENT, policy_id INT NOT NULL, effect ENUM('Allow', 'Deny') NOT NULL, -- 优先Deny(和IAM一致) principal_id INT NULL, -- 关联主体ID,NULL表示所有主体 resource_condition VARCHAR(200), -- 资源匹配条件:比如 'resource_type = "order" AND owner_principal_id = ${principal_id}' action_ids TEXT, -- 关联操作ID,用逗号分隔(或用中间表多对多) created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (policy_id) REFERENCES policies(id), FOREIGN KEY (principal_id) REFERENCES principals(id) );
(5)角色绑定表 role_assignments
实现主体继承角色权限:
CREATE TABLE role_assignments ( id INT PRIMARY KEY AUTO_INCREMENT, principal_id INT NOT NULL, -- 被绑定的主体(比如用户) role_id INT NOT NULL, -- 绑定的角色(来自principals表,type='role') effective_start DATETIME DEFAULT CURRENT_TIMESTAMP, effective_end DATETIME NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (principal_id) REFERENCES principals(id), FOREIGN KEY (role_id) REFERENCES principals(id), UNIQUE KEY (principal_id, role_id) );
2. Actions的存储与管理
两种主流方案,根据系统复杂度选择:
- 枚举式存储(推荐):就是上面的
actions表,所有操作提前定义好,优点是:- 便于权限审计和管理(能清晰看到所有可授权操作)
- 避免非法操作请求(只有表中存在的action才允许判断)
- 命名规范统一:建议用「
资源类型:操作名称」格式,比如order:create、document:update,和IAM的命名逻辑对齐但更通用
- 动态匹配式存储:适合操作灵活、频繁新增的系统,允许用通配符(比如
order:*表示所有订单操作),可以把action直接存在策略语句中,不用单独建表,但要注意:- 需实现通配符匹配逻辑(比如用正则表达式)
- 要做权限缓存,避免每次请求都做字符串匹配影响性能
3. 权限判断核心逻辑
和IAM的判断逻辑一致,步骤如下:
- 收集主体的所有有效策略:包括直接绑定到主体的策略,以及主体继承的角色所关联的策略
- 匹配请求的资源和操作:
- 提取请求中的
principal_id、resource_type/resource_id、action_key - 遍历所有策略语句,筛选出匹配当前主体、资源、操作的语句
- 提取请求中的
- 优先判断Deny:如果存在任何一条
effect=Deny的匹配语句,直接拒绝请求 - 判断Allow:如果存在至少一条
effect=Allow的匹配语句,允许请求;否则拒绝
补充条件判断:比如要实现「用户只能操作自己的订单」,可以在resource_condition中写owner_principal_id = ${principal_id},判断时把${principal_id}替换为当前主体的ID,执行条件校验。
三、实践建议
- 权限缓存:把主体的权限集合(比如允许的
action_key和资源范围)缓存到Redis中,减少数据库查询次数,提升判断性能 - 最小权限原则:给主体分配刚好够用的权限,避免过度授权
- 审计日志:记录所有权限判断请求(主体、资源、操作、结果),便于排查权限问题和合规审计
- 权限测试:在系统上线前,针对不同角色/用户进行权限测试,确保逻辑符合预期
内容的提问来源于stack exchange,提问作者VBAHole




