核心概念:主体、资源、环境、动作、策略——ABAC五要素详解
ABAC 的全称是 Attribute-Based Access Control,基于属性的访问控制。
说白了,就是通过一堆「属性」来判断「谁」能不能对「什么」做「某件事」。这五个要素,我习惯叫它们 ABAC 的「五官」——缺一个,权限判断就做不了。
我记得刚接触 ABAC 时,总把「主体」和「用户」划等号。后来踩了坑才明白,主体远不止是人。咱们一个一个拆开看。
1. 主体(Subject)——谁在发起请求?
主体就是动作的发起方。可以是用户、服务账号、设备,甚至是另一个系统。
你想想看,一个 IoT 场景里,传感器向服务器上报数据,传感器就是主体。一个微服务调用另一个微服务的 API,调用方服务也是主体。
主体的属性通常包括:
- 身份属性: 用户ID、角色、部门、职级
- 分组属性: 所属项目组、安全等级
- 上下文属性: 登录IP、设备指纹、会话有效期
2. 资源(Resource)——要访问什么东西?
资源就是被访问的对象。文档、数据库记录、API 接口、文件、甚至是一台服务器,都是资源。
我曾在项目中遇到一个需求:同一个文档,不同部门的人只能看到不同字段。这就是资源属性在起作用——文档的「所属部门」属性决定了可见范围。
资源的属性常见的有:
- 分类属性: 文档类型、数据敏感级别
- 归属属性: 创建者、所属部门、项目ID
- 状态属性: 是否已发布、是否归档、是否锁定
3. 环境(Environment)——在什么条件下?
环境是 ABAC 里最灵活、也最容易被忽略的要素。它描述的是「此时此刻」的上下文。
举个例子:
- 上班时间(9:00-18:00)允许访问,下班后禁止
- 从公司内网访问允许,从外网访问需要二次认证
- 系统处于维护模式时,所有写操作拒绝
环境的属性通常来自系统运行时:
| 环境属性 | 示例值 | 说明 |
|---|---|---|
| 当前时间 | 2025-03-21 14:30:00 | 用于时间窗口策略 |
| 客户端IP | 10.0.1.100 | 用于网络区域判断 |
| 系统运行模式 | normal / maintenance | 用于全局开关 |
| 风险等级 | low / medium / high | 动态风险评估结果 |
4. 动作(Action)——想做什么?
动作就是主体对资源执行的操作。CRUD 是最基本的,但实际场景远不止这些。
比如:
- 数据操作: 读取、写入、删除、导出
- 管理操作: 审批、授权、锁定、解锁
- 特殊操作: 打印、分享、下载、复制
动作本身也可以有属性。我记得有个金融项目,要求「金额超过 10000 元的转账需要主管审批」。这里的动作属性就是「转账金额」。
5. 策略(Policy)——规则是什么?
策略是 ABAC 的大脑。它把主体、资源、环境、动作这四个要素组合起来,输出一个决定:允许还是拒绝。
一个典型的策略结构长这样:
{
"policyId": "POLICY-001",
"description": "普通员工只能在工作时间查看自己部门的文档",
"effect": "deny", // 或 "allow"
"conditions": {
"subject": {
"role": "employee"
},
"resource": {
"type": "document",
"department": "{subject.department}" // 引用主体属性
},
"action": {
"name": "read"
},
"environment": {
"time": "09:00-18:00",
"dayOfWeek": ["Monday","Tuesday","Wednesday","Thursday","Friday"]
}
}
}
这里有个细节:{subject.department} 表示「当前主体的部门属性」。这种引用机制让策略可以复用,不用为每个部门写一条策略。
五要素的关系图
下面这张图展示了五要素如何协同工作:
从图上可以看得很清楚:策略引擎接收主体、资源、环境、动作四个维度的属性,再拿着策略里定义的规则,最终算出「允许」还是「拒绝」。
五要素的实战要点
最后,我总结几个实战中容易忽略的点:
- 属性来源要可靠。 主体属性来自认证系统,资源属性来自元数据服务,环境属性来自基础设施。不要信任客户端传过来的任何属性。
- 策略要支持属性引用。 像
{subject.department}这种动态引用,能大幅减少策略数量。 - 动作要细分。 别只写 read/write。把「导出」「打印」「分享」都定义成独立动作,策略才能精细控制。
- 环境属性要有时效性。 比如「登录时间超过 8 小时需要重新认证」,这个环境属性是动态计算的,不是静态值。