策略结构:策略的组成(Effect、Condition、Target)
策略是ABAC规则引擎的核心执行单元。说白了,它就是一条「如果…那么…」的逻辑判断。我在做第一个ABAC项目时,把策略想得太复杂了,结果写出来的策略又臭又长。后来才明白,一个完整的策略其实就三个部分:Target(目标)、Condition(条件)、Effect(效果)。
嗯,咱们一个一个拆开看。
4.1 策略的三要素
先给个直观的对比。你想想看,策略就像一道门禁规则:
| 要素 | 门禁类比 | ABAC中的含义 |
|---|---|---|
| Target | 这道门管谁?管哪个区域? | 策略适用的资源、主体、动作范围 |
| Condition | 进门需要什么条件? | 基于属性的布尔逻辑判断 |
| Effect | 允许进还是禁止进? | Permit(允许)或 Deny(拒绝) |
我个人习惯把策略写成这样:
策略 = Target + Condition → Effect
Target先过滤掉不相关的请求,Condition做精细判断,Effect给出最终结论。顺序不能乱。
4.2 Target:策略的「门面」
Target的作用是快速判断「这条策略跟当前请求有没有关系」。我在项目中遇到过一种情况:系统里有上千条策略,每次请求都要遍历一遍,性能惨不忍睹。后来加了Target预过滤,效率直接翻倍。
一个典型的Target包含三个维度:
- 主体(Subject):谁在操作?比如用户角色、部门、安全等级。
- 资源(Resource):操作什么?比如文档类型、API路径、数据分类。
- 动作(Action):做什么?比如读取、写入、删除、审批。
举个例子:
{
"target": {
"subject": { "role": "manager" },
"resource": { "type": "financial_report" },
"action": { "name": "read" }
}
}
这条Target的意思是:只有「经理」角色对「财务报表」的「读取」操作,才可能匹配这条策略。其他请求直接跳过,不用往下走了。
4.3 Condition:策略的「大脑」
Condition是策略里最灵活的部分。它用布尔表达式对属性做精细判断。Target只是粗筛,Condition才是真正的决策逻辑。
Condition支持的操作符我列一下:
| 操作符 | 说明 | 示例 |
|---|---|---|
equals |
等于 | user.department == "finance" |
notEquals |
不等于 | user.level != "guest" |
greaterThan |
大于 | resource.amount > 10000 |
lessThan |
小于 | resource.amount < 50000 |
in |
属于集合 | user.role in ["admin","manager"] |
contains |
包含 | resource.tags contains "confidential" |
stringEquals |
字符串精确匹配 | resource.region == "cn-north" |
dateTimeGreaterThan |
时间比较 | currentTime > "2025-01-01T00:00:00Z" |
Condition可以组合使用。我常用的组合方式有两种:
- AllOf(与):所有子条件必须同时满足。
- AnyOf(或):任意一个子条件满足即可。
看个完整的例子:
{
"condition": {
"allOf": [
{ "equals": { "user.department": "finance" } },
{ "greaterThan": { "resource.amount": 10000 } },
{ "anyOf": [
{ "equals": { "user.level": "senior" } },
{ "equals": { "user.role": "finance_manager" } }
]}
]
}
}
这条Condition的意思是:用户必须是财务部的,操作的金额要大于1万,而且用户要么是高级员工,要么是财务经理。三个条件缺一不可。
4.4 Effect:策略的「结论」
Effect只有两个值:Permit 和 Deny。简单吧?但这里有个坑——Deny优先。
什么意思呢?如果多条策略匹配了同一个请求,其中一条是Deny,其他都是Permit,那最终结果是Deny。这是ABAC的通用安全原则:宁可错杀,不可放过。
我习惯把Effect放在策略的最外层,一眼就能看到结论:
{
"effect": "Deny",
"target": { ... },
"condition": { ... }
}
嗯,这里要注意:Effect不能为空。如果一条策略没有Effect,引擎应该直接报错,不能默认Permit或Deny。我在设计引擎时,把Effect校验放在了策略加载阶段,而不是运行时。这样有问题能早点发现。
4.5 完整策略示例
把上面三部分拼起来,就是一个完整的策略:
{
"policyId": "POLICY-001",
"description": "财务经理可审批金额超过10万的报销单",
"effect": "Permit",
"target": {
"subject": { "role": "finance_manager" },
"resource": { "type": "reimbursement" },
"action": { "name": "approve" }
},
"condition": {
"allOf": [
{ "greaterThan": { "resource.amount": 100000 } },
{ "equals": { "resource.status": "pending" } },
{ "dateTimeLessThan": { "resource.submitTime": "2025-12-31T23:59:59Z" } }
]
}
}
这条策略的逻辑是:
- Target先过滤:只有财务经理对报销单的审批操作才进入判断。
- Condition再判断:金额大于10万、状态是待审批、提交时间在2025年之前。
- 都满足的话,Effect给出Permit。
4.6 策略的编写规范
我在团队里推行了几条硬性规范,分享给你:
- 一条策略只做一件事。别想着一条策略覆盖所有场景,拆开写更清晰。
- Effect写在最前面。读策略的人第一眼就知道是允许还是拒绝。
- Target尽量精确。Target越精确,策略匹配越快,引擎压力越小。
- Condition里不要写死值。比如金额阈值、时间范围,应该做成配置参数,方便后期调整。
- 每条策略必须有description。半年后你回头看自己写的策略,没有注释你会想骂人。
下面这张图展示了策略的完整执行流程:
从流程图能看出来,Target是第一个关卡。它不匹配的话,Condition根本不会执行。这种设计能大幅减少不必要的计算。我在生产环境做过压测,加了Target预过滤后,策略匹配的QPS提升了将近3倍。
4.7 策略的版本管理
最后聊个实战问题。策略写好了,怎么管理?
我建议给每条策略加版本号和生效时间:
{
"policyId": "POLICY-001",
"version": "2.1.0",
"effectiveFrom": "2025-01-01T00:00:00Z",
"effectiveUntil": "2025-12-31T23:59:59Z",
"effect": "Permit",
"target": { ... },
"condition": { ... }
}
为什么要加版本?因为策略会变。你改了一条策略,结果线上出问题了,想回退却发现找不到旧版本——这种事我经历过两次,后来老老实实加了版本管理。
嗯,策略结构就这些。Target、Condition、Effect,三个部分缺一不可。写策略的时候,先想清楚Target范围,再设计Condition逻辑,最后定Effect结论。顺序对了,策略就不会乱。