27、安全与凭证:加密凭证、Gradle属性、环境变量管理
说实话,安全这块儿我以前也吃过亏。记得刚用Gradle那会儿,图省事直接把数据库密码写在了build.gradle.kts里,结果代码不小心传到了公开仓库。嗯,那滋味可不好受。从那以后,我对凭证管理就格外上心。
今天咱们就聊聊,怎么在Gradle Kotlin DSL里把敏感信息管好。说白了就三个方向:环境变量、Gradle属性文件、加密凭证。我一个个给你拆开讲。
为什么不能硬编码凭证?
你想想看,构建脚本里如果直接写密码、API Key、Token,会有什么后果?
- 代码仓库一公开,密码就裸奔了
- 团队成员之间共享,谁改了都不知道
- CI/CD系统里也要单独配置,维护成本高
我见过最夸张的一次,一个项目里居然有十几个地方硬编码了同一个数据库密码。后来要换密码,改得那叫一个痛苦。所以,凭证永远不要出现在源码里,这是底线。
方案一:环境变量(最简单)
环境变量是操作系统级别的配置。Gradle可以直接读取,不需要任何额外插件。
// build.gradle.kts
val dbPassword: String? = System.getenv("DB_PASSWORD")
val apiKey: String? = System.getenv("API_KEY")
println("数据库密码是否已设置: ${dbPassword != null}")
我个人习惯在开发环境用.env文件配合IDE加载,生产环境直接在CI/CD面板里配。这样代码里干干净净,一个密码都看不到。
source .env加载,但记得把.env加到.gitignore里。
方案二:Gradle属性文件(更灵活)
Gradle支持gradle.properties文件。但这个文件也会被提交到仓库,怎么办?
答案是:用两个文件。
gradle.properties— 放公共配置,提交到仓库gradle.properties.local— 放敏感信息,不提交
然后在build.gradle.kts里手动加载本地文件:
// build.gradle.kts
val localProps = File(rootProject.projectDir, "gradle.properties.local")
if (localProps.exists()) {
localProps.reader().use { reader ->
java.util.Properties().apply { load(reader) }.forEach { (key, value) ->
// 只覆盖未设置的属性
if (!project.hasProperty(key.toString())) {
project.ext.set(key.toString(), value)
}
}
}
}
val dbUrl: String by project
val dbUser: String by project
val dbPassword: String by project
println("数据库连接: $dbUrl")
对应的gradle.properties.local内容:
dbUrl=jdbc:mysql://localhost:3306/mydb
dbUser=admin
dbPassword=super_secret_123
gradle.properties.local加到.gitignore里。我曾经见过有人忘了加,结果本地文件被提交了,密码全暴露了。那场面,真是大型社死现场。
方案三:加密凭证(最安全)
如果你觉得明文存文件还是不放心,那就上加密。Gradle官方推荐用Gradle Encrypted Properties插件,或者用sops这类工具。
我比较常用的是gradle-encrypted-properties插件。用法很简单:
// build.gradle.kts
plugins {
id("net.rdrei.gradle.encrypted-properties") version "1.0.0"
}
encryptedProperties {
// 指定加密属性文件
file("gradle.properties.enc")
// 加密密码从环境变量读取
password = System.getenv("GRADLE_ENCRYPTION_PASSWORD") ?: error("需要设置GRADLE_ENCRYPTION_PASSWORD")
}
然后生成加密文件:
# 命令行
./gradlew encryptProperties -PplainText="dbPassword=my_secret"
这样gradle.properties.enc里存的就是密文了。只有知道密码的人才能解密。嗯,这才是企业级的做法。
方案四:CI/CD系统的秘密管理
在CI/CD环境里,比如GitHub Actions、Jenkins、GitLab CI,都有内置的秘密管理功能。我建议你直接用它们。
以GitHub Actions为例:
# .github/workflows/build.yml
name: Build
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build with Gradle
env:
DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
API_KEY: ${{ secrets.API_KEY }}
run: ./gradlew build
然后在GitHub仓库的Settings → Secrets and variables里配置。这样密码只在运行时注入,日志里也不会打印出来。
知识体系总览
下面这张图,帮你把今天讲的内容串起来:
实战建议
讲了这么多,到底怎么选?我个人的经验是这样的:
| 场景 | 推荐方案 | 理由 |
|---|---|---|
| 个人开发 | 环境变量 | 简单直接,不用额外配置 |
| 小团队 | Gradle属性文件 + .local | 灵活,每个人可以有自己的配置 |
| 企业项目 | 加密凭证 + CI/CD秘密 | 安全合规,审计可追溯 |
| 开源项目 | 环境变量 + CI/CD秘密 | 不暴露任何敏感信息给贡献者 |
避坑指南
最后分享几个我踩过的坑:
- 别用
println打印凭证 — 我曾经调试时顺手打印了密码,结果CI日志里全看到了。嗯,那叫一个尴尬。 - 注意
.gitignore的生效范围 — 如果你把gradle.properties.local加到了全局忽略里,但项目里还有别的敏感文件,记得一并处理。 - 加密密码本身也要安全 — 加密凭证的密码如果也写死在脚本里,那加密就失去意义了。建议从环境变量读取。
- 定期轮换凭证 — 别一个密码用三年。我一般每季度换一次,CI/CD里也设置过期提醒。
好了,关于安全与凭证,今天就聊到这儿。记住一句话:信任代码,但别信任代码里的秘密。