3 ABAC策略语言基础:XACML简介、策略集合、规则、目标与条件

做ABAC落地,绕不开策略语言。市面上可选的有XACML、ALFA、甚至自己写DSL。但我个人习惯——只要不是极端边缘场景,先看XACML。为什么?因为它的结构已经被全球几百个生产系统验证过,坑都踩平了。

说白了,XACML就是一套用来描述“谁在什么条件下能对什么资源做什么”的标准XML方言。你不需要背完整个规范,但核心概念必须吃透:策略集合、策略、规则、目标、条件。这五个东西组成了XACML的骨骼。

提示: 本章所有示例基于 XACML 3.0 核心标准。如果你只用ALFA或JSON策略,核心概念完全一样,只是语法糖不同。

3.1 从业务需求到策略结构

先看一个真实业务规则:

  • 只有“高级工程师”或“项目经理”角色的人,才能在“工作时间内”查看“项目成本”报表。

这个需求在XACML里怎么拆?

  • 谁 → 请求的主体属性(角色)
  • 什么资源 → 资源属性(报表类型)
  • 什么操作 → 动作属性(查看)
  • 什么条件 → 环境属性(工作时间)

XACML 用 目标(Target) 来匹配前三个,用 条件(Condition) 来处理第四个。目标只做简单的等值或模式匹配,条件支持更复杂布尔逻辑。

3.2 五层体系一次理清

先看整体结构的SVG图,方便建立全局印象。

XACML 策略层次结构 PolicySet(策略集合) Policy 1(主策略) Policy 2(备用策略) Rule 1(允许规则) Target(目标) Condition(条件) PolicySet 可嵌套 PolicySet,Policy 内可含多个 Rule Target 匹配主体/资源/动作,Condition 做复杂布尔判断

从上图可以看出一个大原则:策略集合(PolicySet) 包含策略(Policy),策略包含规则(Rule)。每个节点都可能有自己的目标,但规则的目标通常继承自父容器。

3.3 各组件详解

3.3.1 PolicySet(策略集合)

用于分组管理策略。典型用途是按域、应用或环境切分。比如“生产环境策略集合”、“开发环境策略集合”。

属性:

  • PolicyCombiningAlg:策略组合算法,决定多个策略冲突时谁说了算(如:先适用、拒绝优先、允许优先)
  • Target:可选,若指定,只处理匹配该目标的请求
注意: 我曾经在一个金融项目中遇到一个Bug——策略集合的组合算法设成了“拒绝优先”,导致所有请求都返回Deny。排查半天才发现是PolicyCombiningAlg写成了"deny-overrides"但拼写错误,变成了"deny-ovverides",PDP直接取了默认值"first-applicable"。拼写错误在XACML中非常隐蔽,务必在测试阶段用schema验证。

3.3.2 Policy(策略)

策略是策略集合的子节点,它才是真正的决策单元。一个Policy包含一个或多个Rule,以及自己的RuleCombiningAlg。

对比一下:

组件组合算法作用范围典型场景
PolicySet多个Policy之间按环境拆分(prod/test)
Policy内部多个Rule之间一个资源的多条权限规则

3.3.3 Rule(规则)

Rule是真正的原子决策单元。每个Rule有:

  • Effect:Permit 或 Deny
  • Target:可选(通常继承Policy的Target再细化)
  • Condition:布尔表达式,返回True才生效
  • Obligation / Advice:附加指令(如审计日志),不是本章重点

看一个具体例子:

<Rule RuleId="成本报表-工作时间规则" Effect="Permit">
    <Target>
        <AnyOf>
            <AllOf>
                <Match MatchId="string-equal">
                    <AttributeValue DataType="string">高级工程师</AttributeValue>
                    <AttributeDesignator Category="urn:oasis:names:tc:xacml:1.0:subject-category:access-subject"
                           AttributeId="role"/>
                </Match>
            </AllOf>
        </AnyOf>
        <!-- 项目经理角色同样匹配 -->
    </Target>
    <Condition>
        <Apply FunctionId="time-in-range">
            <Apply FunctionId="time-one-and-only">
                <AttributeDesignator Category="urn:oasis:names:tc:xacml:3.0:environment-category:environment"
                       AttributeId="current-time"/>
            </Apply>
            <AttributeValue DataType="time">09:00:00</AttributeValue>
            <AttributeValue DataType="time">18:00:00</AttributeValue>
        </Apply>
    </Condition>
</Rule>

这段代码核心就是:如果请求者角色是“高级工程师”或“项目经理”,并且当前时间在09:00~18:00之间,则允许访问。注意Condition里用了一个自定义函数 time-in-range,XACML允许扩展自有函数。

3.3.4 Target(目标)

Target的作用很明确:快速过滤。它只做简单匹配(等于、不等于、子集等),不做复杂计算。PDP在处理请求时,先用Target找到匹配的策略和规则,跳过不相关的分支,性能全靠它。

Target的四个维度:

  1. Subject:主体(谁)
  2. Resource:资源(什么)
  3. Action:动作(操作)
  4. Environment:环境(可选,比如当前时间、位置)

在实际项目中,我建议把80%的过滤逻辑放在Target层。Condition只用来兜底。这样做的好处是——PDP可以快速拒绝无关请求,Condition的布尔计算次数降到最低。

3.3.5 Condition(条件)

Condition是一个可选的布尔表达式,结果为True则规则生效,False则规则不适用(NotApplicable)。支持嵌套、函数调用、比较。

常用内置函数举例:

  • string-equal, string-at-least-one-member-of
  • integer-greater-than, double-less-than
  • time-in-range(需自定义或扩展)
  • and, or, not
经验之谈: Condition不要过于复杂。我在一个项目中看到有人写了20层嵌套的Apply,结果PDP解析花了200ms。后来我们把部分条件拆成多个Rule+Target,性能提升了10倍。记住:Condition是不得已才用的,能用Target解决的绝不动Condition。

3.4 组合算法怎么选

当多个规则或策略同时匹配时,谁来定最终结果?这就是组合算法的职责。

常见算法表:

算法名称含义使用场景
deny-overrides只要有一个Deny,最终就是Deny安全敏感场景,任何拒绝都不能被覆盖
permit-overrides只要有一个Permit,最终就是Permit资源开放场景,有人允许就行
first-applicable按顺序找第一个生效的规则策略有明确优先级时
only-one-applicable只允许一个匹配,否则报错严格隔离场景(比如微服务边界)

我个人习惯:先在全局用deny-overrides保底,针对具体业务再用first-applicable细化。组合算法本身可以嵌套,PolicySet用一套,Policy用另一套。

避坑: 如果PolicySet用deny-overrides,Policy却用了permit-overrides,最终结果可能不是你预期的。我曾经犯过一个错——在Policy里写了permit-overrides,以为能覆盖上级的deny,结果PDP的层级优先级高于规则内优先级。记住:组合算法只在当前层级有效,不能跨层覆盖。上级的Deny会直接短路。

3.5 小结

XACML的核心结构其实不复杂:PolicySet装Policy,Policy装Rule,每个节点都可能带Target做过滤,Rule里还能加Condition做精细判断。你把这个层次关系刻在脑子里,就已经掌握了XACML的七成。