返回博客

ABAC 实现:基于属性的细粒度访问控制

ABAC(Attribute-Based Access Control)是 coomia-dip 三层权限模型的第二层,负责基于主体属性、资源属性和环境属性的组合条件来实现细粒度的访问控制。本文详解 ABAC 的属性模型、策略表达式引擎、8 种运算符的实现、条件评估的性能优化,以及与 RBAC 层和 ReBAC 层的协同工作机制。

Coomia发布于 2025年9月16日13 分钟阅读
分享本文Twitter / X

系列:S6 平台工程 · 第 3 篇 | 难度:高级 | 阅读时间:18 分钟

ABAC 实现:基于属性的细粒度访问控制

#TL;DR

ABAC(Attribute-Based Access Control)是 coomia-dip 三层权限模型的第二层,负责基于主体属性、资源属性和环境属性的组合条件来实现细粒度的访问控制。本文详解 ABAC 的属性模型、策略表达式引擎、8 种运算符的实现、条件评估的性能优化,以及与 RBAC 层和 ReBAC 层的协同工作机制。

#1. ABAC 的核心思想

#1.1 从角色到属性的演进

RBAC 通过角色间接授权,但角色是静态的。ABAC 通过动态属性实现上下文感知的授权:

Code
+-------------------+     +-------------------+     +-------------------+
|   主体属性         |     |   资源属性         |     |   环境属性         |
|   Subject Attrs   |     |   Resource Attrs  |     |   Environ Attrs   |
+-------------------+     +-------------------+     +-------------------+
| - department      |     | - classification  |     | - time            |
| - clearance_level |     | - owner           |     | - ip_address      |
| - location        |     | - sensitivity     |     | - device_type     |
| - job_title       |     | - data_region     |     | - mfa_status      |
| - certifications  |     | - retention_days  |     | - network_zone    |
+-------------------+     +-------------------+     +-------------------+
         \                       |                       /
          +----------------------+----------------------+
                                 |
                    +----------------------------+
                    |    ABAC Policy Engine       |
                    |    8 种运算符评估策略         |
                    +----------------------------+
                                 |
                          ALLOW / DENY

#1.2 ABAC 的优势

维度RBACABAC
授权粒度角色级属性级
动态性静态角色分配实时属性评估
上下文感知时间、IP、设备等
数据感知分类级别、敏感度等
策略灵活性角色组合属性表达式任意组合
管理复杂度中-高

#2. 属性模型设计

#2.1 属性分类体系

coomia-dip 将属性分为四大类,每类包含预定义和自定义属性:

Code
属性体系
├── 主体属性 (Subject Attributes)
│   ├── 身份属性: user_id, username, email
│   ├── 组织属性: department, team, division
│   ├── 安全属性: clearance_level, mfa_enabled
│   ├── 角色属性: roles[], groups[]
│   └── 自定义属性: certifications, specialties
│
├── 资源属性 (Resource Attributes)
│   ├── 分类属性: classification (A1-D)
│   ├── 所有权属性: owner, project, tenant
│   ├── 元数据属性: created_at, sensitivity
│   ├── 地理属性: data_region, storage_location
│   └── 自定义属性: retention_policy, audit_required
│
├── 操作属性 (Action Attributes)
│   ├── 操作类型: action_type (READ/WRITE/DELETE)
│   ├── 批量标识: is_bulk_operation
│   └── 导出标识: is_export
│
└── 环境属性 (Environment Attributes)
    ├── 时间属性: current_time, day_of_week
    ├── 网络属性: source_ip, network_zone
    ├── 设备属性: device_type, os, browser
    ├── 认证属性: auth_method, mfa_status
    └── 会话属性: session_duration, last_activity

#2.2 属性数据模型

PROTOBUF
message AttributeDefinition {
    string name = 1;           // 属性名: "department"
    string display_name = 2;   // 显示名: "部门"
    AttributeType type = 3;    // STRING, INT, BOOL, LIST, MAP
    AttributeCategory category = 4;  // SUBJECT, RESOURCE, ENV
    bool is_required = 5;      // 是否必填
    string default_value = 6;  // 默认值
    repeated string allowed_values = 7;  // 允许的枚举值
    string validation_regex = 8;  // 验证正则
}

enum AttributeType {
    STRING = 0;
    INTEGER = 1;
    BOOLEAN = 2;
    DOUBLE = 3;
    TIMESTAMP = 4;
    STRING_LIST = 5;
    STRING_MAP = 6;
}

message AttributeValue {
    string name = 1;
    oneof value {
        string string_value = 2;
        int64 int_value = 3;
        bool bool_value = 4;
        double double_value = 5;
        google.protobuf.Timestamp timestamp_value = 6;
        StringList list_value = 7;
        StringMap map_value = 8;
    }
}

#3. 策略表达式引擎

#3.1 策略结构

ABAC 策略由条件表达式树组成,支持 AND/OR/NOT 逻辑组合:

Code
PolicyRule
├── effect: ALLOW | DENY
├── target:
│   ├── subjects: {属性匹配条件}
│   ├── resources: {属性匹配条件}
│   └── actions: [READ, WRITE]
└── conditions:
    └── LogicalExpression (AND/OR/NOT)
        ├── Condition (attribute, operator, value)
        ├── Condition (attribute, operator, value)
        └── LogicalExpression (nested)

#3.2 策略 YAML 定义

YAML
policy:
  id: "abac-dataset-access-001"
  name: "数据集访问控制策略"
  effect: ALLOW
  target:
    resource_types: [DATASET, OBJECT]
    actions: [READ, EXPORT]
  conditions:
    operator: AND
    children:
      - attribute: "subject.clearance_level"
        op: "gte"
        value_ref: "resource.classification_level"
      - operator: OR
        children:
          - attribute: "environment.network_zone"
            op: "eq"
            value: "INTERNAL"
          - attribute: "subject.mfa_enabled"
            op: "eq"
            value: true
      - attribute: "environment.current_time"
        op: "between"
        value: ["08:00", "20:00"]
  obligations:
    - type: MASK
      condition:
        attribute: "resource.sensitivity"
        op: "gte"
        value: "HIGH"
      params:
        mode: PARTIAL_MASK
        fields: ["phone", "email", "ssn"]
    - type: AUDIT
      params:
        level: DETAILED

#3.3 表达式评估引擎

Java
public class AbacExpressionEvaluator {

    private final Map<String, OperatorEvaluator> operators;

    public AbacExpressionEvaluator() {
        operators = Map.of(
            "eq", new EqualityOperator(),
            "ne", new InequalityOperator(),
            "gt", new GreaterThanOperator(),
            "lt", new LessThanOperator(),
            "gte", new GreaterOrEqualOperator(),
            "lte", new LessOrEqualOperator(),
            "in", new InSetOperator(),
            "contains", new ContainsOperator(),
            "matches", new RegexMatchOperator(),
            "between", new BetweenOperator()
        );
    }

    public boolean evaluate(LogicalExpression expr,
                            EvaluationContext ctx) {
        if (expr.isLeaf()) {
            return evaluateCondition(expr.getCondition(), ctx);
        }

        switch (expr.getLogicalOperator()) {
            case AND:
                return expr.getChildren().stream()
                    .allMatch(child -> evaluate(child, ctx));
            case OR:
                return expr.getChildren().stream()
                    .anyMatch(child -> evaluate(child, ctx));
            case NOT:
                return !evaluate(
                    expr.getChildren().get(0), ctx);
            default:
                throw new IllegalArgumentException(
                    "Unknown operator: "
                    + expr.getLogicalOperator());
        }
    }

    private boolean evaluateCondition(
            Condition condition, EvaluationContext ctx) {
        Object actualValue = ctx.resolveAttribute(
            condition.getAttribute());
        Object expectedValue = condition.isValueRef()
            ? ctx.resolveAttribute(condition.getValueRef())
            : condition.getValue();

        OperatorEvaluator op = operators.get(
            condition.getOperator());
        if (op == null) {
            throw new PolicyEvaluationException(
                "Unknown operator: " + condition.getOperator());
        }

        return op.evaluate(actualValue, expectedValue);
    }
}

#4. 八种运算符详解

#4.1 运算符实现

Java
// 等于运算符
public class EqualityOperator implements OperatorEvaluator {
    @Override
    public boolean evaluate(Object actual, Object expected) {
        if (actual == null) return expected == null;
        return actual.equals(coerce(expected, actual.getClass()));
    }
}

// 集合包含运算符
public class InSetOperator implements OperatorEvaluator {
    @Override
    public boolean evaluate(Object actual, Object expected) {
        if (expected instanceof Collection<?> set) {
            return set.contains(actual);
        }
        throw new PolicyEvaluationException(
            "IN operator requires collection value");
    }
}

// 正则匹配运算符
public class RegexMatchOperator implements OperatorEvaluator {
    private final Cache<String, Pattern> patternCache =
        Caffeine.newBuilder().maximumSize(1000).build();

    @Override
    public boolean evaluate(Object actual, Object expected) {
        String pattern = expected.toString();
        Pattern compiled = patternCache.get(pattern,
            Pattern::compile);
        return compiled.matcher(actual.toString()).matches();
    }
}

// 范围运算符
public class BetweenOperator implements OperatorEvaluator {
    @Override
    public boolean evaluate(Object actual, Object expected) {
        if (expected instanceof List<?> range
                && range.size() == 2) {
            Comparable comp = (Comparable) actual;
            Comparable min = (Comparable) coerce(
                range.get(0), actual.getClass());
            Comparable max = (Comparable) coerce(
                range.get(1), actual.getClass());
            return comp.compareTo(min) >= 0
                && comp.compareTo(max) <= 0;
        }
        throw new PolicyEvaluationException(
            "BETWEEN requires [min, max] array");
    }
}

#4.2 运算符兼容矩阵

Code
+----------+--------+-----+------+------+-----------+--------+
| 运算符    | String | Int | Bool | Time | List<Str> | Double |
+----------+--------+-----+------+------+-----------+--------+
| eq       |   Y    |  Y  |  Y   |  Y   |     Y     |   Y    |
| ne       |   Y    |  Y  |  Y   |  Y   |     Y     |   Y    |
| gt       |   Y    |  Y  |  -   |  Y   |     -     |   Y    |
| lt       |   Y    |  Y  |  -   |  Y   |     -     |   Y    |
| in       |   Y    |  Y  |  -   |  -   |     -     |   Y    |
| contains |   Y    |  -  |  -   |  -   |     Y     |   -    |
| matches  |   Y    |  -  |  -   |  -   |     -     |   -    |
| between  |   Y    |  Y  |  -   |  Y   |     -     |   Y    |
+----------+--------+-----+------+------+-----------+--------+

#5. 属性解析与收集

#5.1 属性收集架构

Code
+-------------------+
| 请求到达          |
+-------------------+
         |
    +----v----+
    | 属性    |    从多个来源收集属性
    | 收集器   |
    +----+----+
         |
    +----+----+----+----+
    |    |    |    |    |
    v    v    v    v    v
  JWT  LDAP  DB  Env  Custom
  属性  属性  属性 属性  属性
  提取  查询  查询 感知  提供者

         |
    +----v----+
    | 属性    |    合并、验证、缓存
    | 上下文   |
    +----+----+
         |
    +----v----+
    | ABAC    |    策略评估
    | 评估器   |
    +---------+

#5.2 属性收集器实现

Java
@Component
public class AttributeCollector {

    private final List<AttributeProvider> providers;
    private final Cache<String, Map<String, Object>> cache;

    public EvaluationContext collectAttributes(
            AuthenticationContext auth,
            ResourceRef resource,
            HttpServletRequest request) {

        // 主体属性
        Map<String, Object> subjectAttrs = new HashMap<>();
        subjectAttrs.put("user_id", auth.getUserId());
        subjectAttrs.put("roles", auth.getRoles());
        subjectAttrs.put("department",
            auth.getClaim("department"));
        subjectAttrs.put("clearance_level",
            auth.getClaim("clearance_level"));
        subjectAttrs.put("mfa_enabled",
            auth.isMfaAuthenticated());

        // 资源属性(带缓存)
        Map<String, Object> resourceAttrs = cache.get(
            resource.getId(), () ->
                loadResourceAttributes(resource));

        // 环境属性
        Map<String, Object> envAttrs = new HashMap<>();
        envAttrs.put("current_time",
            LocalTime.now().toString());
        envAttrs.put("day_of_week",
            LocalDate.now().getDayOfWeek().name());
        envAttrs.put("source_ip",
            request.getRemoteAddr());
        envAttrs.put("network_zone",
            classifyNetworkZone(request.getRemoteAddr()));
        envAttrs.put("device_type",
            parseDeviceType(
                request.getHeader("User-Agent")));

        // 自定义属性提供者
        for (AttributeProvider provider : providers) {
            provider.enhance(subjectAttrs, resourceAttrs,
                envAttrs);
        }

        return new EvaluationContext(
            subjectAttrs, resourceAttrs, envAttrs);
    }

    private String classifyNetworkZone(String ip) {
        if (ip.startsWith("10.") || ip.startsWith("192.168."))
            return "INTERNAL";
        if (ip.startsWith("172."))
            return "DMZ";
        return "EXTERNAL";
    }
}

#6. 性能优化

#6.1 短路评估

Java
// AND 表达式的短路评估
// 第一个 false 结果即可终止评估
public boolean evaluateAndExpression(
        List<LogicalExpression> children,
        EvaluationContext ctx) {
    for (LogicalExpression child : children) {
        if (!evaluate(child, ctx)) {
            return false;  // 短路:无需评估剩余条件
        }
    }
    return true;
}

// OR 表达式的短路评估
// 第一个 true 结果即可终止评估
public boolean evaluateOrExpression(
        List<LogicalExpression> children,
        EvaluationContext ctx) {
    for (LogicalExpression child : children) {
        if (evaluate(child, ctx)) {
            return true;  // 短路:无需评估剩余条件
        }
    }
    return false;
}

#6.2 策略编译优化

Java
public class CompiledPolicy {
    // 预编译正则表达式
    private final Map<String, Pattern> compiledPatterns;
    // 预解析属性路径
    private final Map<String, AttributePath> resolvedPaths;
    // 预排序条件(成本低的优先评估)
    private final List<Condition> sortedConditions;

    public static CompiledPolicy compile(PolicyRule rule) {
        CompiledPolicy compiled = new CompiledPolicy();

        // 提取并编译所有正则表达式
        for (Condition c : rule.getAllConditions()) {
            if ("matches".equals(c.getOperator())) {
                compiled.compiledPatterns.put(
                    c.getValue().toString(),
                    Pattern.compile(c.getValue().toString()));
            }
        }

        // 按评估成本排序条件
        compiled.sortedConditions = rule.getAllConditions()
            .stream()
            .sorted(Comparator.comparingInt(
                CompiledPolicy::estimateCost))
            .collect(Collectors.toList());

        return compiled;
    }

    private static int estimateCost(Condition c) {
        return switch (c.getOperator()) {
            case "eq", "ne" -> 1;
            case "gt", "lt", "gte", "lte" -> 2;
            case "in" -> 3;
            case "between" -> 4;
            case "contains" -> 5;
            case "matches" -> 10;  // 正则最贵
            default -> 5;
        };
    }
}

#6.3 性能基准

场景平均耗时P99 耗时
简单 eq 条件 (1-2 个)0.1ms0.5ms
复合 AND 条件 (3-5 个)0.3ms1.2ms
嵌套 AND/OR (5-10 个)0.8ms3.5ms
含正则匹配1.5ms6.0ms
含属性值引用1.2ms5.0ms

#7. 实际策略示例

#7.1 工作时间限制策略

YAML
policy:
  id: "abac-time-restriction"
  name: "工作时间数据访问限制"
  effect: DENY
  conditions:
    operator: AND
    children:
      - attribute: "resource.classification"
        op: "gte"
        value: "B2"
      - operator: OR
        children:
          - attribute: "environment.current_time"
            op: "lt"
            value: "08:00"
          - attribute: "environment.current_time"
            op: "gt"
            value: "20:00"

#7.2 地理围栏策略

YAML
policy:
  id: "abac-geo-fence"
  name: "中国数据仅限境内访问"
  effect: DENY
  conditions:
    operator: AND
    children:
      - attribute: "resource.data_region"
        op: "eq"
        value: "CN"
      - attribute: "environment.source_country"
        op: "ne"
        value: "CN"

#7.3 安全级别匹配策略

YAML
policy:
  id: "abac-clearance-match"
  name: "安全级别匹配"
  effect: ALLOW
  conditions:
    operator: AND
    children:
      - attribute: "subject.clearance_level"
        op: "gte"
        value_ref: "resource.classification_level"
      - attribute: "subject.department"
        op: "in"
        value_ref: "resource.allowed_departments"

#8. 与 RBAC 和 ReBAC 的协同

#8.1 评估顺序

Code
请求 -> [RBAC 快速路径] -> [ABAC 条件细化] -> [ReBAC 关系检查]
              |                    |                    |
          快速放行/拒绝        条件评估            关系图遍历
              |                    |                    |
              +-----> DENY-overrides 合并 <-----------+

#8.2 ABAC 增强 RBAC 的典型模式

Code
场景: DATA_ANALYST 角色有 READ 权限 (RBAC ALLOW)
      但需要 ABAC 进一步约束:

RBAC: DATA_ANALYST -> READ -> DATASET    (ALLOW)
ABAC: + classification < C1             (条件 1)
      + network_zone = INTERNAL         (条件 2)
      + time between 08:00-20:00        (条件 3)

最终: 仅当所有 ABAC 条件满足时才允许

#9. 策略管理

#9.1 策略版本控制

Code
策略变更流程:
  1. 编写策略 YAML
  2. 策略验证(语法、属性引用、类型检查)
  3. 影响分析(模拟评估现有请求)
  4. 审批流程
  5. 灰度发布(先在测试环境验证)
  6. 全量发布
  7. 监控告警

#9.2 策略仿真

Python
from ontology_sdk import OntoPlatform

platform = OntoPlatform(endpoint="grpc://control-Layer:9090")

# 仿真策略评估
simulation = await platform.policy.simulate(
    policy_id="abac-dataset-access-001",
    test_cases=[
        {
            "subject": {"clearance_level": "B3",
                        "department": "engineering"},
            "resource": {"classification": "B2",
                         "type": "DATASET"},
            "environment": {"time": "10:00",
                            "network_zone": "INTERNAL"},
            "expected": "ALLOW"
        },
        {
            "subject": {"clearance_level": "A2",
                        "department": "marketing"},
            "resource": {"classification": "C1",
                         "type": "DATASET"},
            "environment": {"time": "22:00",
                            "network_zone": "EXTERNAL"},
            "expected": "DENY"
        }
    ]
)

for result in simulation.results:
    print(f"Case: {result.case_id}, "
          f"Decision: {result.decision}, "
          f"Match Expected: {result.matches_expected}")

#10. 监控与告警

#10.1 ABAC 评估指标

指标说明告警阈值
abac_evaluation_duration_ms评估耗时P99 > 10ms
abac_evaluation_total评估总次数-
abac_evaluation_deny_rate拒绝率> 40%
abac_attribute_resolution_errors属性解析错误> 0
abac_policy_compilation_errors策略编译错误> 0
abac_cache_hit_ratio属性缓存命中率< 60%

#Key Takeaways

  1. 四类属性:主体、资源、操作、环境属性的组合提供了无限灵活的授权表达能力
  2. 8 种运算符覆盖等值比较、范围比较、集合判断、正则匹配等所有企业级需求
  3. 表达式树支持 AND/OR/NOT 嵌套组合,可表达任意复杂的策略逻辑
  4. 短路评估策略编译优化确保亚毫秒级评估性能
  5. 属性值引用允许条件中引用其他属性(如 subject.clearance >= resource.classification)
  6. 策略仿真能力使策略变更安全可控

#Next Article

下一篇 S6-04: ReBAC 实现:基于关系的访问控制 将讲解如何借鉴 Google Zanzibar 论文,通过关系元组和关系图遍历实现基于关系的访问控制。

Tags: #ABAC #属性访问控制 #策略引擎 #运算符 #细粒度权限 #表达式引擎 #coomia-dip #平台工程 #安全 #Palantir