Skip to content
第 236 / 250 章架构⏱ 12 分钟阅读

第 236 章:数据安全与合规

学习目标

  • 数据加密方案
  • 隐私信息保护
  • 法律法规合规
  • 数据备份与恢复

一、数据安全威胁

二、加密技术

2.1 对称加密 vs 非对称加密

维度对称非对称
密钥同一把一对(公/私)
性能
用途大量数据加密密钥交换、签名
算法AES、DES、SM4RSA、ECC、SM2

2.2 AES 加密

java
public class AESUtil {

    private static final String KEY = "my-256-bit-key-...";
    private static final String IV = "1234567890abcdef";   // 16 字节

    public static String encrypt(String data) throws Exception {
        SecretKeySpec keySpec = new SecretKeySpec(KEY.getBytes(), "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, keySpec, new IvParameterSpec(IV.getBytes()));
        byte[] encrypted = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(encrypted);
    }

    public static String decrypt(String encrypted) throws Exception {
        byte[] data = Base64.getDecoder().decode(encrypted);
        SecretKeySpec keySpec = new SecretKeySpec(KEY.getBytes(), "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, keySpec, new IvParameterSpec(IV.getBytes()));
        byte[] original = cipher.doFinal(data);
        return new String(original, StandardCharsets.UTF_8);
    }
}

2.3 国密 SM4/SM2

xml
<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk15on</artifactId>
</dependency>
java
public class SM4Util {
    public byte[] encrypt(byte[] data, byte[] key) {
        // SM4 算法
    }
}

2.4 字段级加密

java
@Entity
public class User {

    @Convert(converter = EncryptedStringConverter.class)
    @Column(name = "phone")
    private String phone;     // 数据库里是密文

    @Convert(converter = EncryptedStringConverter.class)
    private String idCard;
}

@Converter
public class EncryptedStringConverter implements AttributeConverter<String, String> {
    @Override
    public String convertToDatabaseColumn(String attribute) {
        return AESUtil.encrypt(attribute);
    }

    @Override
    public String convertToEntityAttribute(String dbData) {
        return AESUtil.decrypt(dbData);
    }
}

2.5 密钥管理

yaml
# ❌ 不应放在代码中
private static final String KEY = "hardcoded-secret";

# ✅ 通过 Vault / KMS 动态获取
@Autowired
private VaultTemplate vaultTemplate;

String key = vaultTemplate.read("secret/data/appkey")
    .getData().get("aes_key");

工具:

  • HashiCorp Vault
  • AWS KMS
  • 阿里云 KMS
  • 腾讯云 KMS

三、敏感数据保护

3.1 数据分级

级别数据
L4 绝密身份证、银行密码
L3 机密银行卡号、医疗记录
L2 秘密手机号、邮箱
L1 公开公开资料

3.2 手机号脱敏

java
public static String maskPhone(String phone) {
    if (phone == null || phone.length() != 11) return phone;
    return phone.substring(0, 3) + "****" + phone.substring(7);
    // 138****8888
}

public static String maskIdCard(String idCard) {
    if (idCard == null || idCard.length() < 8) return idCard;
    return idCard.substring(0, 4) + "**********" + idCard.substring(14);
    // 1101**********1234
}

3.3 邮箱脱敏

java
public static String maskEmail(String email) {
    int atIndex = email.indexOf("@");
    if (atIndex <= 1) return email;
    return email.charAt(0) + "***" + email.substring(atIndex);
    // t***@gmail.com
}

3.4 JSON 序列化脱敏

java
@JsonSerialize(using = PhoneMaskSerializer.class)
private String phone;

public class PhoneMaskSerializer extends JsonSerializer<String> {
    @Override
    public void serialize(String value, JsonGenerator gen, SerializerProvider provider)
            throws IOException {
        gen.writeString(MaskUtil.maskPhone(value));
    }
}

3.5 日志脱敏

java
public class LogMaskUtil {

    private static final Pattern PHONE = Pattern.compile("\\b1[3-9]\\d{9}\\b");
    private static final Pattern ID_CARD = Pattern.compile("\\b\\d{17}[\\dXx]\\b");
    private static final Pattern EMAIL = Pattern.compile("\\b[\\w.]+@[\\w.]+\\b");

    public static String mask(String log) {
        log = PHONE.matcher(log).replaceAll("$1****$2");
        log = ID_CARD.matcher(log).replaceAll("$1***$2");
        log = EMAIL.matcher(log).replaceAll("***@***");
        return log;
    }
}

// 在 logback 中插入脱敏 Appender
public class MaskAppender extends AppenderBase<ILoggingEvent> {
    @Override
    protected void append(ILoggingEvent event) {
        String msg = LogMaskUtil.mask(event.getFormattedMessage());
        System.out.println(msg);
    }
}

四、备份与恢复

4.1 3-2-1 原则

4.2 MySQL 备份

物理备份(XtraBackup)

bash
# 全量备份
xtrabackup --backup --target-dir=/backup/full \
    --user=root --password=xxx

# 增量备份
xtrabackup --backup --target-dir=/backup/inc1 \
    --incremental-basedir=/backup/full

# 恢复
xtrabackup --prepare --target-dir=/backup/full
xtrabackup --copy-back --target-dir=/backup/full

逻辑备份(mysqldump)

bash
mysqldump -uroot -p \
    --single-transaction \
    --routines --triggers --events \
    --databases mydb > mydb_20260813.sql

4.3 Redis 持久化

yaml
# AOF 方式,记录所有写命令
appendonly yes
appendfsync everysec
bash
# 导出 RDB
redis-cli save
# 或 BGSAVE 异步
redis-cli bgsave

4.4 备份验证

bash
# 每月演练:还原到测试环境
mysql -u root < backup_202607.sql

# 验证
SELECT count(*) FROM orders;
# 检查行数是否一致

4.5 数据库恢复 RPO / RTO

类型含义设计
RPO可容忍的数据丢失主从复制 < 1 秒
RTO恢复服务的时间备库切换 < 1 分钟

五、传输安全

5.1 TLS/HTTPS

yaml
server:
  ssl:
    enabled: true
    key-store: classpath:keystore.p12
    key-store-password: changeit
    key-store-type: PKCS12
    key-alias: myapp
    protocol: TLS
    enabled-protocols: TLSv1.2,TLSv1.3

5.2 mTLS(双向认证)

yaml
server:
  ssl:
    enabled: true
    key-store: classpath:server.p12
    trust-store: classpath:truststore.p12
    client-auth: need          # 强制客户端证书

5.3 数据库连接加密

yaml
spring:
  datasource:
    url: jdbc:mysql://host:3306/db?useSSL=true&requireSSL=true
    hikari:
      data-source-properties:
        sslMode: REQUIRED

5.4 Redis TLS

bash
redis-server --tls-port 6379 \
    --tls-cert-file /path/server.crt \
    --tls-key-file /path/server.key \
    --tls-ca-cert-file /path/ca.crt

六、数据库安全

6.1 最小权限

sql
-- ❌ 应用程序用 root
GRANT ALL ON *.* TO 'app'@'%' IDENTIFIED BY 'pass';

-- ✅ 只给必要权限
GRANT SELECT, INSERT, UPDATE ON orders.* TO 'order_app'@'%';
GRANT SELECT ON users.user TO 'order_app'@'%';     -- 读用户表
REVOKE DELETE ON orders.* FROM 'order_app'@'%';

6.2 网络隔离

yaml
# 数据库只允许应用网段访问
bind-address = 10.0.0.10   # 内网
# iptables
iptables -A INPUT -p tcp --dport 3306 -s 10.0.0.0/8 -j ACCEPT
iptables -A INPUT -p tcp --dport 3306 -j DROP

6.3 SQL 审计

yaml
# MySQL 8 开启审计
plugin-load = audit_log.so
audit_log_format = JSON
audit_log_policy = ALL

6.4 凭据轮换

java
@Component
public class CredentialRotation {

    @Scheduled(cron = "0 0 0 1 * ?")
    public void rotateDbPassword() {
        String newPass = generatePassword();
        jdbcTemplate.update("SET PASSWORD = PASSWORD(?)", newPass);
        vaultTemplate.write("secret/data/mysql", Map.of("password", newPass));
    }
}

七、隐私合规

7.1 GDPR / 中国《个人信息保护法》

yaml
合规要求:
  告知: 用明示告知用户收集目的
  同意: 用户主动勾选同意
  访问: 用户有查看自己数据的权利
  删除: 用户可要求删除数据(被遗忘权)
  修正: 用户可更新错误信息
  限制: 数据只能用于收集目的

7.2 用户"被遗忘权"实现

java
@Service
public class UserDeletionService {

    @Transactional
    public void deleteUser(Long userId) {
        // 1. 软删除用户
        userRepo.setDeletedAt(userId, Instant.now());

        // 2. 删除关联数据(订单、地址等)
        orderRepo.anonymizeByUser(userId);     // 匿名化
        addressRepo.deleteByUser(userId);       // 删除

        // 3. 删除本地缓存
        cacheService.delete("user:" + userId);

        // 4. 通知其他服务清理
        kafkaTemplate.send("user-events", new UserDeletedEvent(userId));
    }
}

7.3 匿名化 vs 假名化

匿名化: 完全不可逆(删除、聚合)
假名化: 用代号替代(数据仍可关联,但不直接对应个人)
java
public class Anonymizer {
    public Order anonymize(Order order) {
        order.setUserId(0L);              // 假名化,用 0 代表被删除用户
        order.setRecipientName("已删除用户");
        order.setPhone("00000000000");
        return order;
    }
}

八、应用层安全配置

8.1 关闭危险端点

yaml
management:
  endpoints:
    web:
      exposure:
        include: "health,info,prometheus"   # 不暴露 env、heapdump

8.2 屏蔽错误堆栈

yaml
server:
  error:
    include-stacktrace: never        # 生产环境不暴露堆栈
    include-message: never
    whitelabel:
      enabled: false

8.3 禁用非必要 HTTP 方法

java
@Configuration
public class HttpSecurityConfig {
    @Bean
    public SecurityFilterChain filter(HttpSecurity http) throws Exception {
        return http
            .csrf(csrf -> csrf.disable())
            .headers(headers -> headers
                .frameOptions(FrameOptionsConfig::deny)         // 防 clickjacking
                .xssProtection(Customizer.withDefaults())       // 防 XSS
                .contentTypeOptions(Customizer.withDefaults()) // 防 MIME sniffing
            )
            .sessionManagement(s -> s.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
            .build();
    }
}

九、密钥安全

9.1 加密机

yaml
国密合规:
  - 服务器密码机: 加密密钥专用硬件
  - UKey / 智能卡: 双因素认证
  - SM2/SM3/SM4 算法

9.2 HSM(硬件安全模块)

厂商产品
AWSCloudHSM
AzureDedicated HSM
阿里云KMS
自建Thales Luna

9.3 密钥生命周期

9.4 代码中密钥

java
// ❌ 不要 hardcode
private static final String SECRET = "abcd";

// ✅ 运行时获取
@Value("${myapp.secret}")
private String secret;

// 通过 Vault / 启动参数注入

十、第三方依赖安全

10.1 OWASP Dependency-Check

xml
<plugin>
    <groupId>org.owasp</groupId>
    <artifactId>dependency-check-maven</artifactId>
</plugin>
bash
mvn dependency-check:check
# 生成: target/dependency-check-report.html

10.2 SCA 工具

  • Trivy:扫描镜像 + 文件系统
  • Snyk:开源 + 商业
  • Mend(原 WhiteSource)
  • 腾讯云 TSCS

10.3 依赖版本控制

xml
<!-- 统一管理 -->
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-bom</artifactId>
            <version>2.15.3</version>
            <type>pom</type>
        </dependency>
    </dependencies>
</dependencyManagement>

定期更新,跟进 CVE 公告。

十一、本章小结

主题关键措施
数据加密AES、字段级加密
密钥管理Vault、KMS
脱敏手机、身份证、邮箱
传输TLS / mTLS
备份3-2-1 原则
合规GDPR、个人信息保护法

动手练习

  1. 用 AES 实现字段级加密(用户身份证)
  2. 用 Vault 管理密钥,动态获取
  3. 写一个手机号脱敏函数,加进日志输出
  4. 配置 mTLS,实现微服务间双向认证

下一章:第 237 章:系统与应用安全

本站基于 VitePress 构建 · 由 Codebook 团队维护