2Secure by Design

领域建模安全

安全设计

值对象不变量领域边界安全约束

🎯 AI 时代的学习建议:不要只读理论。理解这一章的设计动机(为什么需要这个概念?)和边界条件(什么情况下会失效?)。当 AI 生成的代码涉及这些概念时,你能判断它是否用对了场景。AI 能帮你总结内容,但无法替代你亲手阅读、思考、建立直觉的过程。

第2章:领域建模安全(Domain Modeling Security)

一、导读

1.1 本章学习目标

第2章讲解如何通过领域建模来增强安全性——让类型系统成为安全的第一道防线。

本章的核心学习目标包括:

  • 理解值对象在安全中的作用
  • 掌握不变量的设计
  • 学会用领域边界隔离安全关注点
  • 理解类型驱动的安全设计

1.2 为什么本章如此重要

好的领域模型能防止非法状态被表示。如果你的类型系统能表达"一个有效的邮箱地址"而不仅仅是"一个字符串",那么很多安全漏洞在编译时就能被发现。

1.3 核心问题引导

值对象如何帮助防止注入攻击?

不变量在安全设计中扮演什么角色?

如何用领域边界隔离安全关注点?

类型系统能成为安全防线吗?


二、核心概念详解

2.1 值对象与安全

java
// 不好的设计:使用原始类型
public void sendEmail(String email, String message) {
    // email 可能是恶意的
    // message 可能包含注入代码
}

// 好的设计:使用值对象
public class EmailAddress {
    private final String value;
    
    public EmailAddress(String value) {
        if (!isValid(value)) {
            throw new IllegalArgumentException("Invalid email");
        }
        this.value = value;
    }
    
    private boolean isValid(String value) {
        return value.matches("^[A-Za-z0-9+_.-]+@(.+)$");
    }
    
    public String getValue() { return value; }
}

public void sendEmail(EmailAddress email, Message message) {
    // email 一定是有效的
    // message 是类型安全的
}

2.2 不变量

java
// 不可变的金额类
public final class Money {
    private final BigDecimal amount;
    private final Currency currency;
    
    public Money(BigDecimal amount, Currency currency) {
        if (amount.compareTo(BigDecimal.ZERO) < 0) {
            throw new IllegalArgumentException("Amount cannot be negative");
        }
        this.amount = amount;
        this.currency = currency;
    }
    
    public Money add(Money other) {
        if (!this.currency.equals(other.currency)) {
            throw new IllegalArgumentException("Currency mismatch");
        }
        return new Money(this.amount.add(other.amount), this.currency);
    }
}

2.3 领域边界

java
// 安全边界:认证领域
public class AuthContext {
    private final UserId userId;
    private final Set<Permission> permissions;
    
    public boolean hasPermission(Permission p) {
        return permissions.contains(p);
    }
}

// 业务领域:使用认证上下文
public class OrderService {
    public Order createOrder(OrderRequest request, AuthContext auth) {
        if (!auth.hasPermission(Permission.CREATE_ORDER)) {
            throw new AccessDeniedException();
        }
        // 业务逻辑不需要关心认证细节
    }
}

三、实战案例

3.1 案例一:类型安全的 SQL 查询

java
// 值对象防止 SQL 注入
public class UserId {
    private final UUID value;
    
    public UserId(String value) {
        try {
            this.value = UUID.fromString(value);
        } catch (IllegalArgumentException e) {
            throw new SecurityException("Invalid user ID format");
        }
    }
    
    public UUID getValue() { return value; }
}

// 查询构建器使用值对象
public class UserQuery {
    public User findById(UserId id) {
        // id 一定是有效的 UUID,不可能注入 SQL
        return jdbcTemplate.queryForObject(
            "SELECT * FROM users WHERE id = ?",
            new Object[]{id.getValue()},
            UserRowMapper.INSTANCE
        );
    }
}

四、本章小结

值对象:通过类型约束防止非法数据。

不变量:确保对象始终处于有效状态。

领域边界:隔离安全关注点,减少攻击面。

类型驱动安全:让编译器成为安全防线。


五、思考与练习

设计一组值对象来防止常见的 Web 安全漏洞。

如何用不变量保护一个银行账户系统?

分析一个你熟悉的系统,找出可以用领域建模改进的安全点。


2/7领域建模
0%