接手过不少SpringBoot项目,最让我头皮发麻的不是业务代码写得多烂,而是打开application.yml,数据库密码、Redis密码、第三方接口密钥一字排开,全是明文。更夸张的是,很多项目直接把这个文件提交进了Git仓库,一搜就全暴露了。这不是懒不懒的问题,是大多数人根本不知道配置文件里的敏感信息还有一套正规的加密保护打法。
这篇文章就把我实践过、也在多个项目里落地过的三种SpringBoot配置文件敏感信息加密方案一次性讲透:第一种是Jasypt透明加解密,五分钟接入,适合绝大多数单体项目;第二种是自定义EnvironmentPostProcessor加AES解密器,零额外依赖、格式完全可控,适合对依赖敏感或者想彻底搞懂原理的人;第三种是配置中心加环境变量加KMS的外部化方案,适合团队协作和云原生部署。三种方案的原理、步骤、坑,一次性说清楚。
1. 问题本源:配置文件里的明文敏感信息,到底危险在哪
1.1 配置里通常藏着哪些敏感信息
先说清楚范围。一个典型的SpringBoot项目,配置文件里的敏感信息远不止数据库密码这一项。我列一下我平时排查到的:
- 数据源密码:MySQL、PostgreSQL、Oracle的连接密码
- 中间件密码:Redis、RabbitMQ、Kafka的认证密码
- 第三方API密钥:支付回调密钥、短信平台AppSecret、对象存储AccessKey
- 内部系统Token:调用公司内部服务的鉴权Token
- 加密密钥本身:比如JWT的签名密钥、接口加解密的AES密钥
这些信息一旦以明文形式出现在配置文件里,就等于把整套系统的钥匙串挂在了大门口。
1.2 明文配置的泄露途径比你想象的多
很多人觉得,"我的代码仓库是私有的,怎么会泄露?"我复盘过几个真实出事的项目,泄露路径通常有这几种:
- Git仓库泄露:代码仓库权限配置不当、离职员工克隆了仓库、开源误操作,配置文件跟着全量暴露
- 日志输出:应用启动时Spring Boot的
DataSource自动配置会打印数据库连接信息,日志系统再把日志集中采集到ELK,等于配置信息进了第二套系统 - 备份文件:服务器备份、容器镜像打包、代码备份文件被拖走
- 测试环境与生产环境复用:测试环境的配置表被导出,连带生产环境的数据库地址和密码一起泄露
我见过最典型的一个事故:某个项目把生产数据库密码明文放在application-prod.yml里,然后这个文件被打进了Docker镜像,镜像又推到了公开的镜像仓库。发现问题的时候,数据库已经被扫库了。
1.3 加密保护的核心思路:加密算法加上密钥管理,加上透明解密
配置文件加密保护,本质要做三件事:
- 用加密算法把明文变成密文,即使配置泄露,别人拿到的也是一串不可读的密文
- 把解密密钥放在安全的地方,与应用配置分离,比如环境变量、部署平台的密钥管理服务
- 在应用启动时透明解密,让Spring容器拿到的依然是明文,业务代码无感知
一句话概括:配置文件里存密文,运行环境里存密钥,启动过程做解密。下面三种方案,本质都是围绕这三个环节的不同实现方式。
提示:加密不是把配置藏起来不让看,而是即使配置被看光了,没有密钥的人也无法还原出真正的连接信息。密钥的安全程度,决定了整条链路的最终安全上限。
2. 方案一:Jasypt Spring Boot Starter——最成熟的透明加密方案
2.1 Jasypt到底做了什么
Jasypt(Java Simplified Encryption)是Java生态里老牌的加密库,jasypt-spring-boot-starter则是它在SpringBoot世界的桥接器。我第一次用的时候其实没搞懂它怎么实现"配置文件里写密文,应用启动自动变明文"的,后来翻源码才明白。
核心原理是:Jasypt在Spring容器刷新之前,注册了一个自定义的BeanFactoryPostProcessor,拦截了所有PropertySource中的配置值。如果某个值形如ENC(密文),就用配置好的解密器解密还原成明文,再交给Spring环境使用。
换句话说,你在配置文件里写的是:
spring: datasource: password: ENC(9x7bKp8H4mTq2ZvW...)应用启动后,Spring拿到的实际值已经是解密后的明文密码。业务代码、连接池、MyBatis全都无感知,这是它最大的优点。
2.2 集成步骤:三步接入
第一步,引入依赖。以Maven为例:
<dependency> <groupId>com.github.ulisesbocchio</groupId> <artifactId>jasypt-spring-boot-starter</artifactId> <version>3.0.5</version> </dependency>注意一下版本对应关系:3.0.5适用于Spring Boot 2.x,Spring Boot 3.x需要选择对应兼容版本,这个后面在踩坑部分细说。
第二步,在配置文件里声明加密算法和密钥来源:
jasypt: encryptor: algorithm: PBEWITHHMACSHA512ANDAES_256 password: ${JASYPT_PASSWORD}我强烈建议不要直接把密钥明文写在配置文件里,用环境变量JASYPT_PASSWORD占位,这样配置文件就算泄露,没有运行环境里的这个环境变量,密文也无法还原。
第三步,生成密文并替换。Jasypt提供了命令行工具,也可以写一个测试类来生成:
@SpringBootTest class EncryptorTest { @Autowired private StringEncryptor encryptor; @Test void generate() { System.out.println(encryptor.encrypt("root123456")); System.out.println(encryptor.encrypt("r8s7F2kL9qW")); } }生成得到的密文,形如ENC(FgkD3i8sK...一堆字符...),把它替换到配置文件的对应位置即可。
2.3 进阶配置项:算法、盐、IV
Jasypt 3.x默认不指定algorithm时用的是PBEWITHHMACSHA512ANDAES_256,这个算法本身是PBE(基于密码的加密)和AES-256的组合,安全性够用。但有几个配置项值得留意:
jasypt: encryptor: algorithm: PBEWITHHMACSHA512ANDAES_256 password: ${JASYPT_PASSWORD} iv-generator-classname: org.jasypt.iv.RandomIvGenerator salt-generator-classname: org.jasypt.salt.RandomSaltGenerator key-obtention-iterations: 1000iv-generator-classname:指定IV(初始向量)生成器,RandomIvGenerator会为每次加密生成随机IV,让同样的明文每次加密出的密文都不同,推荐开启salt-generator-classname:盐生成器,同样建议随机盐key-obtention-iterations:密钥派生迭代次数,越大越安全,但启动时计算开销也越大,我一般用1000到10000之间
在生产环境我建议至少设置RandomIvGenerator,否则相同明文总是生成相同密文,相当于给攻击者提供了"已知明文对照"的参考。
2.4 实测体验与要注意的地方
Jasypt方案我用下来最大的感受是:接入成本极低,适合快速落地。但它有几个先天限制:
- 依赖较重:引入starter后,还会间接引入一些加密库,对依赖洁癖的人来说不太友好
- 密钥来源单一:默认从环境变量或配置里读密钥,缺乏与KMS之类密钥管理系统的集成,当然你也可以实现自定义的
StringEncryptor来对接 - 可定制性一般:如果我想用自定义的加密格式、自定义的密文前缀,Jasypt虽然能做但需要扩展较多接口
另外有一点很多人忽略:Jasypt解密时机比较早,但如果你同时用了@ConfigurationProperties绑定配置,解密后的值是可以正确绑定的,我验证过。但如果你在spring.factories里注册了自己定义的自动配置类,并且它读取配置的时机更早,就要小心可能读到没解密的密文。
3. 方案二:自定义EnvironmentPostProcessor加AES解密器——零依赖的轻量方案
3.1 为什么还要自己写一套
有人可能会问:Jasypt都这么成熟了,为什么还要自己造轮子?我自己的理由有几个:
- 有的项目对第三方依赖管控很严格,能少引一个包就少一个包
- 我想完全控制密文格式,比如用
cipher(作为前缀,而不是Jasypt的ENC( - 我想在解密时做一点额外逻辑,比如解密失败时走特定的告警或者降级
- 想彻底搞懂SpringBoot配置加载的时机,而不是黑盒式的"反正它解密了"
事实证明,自己实现一套并不复杂,核心就两个类:一个EnvironmentPostProcessor,一个AES工具类。
3.2 核心实现:EnvironmentPostProcessor加AES/GCM
SpringBoot从2.0开始提供了EnvironmentPostProcessor接口,允许在ConfigurableEnvironment创建后、Spring容器刷新前对配置进行拦截修改。这正是做透明解密的绝佳时机。
第一步,定义密文格式。我用的格式是cipher(base64串),简单直观。
第二步,实现EnvironmentPostProcessor:
public class CipherEnvironmentPostProcessor implements EnvironmentPostProcessor { private static final String PREFIX = "cipher("; private static final String SUFFIX = ")"; @Override public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { String secretKey = environment.getProperty("APP_CONFIG_SECRET_KEY"); if (secretKey == null || secretKey.isEmpty()) { throw new IllegalStateException("APP_CONFIG_SECRET_KEY 环境变量未配置,无法解密配置文件"); } // 解析所有属性源,替换密文 for (PropertySource<?> propertySource : environment.getPropertySources()) { if (propertySource instanceof EnumerablePropertySource) { EnumerablePropertySource<?> source = (EnumerablePropertySource<?>) propertySource; Map<String, Object> copied = new HashMap<>(); for (String name : source.getPropertyNames()) { Object value = source.getProperty(name); if (value instanceof String && ((String) value).startsWith(PREFIX)) { String cipherText = extractCipherText((String) value); copied.put(name, AesGcmUtil.decrypt(cipherText, secretKey)); } } if (!copied.isEmpty()) { environment.getPropertySources().addFirst( new MapPropertySource("decrypted-" + propertySource.getName(), copied)); } } } } private String extractCipherText(String value) { return value.substring(PREFIX.length(), value.length() - SUFFIX.length()); } }这里有个很重要的设计:我不是直接修改原PropertySource,而是把解密后的键值放入一个新的MapPropertySource,并插入到PropertySource链的最前面。因为Spring获取配置是按PropertySource的顺序来的,放在最前面就能覆盖原始密文源。这样做的好处是不破坏原始来源,后续排查配置来源时还能看到decrypted-xxx这个源的踪迹。
第三步,实现AES/GCM工具类:
public class AesGcmUtil { private static final int IV_LENGTH = 12; private static final int T_LENGTH = 128; public static String decrypt(String cipherText, String secretKey) { try { byte[] decoded = Base64.getDecoder().decode(cipherText); byte[] iv = Arrays.copyOfRange(decoded, 0, IV_LENGTH); byte[] encrypted = Arrays.copyOfRange(decoded, IV_LENGTH, decoded.length - T_LENGTH / 8); byte[] tag = Arrays.copyOfRange(decoded, decoded.length - T_LENGTH / 8, decoded.length); SecretKeySpec keySpec = new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), "AES"); GCMParameterSpec gcmSpec = new GCMParameterSpec(T_LENGTH, iv); Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmSpec); byte[] plainBytes = cipher.doFinal(encrypted, tag); return new String(plainBytes, StandardCharsets.UTF_8); } catch (Exception e) { throw new IllegalStateException("配置文件解密失败", e); } } }这里我选AES/GCM而不是AES/CBC,因为GCM是AEAD加密模式,密文自带完整性校验,密文被篡改后解密直接失败,安全性比单纯CBC高一个档次。而且我设计密文格式为Base64(IV + 密文 + Tag),把IV和认证标签都塞进密文一起传,避免额外维护IV。
第四步,注册EnvironmentPostProcessor。在META-INF/spring.factories里声明:
org.springframework.boot.env.EnvironmentPostProcessor=\ com.example.config.CipherEnvironmentPostProcessor3.3 配套的密文生成工具
有了解密,必须配套密文生成工具,否则没法用。我通常写一个main方法或者独立的工具类:
public class CipherGenerateTool { public static void main(String[] args) throws Exception { String secretKey = System.getenv("APP_CONFIG_SECRET_KEY"); String plainText = args.length > 0 ? args[0] : "root123456"; byte[] iv = new byte[12]; new SecureRandom().nextBytes(iv); SecretKeySpec keySpec = new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), "AES"); Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); cipher.init(Cipher.ENCRYPT_MODE, keySpec, new GCMParameterSpec(128, iv)); byte[] encrypted = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8)); byte[] tag = Arrays.copyOfRange(cipher.doFinal(new byte[0]), 0, 16); ByteBuffer buffer = ByteBuffer.allocate(iv.length + encrypted.length + tag.length); buffer.put(iv); buffer.put(encrypted); buffer.put(tag); String cipherText = Base64.getEncoder().encodeToString(buffer.array()); System.out.println("cipher(" + cipherText + ")"); } }实际使用流程:先设置环境变量APP_CONFIG_SECRET_KEY,运行工具生成密文,再手动把密文替换到配置文件中。整个过程不依赖Spring容器启动,生成和验证都能独立完成。
3.4 这个方案的边界与风险
自己实现方案,优点很明显:零额外依赖、格式可控、原理透明。但风险也必须有清醒认识:
- 密钥管理依旧是个问题:我上面从环境变量读取密钥,如果环境变量所在机器被攻破,密钥照样泄露,所以环境变量的安全需要依赖部署平台本身
- 加密算法实现的正确性:AES/GCM的IV长度、Tag长度、密文拼接顺序都必须严格一致,写错一个字节就是启动即失败
- 维护成本:这套代码需要自己测试、自己维护,不如Jasypt社区成熟
- spring.factories在SpringBoot 3.x的变化:SpringBoot 3.x开始用
org.springframework.core.io.support.SpringFactoriesLoader加载,注册方式有所调整,需要对应适配
注意:
EnvironmentPostProcessor无法解读远端配置中心已经完成占位符替换的配置值,如果你的配置来自Nacos等配置中心并且带着cipher(...)前缀,这个方案的处理时机需要额外验证。
4. 方案三:配置中心加环境变量加KMS——面向团队与云原生的外部化方案
4.1 方案的思路转变:把密钥和配置从代码中彻底剥离
说句实话,前两种方案解决的是"配置文件泄露"的问题,但没有解决"运维人员需要接触密钥"的问题。在团队协作场景里,开发人员、测试人员、运维人员都要拿着密钥去解密配置,密钥的传播面会越来越大。
于是有了第三种思路:把敏感信息从代码仓库里彻底赶出去。配置文件里连密文都不放,放的是一个"引用的名字",真正的值保存在运行环境或者密钥管理服务里。
4.2 Spring Cloud Config的{cipher}机制
如果你的项目已经用了Spring Cloud Config Server,那么对称加密是内置能力。把敏感信息写成这样:
spring: datasource: password: "{cipher}a1b2c3d4e5..."Config Server配置了encrypt.key(对称密钥)后,客户端请求配置时,服务端自动解密{cipher}开头的值。这种方式的好处是:客户端代码和配置仓库都不接触明文,只有Config Server持有密钥。
但它有个历史包袱:encrypt.key本身如果放在Config Server的配置文件里,依然是个密钥保管问题。生产环境建议把encrypt.key也放到环境变量中。
4.3 Nacos配置加密的落地形态
国内团队用Nacos做配置中心的非常多。Nacos从2.x开始支持配置加密,但默认并没有对配置文件里所有内容做透明解密,需要在客户端配合处理。
我的做法是三步:
- 在Nacos配置中心存储密文,例如
password: cipher(xx...) - 在SpringBoot客户端引入一个自定义配置解密器(类似方案二,但针对Nacos的PropertySource做解密)
- 加解密密钥放在部署平台的环境变量或者KMS服务里
这里要注意:Nacos客户端拉取配置后,配置内容会作为PropertySource的一部分进入Spring环境,但Nacos的配置源是CompositePropertySource,用方案二的EnumerablePropertySource判断时,Nacos内部的结构需要专门适配。我踩过这个坑:直接遍历environment.getPropertySources()时,Nacos的源类型不一定是你想要的,需要对NacosPropertySource做单独处理。
4.4 云厂商KMS与信封加密
更彻底的做法是用云厂商的KMS服务。KMS(Key Management Service)的核心价值不是加密算法多厉害,而是把密钥的生成、存储、轮换、审计都托管了,运维人员不需要把密钥放进任何文件。
典型形态是信封加密(Envelope Encryption):
- 调用KMS生成一个数据密钥(DEK),用它加密配置内容得到密文
- DEK本身再由KMS的主密钥(CMK)加密保存
- 应用启动时,先从KMS解出DEK,再用DEK解配置密文
在SpringBoot里,实现思路就是自定义一个StringEncryptor,解密时调用云厂商KMS的SDK。阿里云KMS、腾讯云KMS、AWS KMS都有对应的SDK。这样密钥根本不出KMS服务,安全性上限是最高的。
但这条路也有代价:强依赖云厂商,本地开发环境模拟KMS比较麻烦。我的建议是,非容器化、不上云的生产环境没必要一步到位上KMS,用方案一就够了。
5. 三个方案横向对比:安全等级、接入成本、适用团队
5.1 关键维度对照表
| 对比维度 | Jasypt透明加密 | 自定义EnvironmentPostProcessor | 配置中心+KMS外部化 |
|---|---|---|---|
| 依赖成本 | 引入starter,约几个间接依赖 | 零第三方依赖 | 依赖配置中心或云厂商SDK |
| 接入难度 | 最低,三步搞定 | 中等,需要编码并理解加载时机 | 较高,涉及配置中心/KMS配置 |
| 密文格式控制 | 固定ENC(...),可扩展但麻烦 | 完全自定义,如cipher(...) | 自定义加上游系统配合 |
| 密钥管理 | 环境变量或自定义加密器 | 环境变量,可扩展 | KMS托管,支持轮换审计 |
| 解密时机 | Spring容器刷新前 | Environment准备阶段 | 拉取配置时或容器刷新前 |
| 适用场景 | 单体项目快速改造 | 依赖管控严格、想完全可控 | 团队协作、微服务、云原生 |
| 安全性上限 | 中高 | 中高 | 高 |
| 常见踩坑成本 | 版本兼容、算法配置 | 编码细节、Nacos适配 | 配置中心权限、网络 |
5.2 不同团队规模的选择建议
如果是个人项目或者三五人的小团队,只有单个SpringBoot应用,我的建议是直接上Jasypt。用最少的时间解决最大的隐患,把精力留给业务。加上一个环境变量存密钥,安全等级已经比90%的项目高了。
如果是依赖管控严格的企业内部项目,不能随便引入第三方库,或者对配置格式有特殊要求,自定义EnvironmentPostProcessor方案更合适。这套代码量不大,写一次基本能覆盖所有项目复用。
如果是几十个微服务的中大型团队,必须用配置中心做统一配置管理,那就别纠结了,直接走配置中心加客户端解密,生产环境的密钥无论如何都要挪到KMS或者部署平台的密钥管理能力里,否则运维人员靠口头传密钥,早晚会出问题。
5.3 混合使用的常见组合
实际项目中我见过不少混合用法,这里列两个典型组合:
组合一:Jasypt加Nacos加环境变量Nacos里存Jasypt格式的密文,客户端配置Jasypt的密钥从环境变量读取。这样既享受了配置中心的动态刷新,又保留了Jasypt的透明解密能力。注意动态刷新时Jasypt的解密逻辑要能正确处理新拉取配置中的密文。
组合二:自定义解密器加KMS自定义EnvironmentPostProcessor做完格式解密,但密钥不是从环境变量读,而是启动时调KMS的SDK获取。这样密钥完全不落盘,安全性拉满。适合安全要求苛刻的金融、政务类项目。
6. 实际落地中的坑与排查经验
6.1 日志泄露:密文和明文都可能出现在日志里
加密不是配完就完事了。我见过很多项目,配置是加密了,但应用启动时SpringBoot打印的DataSource信息里,密码被明文打印出来了。Jasypt解密后,连接池拿到的是明文,如果连接池配置了打印SQL或者初始化日志,泄露的就不仅是密文了。
排查方法:启动时用--debug看日志,检索关键字password、url,一旦发现明文密码出现在日志里,立即调整日志级别或者配置连接池的日志输出。
6.2 密钥泄漏等于没加密
这句话我说了很多遍,但还是要强调:密钥和配置绝对不能放在同一个文件里。如果JASYPT_PASSWORD直接写在application.yml里,那么攻击者拿到配置文件的瞬间也拿到了密钥,加密形同虚设。
我见过最离谱的操作:jasypt.encryptor.password写在application-dev.yml里,还把那个文件一起推到了Git仓库。等于保险柜钥匙就挂在保险柜外面。
提示:密钥来源优先级从高到低依次是:KMS服务、部署平台的环境变量/密钥管理、启动命令参数、配置文件。宁可每次部署时手动注入环境变量,也不要写死在配置文件里。
6.3 解密失败排查链路
解密失败是接入这些方案后最常见的故障。我总结一个排查链路:
- 看异常信息:如果是
DecryptionException或BadPaddingException,大概率是密钥不对,或者密文被截断 - 确认密钥长度:自定义AES方案,密钥必须是16、24或32字节,否则初始化
SecretKeySpec就直接报错 - 确认JCE权限:JDK8的早期版本默认限制了AES-256,需要替换
local_policy.jar和US_export_policy.jar;JDK8u161之后默认就支持了,但如果报Illegal key size,先检查JDK版本再检查JCE - 确认算法参数一致:Jasypt生成密文用的算法、盐策略、IV策略,必须和运行时完全一致。如果你升级了Jasypt版本但没注意默认算法变化,旧的密文会全部失效
- 确认字符编码:配置文件的编码建议统一UTF-8,否则密文中的Base64字符集在不同编码下可能出现多字节差异
我遇到过一次很隐蔽的坑:某个同事在Windows上编辑了application.yml,文件变成了GBK编码,里面密文的非ASCII字符被转码,导致Linux环境启动时解密失败。后来约定所有配置文件统一用UTF-8编码并提交到Git,问题才彻底解决。
6.4 配置优先级导致解密结果被覆盖
在自定义EnvironmentPostProcessor方案中,我把解密后的MapPropertySource插入到PropertySource链的第一位,目的就是保证解密值优先级最高。但有些项目里,配置文件有多个来源:命令行参数、环境变量、application.yml、bootstrap.yml、Nacos远端配置等。
SpringBoot的配置优先级大致是:命令行参数大于Java系统属性大于环境变量大于远端配置中心大于本地application.yml。如果你的明文出现在优先级更高的来源中(比如启动参数里传了--spring.datasource.password=xxx),那解密后的值就会被明文覆盖。这个问题排查起来很隐蔽,因为启动日志不一定显示配置来源变化。
排查方法:在启动时加--debug,或者在应用启动后打印Environment中对应属性的实际值和来源。我在排查时经常写一个临时ApplicationRunner来输出:
@Component public class ConfigCheckRunner implements ApplicationRunner { @Override public void run(ApplicationArguments args) { System.out.println(environment.getProperty("spring.datasource.password")); } }如果打印出来是明文且和密文不一致,就要关注是否有更高优先级的配置源在捣乱。
6.5 多环境配置的密钥管理
开发、测试、生产三个环境,密钥不能共用,因为一旦开发环境的密钥泄露,生产环境也跟着遭殃。我的做法是:
- 开发环境:每个开发者本地生成自己的密钥,写入开发者本机的
~/.bashrc或IDE环境变量 - 测试环境:由测试团队统一管理一个测试专用密钥
- 生产环境:密钥由运维团队管理,仅在生产部署平台的环境变量或KMS中配置
这样做还有个额外好处:同一个密文在不同环境需要不同密钥,所以不同环境的配置密文也不同,即使某环境的配置泄露,其他环境不受影响。
6.6 Git历史里的明文密码怎么清理
文章最后补一个很实际的操作。如果项目已经用明文密码提交过Git,光是改成密文还不够,Git历史里依然躺着明文。清理思路:
- 先修改密码,让历史中的明文密码失效
- 用
git filter-repo重写Git历史,将包含敏感信息的文件从历史中抹掉 - 强制推送并通知所有人重新clone
git filter-repo是我用过最顺手的工具,一条命令就能把指定文件从所有历史提交中删除。但要注意,重写历史会改变commit SHA,团队需要协调好变基时序,否则会一团乱。
7. 我的选型经验与最后一点小技巧
三种方案我都实打实用过,各自的适用场景说得很清楚了。如果让我给一个最省心的建议:单体项目先上Jasypt,配置中心团队直接做客户端解密加KMS。自定义EnvironmentPostProcessor适合爱钻研原理或者受制于依赖管控的人,它让你真正掌握SpringBoot配置加载的脉络。
最后分享一个我习惯性使用的小技巧:加解密工具类里,可以额外输出一行带有配置源名称的启动日志,比如"decrypted-application.yml: spring.datasource.password 已解密"。这样每次应用启动,一眼就能确认哪些配置走了解密逻辑,哪些可能被更高优先级覆盖,排查问题能省一半时间。
敏感信息加密这件事,做起来不难,难的是坚持一个原则:配置文件可以被看,开发者不该被吓。把明文密码从配置里清干净,是对项目、对团队、也是对自己负责。