Spring Boot 配置体系与敏感信息治理:多环境、Bootstrap、Jasypt 到生产级 Secret 管理

欢迎你来读这篇博客。

这篇文章不只讨论 Spring Boot 配置文件敏感信息加密,而是从 Spring Boot 配置体系本身讲起,系统梳理 application.yml、多环境 Profile、bootstrap.yml、配置中心、Jasypt、Kubernetes Secret、Vault 和云厂商 Secret Manager 之间的关系。

很多项目的配置问题,表面看是“密码要不要加密”,本质上其实是:

配置放在哪里、什么时候加载、哪个环境生效、谁能读取、谁能解密、如何轮换。

如果这些问题没有想清楚,application-prod.yml 写得再整齐,也可能只是把风险按 YAML 缩进排版了一遍。

序言

在 Spring Boot 项目里,application.ymlapplication.properties 通常会保存很多运行配置,例如数据库连接、Redis 地址、MQ 账号、第三方 API Key、短信平台密钥、对象存储密钥等。

开发阶段图方便,很多人会这么写:

1
2
3
4
5
spring:
datasource:
url: jdbc:mysql://localhost:3306/demo
username: root
password: 123456

这当然能跑,但问题也很明显:一旦代码提交到 Git、被打包进镜像、发到测试环境、上传到制品库,密码就会跟着到处裸奔。

配置文件不是保险柜。把密码写进去,最多只能算“给小偷整理好了目录”。

所以,配置治理至少要回答几个问题:

  1. 配置文件应该怎么拆?
  2. 多环境配置应该怎么管理?
  3. application.ymlbootstrap.yml 分别适合放什么?
  4. 敏感信息是否应该出现在代码仓库里?
  5. 如果必须放在配置文件里,能否以密文形式保存?
  6. 解密密钥、访问凭证、轮换机制应该由谁来管理?

这篇文章从 Spring Boot 常见场景出发,先讲配置体系,再讲敏感信息加密,最后扩展到配置中心、Kubernetes Secret、Vault、云厂商 Secret Manager 等更适合生产环境的方案。

正文

chapter 1:Spring Boot 配置为什么容易泄露敏感信息?

Spring Boot 的配置系统非常灵活。它可以从很多地方读取配置,例如:

  • application.yml
  • application.properties
  • application-dev.yml
  • 环境变量
  • JVM 系统属性
  • 命令行参数
  • 外部配置目录
  • 配置中心
  • Kubernetes Secret / ConfigMap
  • Vault / Secret Manager

灵活是好事,但灵活也意味着风险更分散。

常见泄露场景包括:

  1. 配置文件提交到 Git

    开发人员把数据库密码、Redis 密码、第三方平台密钥直接写在 application.yml,然后提交到仓库。即便后面删掉,Git 历史里也可能还在。

  2. 打包进 Jar 或镜像

    如果生产密码写在项目资源目录里,它会被一起打进 Jar 包或 Docker 镜像。只要有人拿到制品,就有机会解压看到配置。

  3. 日志误打印

    有些项目启动时会打印配置对象,或者异常堆栈里带出连接串,敏感信息可能直接进入日志系统。

  4. 配置中心权限过大

    微服务项目常用 Nacos、Apollo、Spring Cloud Config 等配置中心。如果所有开发、测试、运维人员都能看到生产配置,那配置中心就变成了“密码公告栏”。

  5. 密钥和密文放在一起

    有人把配置值加密了,但又把解密密钥也写在同一个 application.yml。这就像门上挂了把锁,然后把钥匙贴在门框上。形式上安全,实际上很尴尬。

所以,配置安全的第一原则是:

明文敏感信息不要进入代码仓库,解密密钥不要和密文放在同一个地方。

chapter 2:先理解 Spring Boot 配置文件体系

在谈配置加密之前,先要搞清楚 Spring Boot 配置文件到底是怎么组织的。

Spring Boot 常见配置文件有两类:

1
2
application.properties
application.yml

两者本质上都用于描述配置属性,只是格式不同。

properties 更直接:

1
2
3
spring.datasource.url=jdbc:mysql://localhost:3306/demo
spring.datasource.username=root
spring.datasource.password=123456

yml 层级更清晰:

1
2
3
4
5
spring:
datasource:
url: jdbc:mysql://localhost:3306/demo
username: root
password: 123456

实际项目里更推荐统一使用一种格式,不要一个项目里同时大量混用 application.ymlapplication.properties。否则同一个配置项到底谁覆盖谁,很容易把自己绕进去。

2.1 application.yml 适合放什么?

application.yml 适合放所有环境通用的默认配置,例如:

  • 应用名称
  • 日志基础配置
  • Jackson 序列化规则
  • MyBatis / JPA 基础配置
  • Actuator 基础配置
  • 业务开关默认值
  • 非敏感的默认参数

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
spring:
application:
name: order-service

server:
port: 8080

logging:
level:
root: info

management:
endpoints:
web:
exposure:
include: health,info,prometheus

注意:

默认配置不是生产配置,更不是敏感配置仓库。

application.yml 可以提交 Git,但里面不应该出现生产数据库密码、生产 Redis 密码、生产 API Key、云厂商 AccessKey 等敏感信息。

2.2 外部化配置的核心思想

Spring Boot 配置管理的核心思想是:

同一份代码,在不同环境里通过外部化配置产生不同行为。

也就是说,代码不应该因为部署到开发、测试、预发、生产环境而改变,改变的应该是外部配置。

常见外部化方式包括:

1
2
3
4
5
6
7
8
9
10
11
12
# 环境变量
export DB_USERNAME=root
export DB_PASSWORD=123456

# 启动参数
java -jar app.jar --spring.profiles.active=prod

# JVM 系统属性
java -Dspring.profiles.active=prod -jar app.jar

# 外部配置文件
java -jar app.jar --spring.config.location=/opt/app/config/application.yml

这个思想很重要。

很多配置混乱的项目,本质上不是不会写 YAML,而是把“构建产物”和“运行环境”绑死了。一个 Jar 包对应一套密码,一个镜像对应一套环境,这种方式后面一定难维护。

chapter 3:Spring Boot 多环境配置怎么做?

Spring Boot 多环境配置主要依赖 Profile。

常见环境可以拆成:

1
2
3
4
dev    本地开发环境
test 测试环境
uat 预发环境
prod 生产环境

对应配置文件:

1
2
3
4
5
application.yml
application-dev.yml
application-test.yml
application-uat.yml
application-prod.yml

推荐分工如下:

1
2
3
4
5
application.yml        放通用默认配置
application-dev.yml 放开发环境差异配置
application-test.yml 放测试环境差异配置
application-uat.yml 放预发环境差异配置
application-prod.yml 放生产环境非敏感差异配置

3.1 激活指定环境

可以通过配置文件激活:

1
2
3
spring:
profiles:
active: dev

也可以通过环境变量激活:

1
2
export SPRING_PROFILES_ACTIVE=prod
java -jar app.jar

也可以通过启动参数激活:

1
java -jar app.jar --spring.profiles.active=prod

在真实生产环境里,更推荐通过环境变量、启动参数、部署平台变量来指定 Profile,而不是把生产 Profile 写死在 Jar 包里的 application.yml

也就是说:

1
2
3
spring:
profiles:
active: prod

这类配置尽量不要直接提交在默认配置里。

否则某个开发同学本地一启动,发现连接的是生产库,这个场面就不是“惊喜”,是事故预告片。

3.2 多环境配置示例

application.yml

1
2
3
4
5
6
7
8
9
10
spring:
application:
name: order-service

server:
port: 8080

app:
feature:
settlement-enabled: true

application-dev.yml

1
2
3
4
5
6
7
8
9
spring:
datasource:
url: jdbc:mysql://localhost:3306/order_dev
username: ${DB_USERNAME:root}
password: ${DB_PASSWORD:123456}

logging:
level:
com.example.order: debug

application-prod.yml

1
2
3
4
5
6
7
8
9
spring:
datasource:
url: ${DB_URL}
username: ${DB_USERNAME}
password: ${DB_PASSWORD}

logging:
level:
com.example.order: info

这里有一个关键差异:

  • dev 环境可以给默认值,方便本地启动。
  • prod 环境不建议给敏感默认值,必须由部署平台注入。

生产配置里如果写成这样:

1
2
3
spring:
datasource:
password: ${DB_PASSWORD:123456}

那就很危险。因为一旦环境变量漏配,应用可能用默认密码启动。默认值在开发环境是便利,在生产环境可能是坑,坑还带自动扶梯。

3.3 使用 Profile Group 管理组合环境

有些项目的环境配置不是一个 Profile 能表达的,例如:

  • prod-db
  • prod-redis
  • prod-mq
  • prod-observability

这时可以使用 Profile Group,把多个 Profile 组合起来:

1
2
3
4
5
6
7
8
spring:
profiles:
group:
prod:
- prod-db
- prod-redis
- prod-mq
- prod-observability

启动时只需要:

1
java -jar app.jar --spring.profiles.active=prod

实际会激活:

1
2
3
4
5
prod
prod-db
prod-redis
prod-mq
prod-observability

这种方式比把所有生产配置塞进一个巨大的 application-prod.yml 更清晰。

3.4 使用 spring.config.activate.on-profile

除了拆多个文件,也可以在一个 YAML 文件里用多文档形式写不同环境配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
spring:
application:
name: order-service

---
spring:
config:
activate:
on-profile: dev

logging:
level:
com.example.order: debug

---
spring:
config:
activate:
on-profile: prod

logging:
level:
com.example.order: info

这种写法适合配置量不大、希望集中管理的场景。

但是如果配置很多,还是建议拆成多个文件,不然一个 application.yml 会变成“配置年鉴”。

3.5 多环境配置的几个原则

建议遵守下面几条原则:

  1. application.yml 只放通用默认配置。
  2. application-prod.yml 不放明文敏感信息。
  3. spring.profiles.active 不要在默认配置里写死为 prod
  4. 开发环境可以有默认值,生产环境尽量不要给敏感默认值。
  5. 不同环境使用不同账号、不同密码、不同密钥。
  6. 本地 .envapplication-local.yml 不提交 Git。
  7. 配置项命名要稳定,不要同一个含义在不同环境叫不同名字。
  8. 配置变更要有发布记录,生产配置变更不能靠“我记得改过”。

chapter 4:bootstrap.yml 是什么?和 application.yml 有什么区别?

很多 Spring Cloud 项目里会看到:

1
2
3
4
bootstrap.yml
bootstrap.properties
application.yml
application.properties

新手最容易困惑的问题是:

为什么有了 application.yml,还要 bootstrap.yml?

简单理解:

1
2
bootstrap.yml    更早加载,主要用于启动主应用上下文之前的引导配置
application.yml 主应用配置,承载大多数业务和框架配置

在传统 Spring Cloud 场景里,bootstrap.yml 主要用于配置那些“应用启动早期就必须知道”的信息,例如:

  • 应用名 spring.application.name
  • 配置中心地址
  • 配置中心命名空间
  • 配置中心认证信息
  • 注册中心早期配置
  • 解密相关的早期配置
  • 远程配置拉取所需参数

典型示例:

1
2
3
4
5
6
7
8
spring:
application:
name: order-service
cloud:
config:
uri: ${SPRING_CONFIG_URI:http://localhost:8888}
username: ${CONFIG_USERNAME}
password: ${CONFIG_PASSWORD}

这里的逻辑是:

应用要先知道去哪里拉远程配置,才能继续加载真正的业务配置。

所以配置中心地址这类“寻找配置的配置”,适合放在 bootstrap.yml 里。

4.1 bootstrap.yml 适合放什么?

一般适合放:

1
2
3
4
5
6
7
8
9
10
11
spring:
application:
name: order-service
cloud:
nacos:
config:
server-addr: ${NACOS_SERVER_ADDR}
namespace: ${NACOS_NAMESPACE}
group: ${NACOS_GROUP:DEFAULT_GROUP}
username: ${NACOS_USERNAME}
password: ${NACOS_PASSWORD}

或者 Spring Cloud Config:

1
2
3
4
5
6
7
8
spring:
application:
name: order-service
cloud:
config:
uri: ${SPRING_CONFIG_URI}
label: ${SPRING_CONFIG_LABEL:main}
profile: ${SPRING_PROFILES_ACTIVE:dev}

它们的共同点是:

这些配置是为了把远程配置源接进来,而不是普通业务配置。

4.2 application.yml 适合放什么?

application.yml 仍然是主配置文件,适合放:

  • Web 服务端口
  • 数据源配置
  • Redis 配置
  • MQ 配置
  • 日志配置
  • 业务配置
  • Actuator 配置
  • 线程池配置
  • 限流配置
  • 业务开关

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
server:
port: 8080

spring:
datasource:
url: ${DB_URL}
username: ${DB_USERNAME}
password: ${DB_PASSWORD}

app:
settlement:
enabled: true
batch-size: 500

4.3 Spring Boot 2.4+ 之后:不一定非要 bootstrap.yml

需要注意的是,Spring Boot 2.4 引入了新的 Config Data 机制,很多外部配置可以通过 spring.config.import 引入。

例如 Spring Cloud Config Client 可以这样写:

1
2
3
4
5
spring:
application:
name: order-service
config:
import: optional:configserver:${SPRING_CONFIG_URI:http://localhost:8888}

Vault 可以这样写:

1
2
3
4
5
spring:
application:
name: order-service
config:
import: vault://

也就是说,在较新的 Spring Boot / Spring Cloud 项目里,很多场景可以不使用 bootstrap.yml,而是使用 spring.config.import

所以,比较稳妥的判断方式是:

1
2
3
4
5
6
7
8
老 Spring Cloud 项目 / 公司已有 bootstrap 规范:
可以继续使用 bootstrap.yml,但要保持边界清晰。

新 Spring Boot 2.4+ / Spring Boot 3.x 项目:
优先考虑 spring.config.import,除非具体组件或团队规范要求使用 bootstrap.yml。

Nacos / Apollo / Config Server 等配置中心:
按你使用的 starter 和版本要求决定,不要凭感觉混用。

一句话:

bootstrap.yml 不是废弃知识,但也不是所有 Spring Boot 项目的标配。

4.4 bootstrap 和 application 的常见误区

误区一:把所有配置都放 bootstrap.yml

不建议。

bootstrap.yml 应该尽量薄,只放引导配置。普通业务配置还是放 application.yml、Profile 文件或配置中心。

误区二:把生产密码放 bootstrap.yml

不建议。

bootstrap.yml 更早加载,不代表它更安全。它如果被提交到 Git,泄露风险一样存在。

误区三:同时用 bootstrap.yml 和 spring.config.import,但边界不清

这种情况最容易出现“配置到底从哪里来”的问题。

建议团队统一规则:

1
2
方案 A:bootstrap.yml 负责配置中心接入。
方案 B:application.yml + spring.config.import 负责配置中心接入。

不要两个方案混着用,除非你非常清楚加载顺序和覆盖关系。

误区四:以为 application-prod.yml 一定覆盖远程配置

不一定。

不同配置来源有不同优先级,配置中心、启动参数、环境变量、外部文件、本地文件之间的覆盖关系需要根据实际版本和配置方式确认。生产排查配置问题时,不要只看一个文件,要看完整的 PropertySource 链路。

chapter 5:配置加载优先级与生产排查思路

Spring Boot 外部化配置的来源很多,常见包括:

  • 默认属性
  • @PropertySource
  • 配置文件
  • Profile 配置文件
  • 环境变量
  • JVM 系统属性
  • 命令行参数
  • 测试配置
  • Devtools 配置
  • 配置中心
  • Vault / Secret Manager
  • Kubernetes ConfigMap / Secret

实际排查时,不建议死记完整优先级表,而是记住一个原则:

越靠近运行时、越靠近部署平台、越靠近命令行的配置,通常越容易覆盖打包在应用内部的配置。

例如:

1
java -jar app.jar --server.port=9090

大概率会覆盖 application.yml 里的:

1
2
server:
port: 8080

再比如环境变量:

1
export SPRING_DATASOURCE_PASSWORD=prod-secret

可以绑定到:

1
2
3
spring:
datasource:
password: ${SPRING_DATASOURCE_PASSWORD}

5.1 配置排查时看什么?

线上配置异常时,建议按下面顺序排查:

  1. 当前激活了哪些 Profile?
  2. 目标配置项在几个地方出现过?
  3. 是否存在命令行参数覆盖?
  4. 是否存在环境变量覆盖?
  5. 是否存在外部配置文件覆盖?
  6. 是否从配置中心拉到了远程配置?
  7. 是否存在 bootstrap 阶段加载的 PropertySource?
  8. 是否被 Kubernetes Secret / ConfigMap 注入?
  9. 是否被 CI/CD 平台变量覆盖?
  10. 是否存在默认值兜底导致误启动?

5.2 推荐配置分层

一个更清晰的分层方式是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
第一层:application.yml
放通用默认配置,不放敏感信息。

第二层:application-{profile}.yml
放环境差异配置,生产文件不放明文敏感信息。

第三层:配置中心
放需要集中管理、动态调整、跨服务共享的配置。

第四层:Secret 注入
放数据库密码、Redis 密码、API Key、Token、证书、私钥等敏感信息。

第五层:启动参数 / 环境变量
放部署时决定的参数,例如 profile、端口、配置中心地址、密钥引用。

如果再结合 bootstrap,可以这样理解:

1
2
3
4
5
6
7
8
9
10
11
bootstrap.yml
解决“去哪找配置”的问题。

application.yml
解决“应用默认怎么运行”的问题。

application-{profile}.yml
解决“不同环境有什么差异”的问题。

配置中心 / Secret Manager
解决“配置如何集中治理、权限控制、审计、轮换”的问题。

chapter 6:Jasypt 是什么?

Jasypt,全称 Java Simplified Encryption,是 Java 生态里常用的加解密工具。结合 jasypt-spring-boot-starter 后,它可以让 Spring Boot 在读取配置属性时自动识别密文,并在注入到 Spring Environment 前完成解密。

典型效果是这样:

1
2
3
4
spring:
datasource:
username: ENC(加密后的用户名)
password: ENC(加密后的密码)

应用启动时,Jasypt 会识别 ENC(...),用指定的解密密钥把里面的密文解开,最终业务代码拿到的仍然是正常的明文值。

也就是说,代码里依旧可以这样使用:

1
2
@Value("${spring.datasource.password}")
private String password;

或者:

1
2
3
4
5
@ConfigurationProperties(prefix = "spring.datasource")
public class DataSourceProperties {
private String username;
private String password;
}

业务代码无需关心加解密过程。

这就是 Jasypt 的核心价值:

配置文件里存密文,应用启动时自动解密,业务代码无感知。

chapter 7:Spring Boot 集成 Jasypt

以 Maven 项目为例,先引入依赖。

1
2
3
4
5
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>4.0.4</version>
</dependency>

然后在配置文件里设置加密算法和密文识别规则:

1
2
3
4
5
6
7
jasypt:
encryptor:
algorithm: PBEWITHHMACSHA512ANDAES_256
iv-generator-classname: org.jasypt.iv.RandomIvGenerator
property:
prefix: ENC(
suffix: )

注意,这里故意没有写:

1
2
3
jasypt:
encryptor:
password: xxx

因为 jasypt.encryptor.password 是解密密钥,不能和密文一起放在配置文件中。

更推荐通过环境变量、JVM 参数或启动参数传入:

1
java -jar app.jar --jasypt.encryptor.password=your-secret-key

或者:

1
2
export JASYPT_ENCRYPTOR_PASSWORD=your-secret-key
java -jar app.jar

在 Docker 或 Kubernetes 环境里,也可以通过 Secret 注入环境变量。

7.1 Jasypt 和多环境配置怎么配合?

Jasypt 可以和 Profile 配合使用。

例如开发环境:

1
2
3
4
5
6
# application-dev.yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/demo_dev
username: root
password: 123456

测试环境:

1
2
3
4
5
6
# application-test.yml
spring:
datasource:
url: jdbc:mysql://test-db:3306/demo
username: ENC(test-encrypted-username)
password: ENC(test-encrypted-password)

生产环境:

1
2
3
4
5
6
# application-prod.yml
spring:
datasource:
url: ${DB_URL}
username: ENC(prod-encrypted-username)
password: ENC(prod-encrypted-password)

对应环境的解密密钥应该分别注入:

1
2
3
4
5
# test
export JASYPT_ENCRYPTOR_PASSWORD=test-secret-key

# prod
export JASYPT_ENCRYPTOR_PASSWORD=prod-secret-key

不要所有环境共用同一个 Jasypt 密钥。

测试环境密钥泄露,不应该导致生产环境一起陪葬。

7.2 Jasypt 配置应该放 application 还是 bootstrap?

这要看密文出现在哪里。

如果密文只出现在本地 application.ymlapplication-{profile}.yml,通常放在 application.yml 就可以:

1
2
3
jasypt:
encryptor:
algorithm: PBEWITHHMACSHA512ANDAES_256

如果密文出现在配置中心,并且配置中心相关内容是在 bootstrap 阶段加载,那么 Jasypt 的部分配置可能也需要在 bootstrap 阶段可用。

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# bootstrap.yml
spring:
application:
name: order-service
cloud:
nacos:
config:
server-addr: ${NACOS_SERVER_ADDR}

jasypt:
encryptor:
algorithm: PBEWITHHMACSHA512ANDAES_256
property:
prefix: ENC(
suffix: )

但解密密钥仍然不要写进去:

1
export JASYPT_ENCRYPTOR_PASSWORD=prod-secret-key

一句话:

密文在哪里加载,解密能力就要在那个阶段可用;但密钥永远不要和密文放在一起。

chapter 8:如何生成密文?

Jasypt 常见有两种方式生成密文。

方式一:写测试类生成密文

可以注入 StringEncryptor

1
2
3
4
5
6
7
8
9
10
11
12
13
@SpringBootTest
class JasyptEncryptTest {

@Autowired
private StringEncryptor stringEncryptor;

@Test
void encrypt() {
System.out.println(stringEncryptor.encrypt("root"));
System.out.println(stringEncryptor.encrypt("123456"));
System.out.println(stringEncryptor.encrypt("jdbc:mysql://localhost:3306/demo"));
}
}

然后将输出结果放进配置文件:

1
2
3
4
5
spring:
datasource:
url: ENC(xxx)
username: ENC(xxx)
password: ENC(xxx)

这种方式简单,但容易把明文、密文、测试代码混在项目里。临时验证可以,长期使用不优雅。

方式二:使用 jasypt-maven-plugin

可以在 pom.xml 中加入插件:

1
2
3
4
5
6
7
8
9
<build>
<plugins>
<plugin>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-maven-plugin</artifactId>
<version>4.0.4</version>
</plugin>
</plugins>
</build>

先在配置文件中用 DEC(...) 包住待加密值:

1
2
spring.datasource.username=DEC(root)
spring.datasource.password=DEC(123456)

执行:

1
mvn jasypt:encrypt -Djasypt.encryptor.password="your-secret-key"

插件会把 DEC(...) 转换成 ENC(...)

1
2
spring.datasource.username=ENC(encrypted-username)
spring.datasource.password=ENC(encrypted-password)

如果只想加密单个值,可以执行:

1
2
3
mvn jasypt:encrypt-value \
-Djasypt.encryptor.password="your-secret-key" \
-Djasypt.plugin.value="123456"

这个方式更适合工程化使用。

chapter 9:Jasypt 的关键安全点

Jasypt 很好用,但别把它当成“万能保险箱”。它解决的是“配置文件密文存储”的问题,不解决所有 Secret 管理问题。

1. 密文可以提交,密钥不能提交

这是底线。

可以提交:

1
2
3
spring:
datasource:
password: ENC(xxxxxx)

不要提交:

1
2
3
jasypt:
encryptor:
password: your-secret-key

密文和密钥一旦放在一起,加密就失去了意义。

2. 不同环境使用不同密钥

开发、测试、预发、生产环境应该使用不同的加密密钥。

不要所有环境共用一个 jasypt.encryptor.password。否则测试环境泄露,生产环境也跟着裸奔。

建议:

1
2
3
4
5
6
7
8
# dev
JASYPT_ENCRYPTOR_PASSWORD=dev-key

# test
JASYPT_ENCRYPTOR_PASSWORD=test-key

# prod
JASYPT_ENCRYPTOR_PASSWORD=prod-key

3. 不要在日志中打印敏感配置

尤其是:

  • 数据库密码
  • Redis 密码
  • MQ 密码
  • Token
  • SecretKey
  • AccessKey
  • RefreshToken
  • 私钥内容

建议对配置对象的 toString() 做脱敏,或者禁止敏感字段打印。

示例:

1
2
3
4
5
6
private String mask(String value) {
if (value == null || value.length() <= 4) {
return "****";
}
return value.substring(0, 2) + "****" + value.substring(value.length() - 2);
}

日志里宁可少一点,也别把公司数据库密码“直播”出去。

4. 注意 Actuator 暴露风险

Spring Boot Actuator 的 /env/configprops 等端点可能暴露配置信息。生产环境要谨慎开放。

建议:

1
2
3
4
5
management:
endpoints:
web:
exposure:
include: health,info,prometheus

不要随手暴露所有端点:

1
2
3
4
5
management:
endpoints:
web:
exposure:
include: "*"

这属于“门开着,狗还帮你摇尾巴迎客”。

5. 定期轮换密钥

加密密钥不是祖传玉玺,不能一用十年。

一旦人员离职、仓库泄露、CI/CD 变量泄露、配置中心权限异常,都应该考虑轮换密钥。

Jasypt 轮换密钥的一般思路是:

  1. 用旧密钥解密旧配置。
  2. 用新密钥重新加密配置。
  3. 更新部署环境中的解密密钥。
  4. 重启或滚动发布服务。
  5. 删除旧密钥。

chapter 10:Jasypt 适合什么场景?

Jasypt 适合以下场景:

  1. 单体 Spring Boot 应用

    项目不复杂,配置主要在 application.yml 中,使用 Jasypt 可以快速避免明文密码入库。

  2. 中小型后台系统

    没有复杂的 Secret 管理平台,但又不希望配置中心或 Git 仓库里直接出现明文密码。

  3. 遗留系统改造

    老项目里已经有大量配置文件,短期内无法接入 Vault 或云厂商 Secret Manager,可以先用 Jasypt 做一层改造。

  4. 本地开发和测试环境

    对安全有要求,但暂时不想引入完整的密钥管理基础设施。

但如果是更复杂的生产系统,尤其是微服务、多环境、多租户、多团队协作,就不能只依赖 Jasypt。

chapter 11:配置中心场景应该怎么做?

在微服务体系里,配置常常放在 Nacos、Apollo、Spring Cloud Config 等配置中心。

这时要区分两个问题:

  1. 配置中心里是否允许存明文?
  2. 应用启动后如何拿到明文?

如果使用 Spring Cloud Config,它本身支持 {cipher} 形式的加密配置。配置仓库中可以保存:

1
2
3
spring:
datasource:
password: "{cipher}加密后的内容"

Config Server 读取后会先解密,再把明文配置发给客户端。

它的好处是:

  • Git 仓库里不是明文。
  • 加解密集中在 Config Server。
  • 客户端不需要每个服务都维护解密逻辑。

但是也要注意:

  • Config Server 的加密密钥必须保护好。
  • /encrypt/decrypt 端点必须加认证和访问控制。
  • 不要让所有人都能调用解密接口。
  • 配置中心和配置仓库权限要分级。

如果使用 Nacos 或 Apollo,常见做法有两种。

第一种是配置中心保存 Jasypt 密文,应用侧用 Jasypt 解密:

1
2
3
spring:
datasource:
password: ENC(xxxxxx)

第二种是配置中心或平台侧提供加密插件、密文管理、权限控制。

实际落地时,我更推荐按照团队成熟度选择:

  • 小团队:Jasypt + 环境变量密钥。
  • 中型团队:配置中心 + Jasypt + 权限隔离。
  • 大型团队:配置中心只放非敏感配置,敏感信息交给 Vault、KMS、Secret Manager。

11.1 配置中心和多环境怎么对应?

配置中心里也要有环境隔离。

以 Nacos 为例,常见隔离维度有:

1
2
3
4
namespace    隔离环境,例如 dev/test/uat/prod
group 隔离业务组或应用组
dataId 隔离具体配置文件
profile 隔离不同运行环境

一个常见命名方式:

1
2
3
4
order-service.yaml
order-service-dev.yaml
order-service-test.yaml
order-service-prod.yaml

或者:

1
2
3
namespace: prod
group: ORDER_GROUP
dataId: order-service.yaml

建议:

  1. dev/test/prod 使用不同 namespace。
  2. 生产 namespace 权限单独控制。
  3. 生产敏感配置不要所有开发人员可见。
  4. 配置变更要有审批和发布记录。
  5. 配置中心不要替代 Secret Manager。
  6. 配置中心账号密码本身也要通过环境变量或 Secret 注入。

配置中心不是“更高级的 application.yml”。它是一个运行时配置平台,一旦权限失控,影响范围比本地配置文件更大。

chapter 12:Kubernetes 场景:Secret 不是终点

在 Kubernetes 中,可以使用 Secret 保存密码、Token、证书等敏感数据。

例如:

1
2
3
kubectl create secret generic db-secret \
--from-literal=username=root \
--from-literal=password=123456

然后在 Deployment 中注入环境变量:

1
2
3
4
5
6
7
8
9
10
11
env:
- name: SPRING_DATASOURCE_USERNAME
valueFrom:
secretKeyRef:
name: db-secret
key: username
- name: SPRING_DATASOURCE_PASSWORD
valueFrom:
secretKeyRef:
name: db-secret
key: password

Spring Boot 可以自动把环境变量绑定到配置项:

1
2
3
4
spring:
datasource:
username: ${SPRING_DATASOURCE_USERNAME}
password: ${SPRING_DATASOURCE_PASSWORD}

也可以把 Secret 挂载为文件,然后通过 Spring Boot 的 configtree: 读取。

例如:

1
2
3
spring:
config:
import: optional:configtree:/etc/secrets/

这种方式适合 Docker Secret、Kubernetes Secret Volume 等场景。

不过要注意,Kubernetes Secret 默认并不等于绝对安全。它更像是 Kubernetes 原生的敏感信息承载对象,而不是完整的密钥管理系统。生产环境至少要考虑:

  • etcd 加密存储。
  • RBAC 最小权限。
  • 禁止无关 Pod 读取 Secret。
  • 限制 namespace 权限。
  • 尽量通过 Volume 挂载而不是 API 读取。
  • 高安全场景接入外部 Secret Store。

chapter 13:Vault / Secret Manager:更生产级的方案

当系统规模变大后,Jasypt 的短板会越来越明显:

  • 密钥轮换麻烦。
  • 谁能解密不容易审计。
  • 密钥生命周期不够清晰。
  • 多服务、多环境、多团队管理复杂。
  • 无法很好支持动态凭证。

这时可以考虑使用:

  • HashiCorp Vault
  • AWS Secrets Manager
  • Azure Key Vault
  • Google Secret Manager
  • 阿里云 KMS / 凭据管家
  • 腾讯云 KMS / 凭据管理

以 Vault 为例,Spring Cloud Vault 可以通过 spring.config.import 引入 Vault 中的配置:

1
2
3
4
5
spring:
application:
name: order-service
config:
import: vault://

这样应用启动时会从 Vault 拉取配置,敏感信息不需要写进 application.yml

Vault 这类工具的核心价值不是“把字符串加密一下”,而是完整的 Secret 生命周期管理:

  • 集中存储。
  • 权限控制。
  • 审计日志。
  • 动态凭证。
  • 自动续租。
  • 密钥轮换。
  • 凭证吊销。

如果项目已经上云,云厂商 Secret Manager 也很适合。比如 AWS Secrets Manager 可以保存数据库账号、API Key 等,并支持自动轮换和按需读取。

chapter 14:不同方案怎么选?

可以按复杂度分层选择。

1. 个人项目 / 小型后台

推荐:

1
环境变量 + application.yml 占位符

示例:

1
2
3
4
spring:
datasource:
username: ${DB_USERNAME}
password: ${DB_PASSWORD}

优点是简单、直接、不引入额外依赖。

2. 已经有明文配置的老项目

推荐:

1
Jasypt

改造成本低,业务代码基本无感知。

3. 使用配置中心的微服务

推荐:

1
配置中心 + Jasypt / Spring Cloud Config 加密能力

重点是做好配置中心权限隔离,不要让所有人都能看生产配置。

4. Kubernetes 部署

推荐:

1
Kubernetes Secret + RBAC + etcd 加密 + Volume 挂载

如果安全要求更高,可以接入 External Secrets Operator 或 Secret Store CSI Driver。

5. 金融、支付、SaaS 多租户、强合规系统

推荐:

1
Vault / KMS / Secret Manager

这种场景不要只靠 Jasypt。Jasypt 是工具,不是治理体系。

chapter 15:一个推荐落地方案

如果现在有一个普通 Spring Boot / Spring Cloud 项目,我会这样落地。

本地开发环境

.env 或本机环境变量保存敏感信息,不提交 Git。

1
2
export DB_USERNAME=root
export DB_PASSWORD=123456

配置文件:

1
2
3
4
spring:
datasource:
username: ${DB_USERNAME}
password: ${DB_PASSWORD}

如果需要本地专属配置,可以使用:

1
application-local.yml

但要加入 .gitignore

1
2
3
4
application-local.yml
.env
*.pem
*.key

启动时:

1
java -jar app.jar --spring.profiles.active=local

测试环境

配置中心保存非敏感配置和必要的 Jasypt 密文:

1
2
3
spring:
datasource:
password: ENC(xxxxxx)

解密密钥通过启动参数或环境变量注入:

1
JASYPT_ENCRYPTOR_PASSWORD=test-secret-key

生产环境

优先使用 Secret Manager / Vault / Kubernetes Secret。

如果短期只能用 Jasypt,则至少做到:

  • 生产密钥只在部署平台配置。
  • 生产密钥不进入 Git。
  • 生产密钥不出现在日志。
  • 生产配置中心权限严格隔离。
  • 定期轮换数据库密码和加密密钥。
  • CI/CD 平台的变量权限单独控制。
  • Actuator 端点只暴露必要能力。

推荐项目配置结构

一个相对清晰的项目配置结构可以这样设计:

1
2
3
4
5
6
7
src/main/resources/
├── application.yml
├── application-dev.yml
├── application-test.yml
├── application-uat.yml
├── application-prod.yml
└── bootstrap.yml # 仅在需要 Spring Cloud bootstrap 时保留

如果使用 spring.config.import,也可以不保留 bootstrap.yml

1
2
3
4
5
6
src/main/resources/
├── application.yml
├── application-dev.yml
├── application-test.yml
├── application-uat.yml
└── application-prod.yml

生产敏感配置不要放在上面这些文件里,而是放在:

1
2
3
4
5
环境变量
Kubernetes Secret
Vault
Secret Manager
CI/CD 受保护变量

chapter 16:常见误区

误区一:配置加密后就绝对安全

不是。

配置加密只解决“静态配置文件不存明文”的问题。应用运行时最终还是要拿到明文,否则它无法连接数据库、Redis、MQ。

所以真正的重点是:

  • 谁能拿到密文?
  • 谁能拿到密钥?
  • 谁能触发解密?
  • 解密过程有没有审计?
  • 密钥能不能轮换?

误区二:把 Jasypt 密钥写在 application.yml

这等于没加密。

错误示例:

1
2
3
4
5
6
7
spring:
datasource:
password: ENC(xxxxxx)

jasypt:
encryptor:
password: prod-secret-key

正确方向:

1
java -jar app.jar --jasypt.encryptor.password=prod-secret-key

或者使用部署平台注入环境变量。

误区三:所有敏感信息都用同一个密钥

不建议。

至少应该区分环境。更进一步,可以按业务域、系统等级、租户隔离策略拆分密钥。

误区四:配置中心权限随便给

配置中心是微服务时代的“中枢神经”。如果生产配置人人可见,那加密、认证、网关、审计都可能被绕开。

配置中心权限建议分层:

  • 开发人员:可看开发环境。
  • 测试人员:可看测试环境。
  • 运维人员:可管理部署变量。
  • 生产敏感配置:最小化授权。
  • 解密密钥:只给部署平台或密钥管理系统。

误区五:忽略历史提交

如果明文密码曾经提交过 Git,只删除当前文件是不够的。

要做的是:

  1. 立即更换已泄露密码。
  2. 清理 Git 历史或更换仓库访问权限。
  3. 检查 CI/CD、制品库、镜像仓库。
  4. 检查日志系统是否记录过明文。
  5. 增加 Git secret 扫描。

误区六:Profile 当成安全边界

Profile 是环境隔离机制,不是安全机制。

application-prod.yml 被提交到 Git 后,不会因为文件名带 prod 就自动变安全。

真正的安全边界应该来自:

  • 权限控制
  • Secret 管理
  • 部署平台隔离
  • 审计日志
  • 密钥轮换
  • 最小权限

误区七:bootstrap.yml 放得越多越专业

不是。

bootstrap.yml 的价值在于“引导”,不是“承包所有配置”。

如果一个项目里 80% 配置都在 bootstrap.yml,那大概率是边界错了。

chapter 17:实践清单

最后给一份检查清单。

代码仓库

  • 不提交明文密码。
  • 不提交 .env
  • 不提交私钥文件。
  • 不提交生产配置。
  • 添加 .gitignore
  • 接入 secret scanning。

配置文件

  • 敏感字段使用占位符或密文。
  • 不在配置文件里写解密密钥。
  • 不在默认 profile 里放生产配置。
  • 不同环境配置隔离。
  • application.yml 放通用默认配置。
  • application-{profile}.yml 放环境差异配置。
  • bootstrap.yml 只放引导配置。
  • 新项目优先评估 spring.config.import

启动部署

  • 密钥通过环境变量、启动参数、Secret Volume 或密钥管理平台注入。
  • CI/CD 变量做权限控制。
  • 生产环境禁止打印完整启动参数。
  • 容器镜像不内置生产密钥。
  • SPRING_PROFILES_ACTIVE 由部署平台注入。
  • 配置中心地址和认证信息不要写死。

应用运行

  • 日志脱敏。
  • Actuator 最小暴露。
  • 配置中心访问控制。
  • 数据库账号最小权限。
  • 定期轮换密码和密钥。
  • 监控配置变更。
  • 对关键配置变更增加审计。

应急处理

  • 发现泄露后第一时间换密钥。
  • 不要只删配置文件。
  • 检查 Git 历史、日志、镜像、制品、配置中心。
  • 复盘权限链路。
  • 补充自动化扫描和发布前检查。

参考资料

  • Spring Boot Reference Documentation:Externalized Configuration
  • Spring Boot Reference Documentation:Profiles
  • Spring Boot Reference Documentation:Common Application Properties
  • Spring Cloud Commons Reference Documentation:Application Context Services / Bootstrap
  • Spring Cloud Config Reference Documentation:Config Client
  • Spring Cloud Config Reference Documentation:Encryption and Decryption
  • Spring Cloud Config Reference Documentation:Key Management
  • ulisesbocchio / jasypt-spring-boot 官方 README
  • Spring Cloud Kubernetes:Secrets PropertySource
  • Kubernetes Documentation:Secrets
  • Spring Cloud Vault Reference Documentation
  • AWS Secrets Manager Documentation

启示录

配置治理这件事,表面看是“配置文件怎么写”,深一层是“环境如何隔离”,再深一层是“谁能在什么时候,以什么权限,拿到什么秘密”。

application.yml 解决默认配置问题,Profile 解决环境差异问题,bootstrap.ymlspring.config.import 解决早期配置源接入问题,Jasypt 解决配置文件密文存储问题,Vault / KMS / Secret Manager 解决 Secret 生命周期治理问题。

不要把它们混成一锅粥。

配置写得越清楚,系统越容易部署;密钥管得越克制,事故越不容易找上门。

安全不是把门锁上这么简单,还要知道钥匙在谁手里、谁配过钥匙、钥匙丢了怎么办。

富贵岂由人,时会高志须酬。

能成功于千载者,必以近察远。


Spring Boot 配置体系与敏感信息治理:多环境、Bootstrap、Jasypt 到生产级 Secret 管理
https://allendericdalexander.github.io/2026/06/01/java/spring/spring-boot-config-file/
作者
AtLuoFu
发布于
2026年6月1日
许可协议