Skip to content
第 48 / 250 章后端⏱ 10 分钟阅读

第 48 章:Jasypt 配置加密

学习目标

  • 理解配置明文的安全风险
  • 掌握 Jasypt 集成与使用
  • 了解密钥管理的最佳实践

一、为什么要加密配置?

yaml
# ❌ 明文密码提交到 Git
spring:
  datasource:
    password: Prod@Db#2024
  redis:
    password: RedisSecret123

风险清单

风险后果
Git 历史永久保留即使后来删了,git log -p 还能翻出来
仓库泄露私仓变公开、离职员工有 clone、外包接触过代码
运维人员可见服务器上 cat application.yml 就能看到
无法审计谁看过、谁改过密码,无迹可查

真实案例:GitHub 上每天都有扫描机器人在找泄露的密钥。AWS 密钥泄露后几分钟内就会被拿去挖矿。

二、Jasypt 集成

引入依赖

xml
<dependency>
    <groupId>com.github.ulisesbocchio</groupId>
    <artifactId>jasypt-spring-boot-starter</artifactId>
    <version>3.0.5</version>
</dependency>

配置

yaml
jasypt:
  encryptor:
    password: ${JASYPT_PASSWORD}          # ① 主密钥:从环境变量读,绝不写在文件里
    algorithm: PBEWITHHMACSHA512ANDAES_256  # ② 强算法
    iv-generator-classname: org.jasypt.iv.RandomIvGenerator
    property:
      prefix: "ENC("                       # ③ 密文标识(默认就是这个)
      suffix: ")"

使用

yaml
spring:
  datasource:
    username: root
    password: ENC(G6N718UuyPE5bHyWKyuLQSm02auQPUtm)   # ✅ 密文

Spring 启动时会自动识别 ENC(...) 包裹的值,用主密钥解密后再注入。业务代码完全无感知

三、生成密文

方式 1:命令行(推荐,不污染代码)

bash
java -cp jasypt-1.9.3.jar org.jasypt.intf.cli.JasyptPBEStringEncryptionCLI \
  input="Prod@Db#2024" \
  password="你的主密钥" \
  algorithm=PBEWITHHMACSHA512ANDAES_256 \
  ivGeneratorClassName=org.jasypt.iv.RandomIvGenerator

输出:

----OUTPUT----------------------
G6N718UuyPE5bHyWKyuLQSm02auQPUtm

方式 2:写个工具类(本地用)

java
public class JasyptTool {

    public static void main(String[] args) {
        PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();

        SimpleStringPBEConfig config = new SimpleStringPBEConfig();
        config.setPassword("你的主密钥");                              // ①
        config.setAlgorithm("PBEWITHHMACSHA512ANDAES_256");
        config.setKeyObtentionIterations("1000");
        config.setPoolSize("1");
        config.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator");
        config.setIvGeneratorClassName("org.jasypt.iv.RandomIvGenerator");
        config.setStringOutputType("base64");

        encryptor.setConfig(config);

        // ② 加密
        String encrypted = encryptor.encrypt("Prod@Db#2024");
        System.out.println("密文: ENC(" + encrypted + ")");

        // ③ 解密验证
        System.out.println("明文: " + encryptor.decrypt(encrypted));
    }
}

⚠️ 这个工具类不要提交到 Git(主密钥在里面)。放在 src/test/java 下,或者加到 .gitignore

💡 注意:同一个明文每次加密结果都不同(因为用了随机盐 + 随机 IV),但都能正确解密。这是正确行为,不是 bug——固定输出会让攻击者能做字典比对。

四、主密钥怎么管理?

这是整个方案的核心难点:配置加密了,但解密用的主密钥放哪?

✅ 方案 1:环境变量(中小项目推荐)

bash
# Linux 服务器
export JASYPT_PASSWORD='主密钥'
java -jar app.jar

# systemd
[Service]
Environment="JASYPT_PASSWORD=主密钥"

# Docker
docker run -e JASYPT_PASSWORD=主密钥 taskflow:1.0

# Kubernetes(用 Secret)
env:
  - name: JASYPT_PASSWORD
    valueFrom:
      secretKeyRef:
        name: taskflow-secret
        key: jasypt-password

❌ 方案 2:启动参数(不推荐)

bash
java -jar app.jar --jasypt.encryptor.password=主密钥

为什么不行? 服务器上任何用户执行 ps aux | grep java 都能看到完整命令行,包括密钥。

✅ 方案 3:外部文件 + 严格权限

bash
echo "主密钥" > /etc/taskflow/.jasypt-key
chmod 400 /etc/taskflow/.jasypt-key       # 只有属主可读
chown appuser:appuser /etc/taskflow/.jasypt-key
java
@Bean("jasyptStringEncryptor")
public StringEncryptor stringEncryptor() throws IOException {
    String key = Files.readString(Path.of("/etc/taskflow/.jasypt-key")).trim();

    PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();
    SimpleStringPBEConfig config = new SimpleStringPBEConfig();
    config.setPassword(key);
    config.setAlgorithm("PBEWITHHMACSHA512ANDAES_256");
    config.setIvGeneratorClassName("org.jasypt.iv.RandomIvGenerator");
    encryptor.setConfig(config);
    return encryptor;
}

⭐ 方案 4:云 KMS / Vault(大型项目)

平台服务
AWSKMS + Secrets Manager
阿里云KMS
通用HashiCorp Vault
K8sSealed Secrets / External Secrets Operator

优势:密钥永不落盘、自动轮换、完整审计日志、细粒度权限控制。

五、需要加密哪些配置?

必须加密不需要加密
数据库密码数据库连接池大小
Redis 密码服务端口
MQ 密码日志级别
第三方 API Secret(支付、短信、OSS)超时时间
JWT 签名密钥功能开关
加密盐值域名(内网 IP 建议也加密)

六、Jasypt 的局限与替代方案

局限说明
仍是对称加密拿到主密钥就能解密所有配置
无法轮换换主密钥要重新加密所有配置项
无审计不知道谁在什么时候解密了什么
主密钥仍需保护只是把「保护 N 个密码」变成「保护 1 个密钥」

准确定位:Jasypt 解决的是「配置文件不能出现明文密码」这一个问题,它不是完整的密钥管理方案。

更好的选择

yaml
# ① 配置中心(Nacos/Apollo):密码存在配置中心,有权限和审计
spring:
  cloud:
    nacos:
      config:
        server-addr: nacos:8848

# ② K8s Secret:密码作为环境变量注入,代码里完全没有
spring:
  datasource:
    password: ${DB_PASSWORD}

# ③ 云数据库 IAM 认证:连数据库都不用密码,用临时 token

优先级建议:K8s Secret / 云 IAM > 配置中心 > Jasypt > 明文。

七、避坑清单

yaml
# ❌ 坑 1:主密钥写在配置文件里 —— 等于没加密
jasypt:
  encryptor:
    password: mySecretKey            # ❌ 密钥和密文放在一起

# ❌ 坑 2:用弱算法(Jasypt 2.x 默认的 PBEWithMD5AndDES 已不安全)
jasypt:
  encryptor:
    algorithm: PBEWithMD5AndDES      # ❌ 用 PBEWITHHMACSHA512ANDAES_256

# ❌ 坑 3:yml 里密文没引号,含特殊字符时解析出错
password: ENC(abc+/=def)             # ⚠️ 建议加引号
password: "ENC(abc+/=def)"           # ✅
bash
# ❌ 坑 4:历史 Git 记录里还有明文
# 加密只对新提交有效,历史记录必须清理:
git filter-repo --path application-prod.yml --invert-paths
# 然后:立即在数据库/第三方平台上把泄露的密码全部改掉!

关键认知:一旦密码进过 Git(哪怕已删除),就必须当作已泄露处理——立即更换密码,而不是只清理仓库。

八、本章小结

要点关键
目的配置文件不出现明文密码
用法ENC(密文),Spring 自动解密
算法PBEWITHHMACSHA512ANDAES_256
主密钥环境变量 / KMS,绝不进配置文件
每次密文不同正常,随机盐 + IV
局限不是完整密钥管理方案
更优方案K8s Secret / 云 IAM / 配置中心
已泄露必须换密码,不只是清仓库

动手练习

练习 1:基础题

在项目中集成 Jasypt,把数据库密码加密,主密钥通过环境变量传入,验证应用能正常启动并连上数据库。

练习 2:思考题

你的团队有 5 个环境(dev/test/uat/pre/prod),每个环境有 8 个敏感配置。用 Jasypt 时,主密钥应该每个环境一个还是共用一个?为什么?


下一章第 49 章:日志体系

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