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面板里配。这样代码里干干净净,一个密码都看不到。

小技巧: 如果你用IntelliJ IDEA,可以在运行配置里加环境变量。或者用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 凭证管理方案 凭证管理 环境变量 System.getenv() 操作系统级别 最简单,适合开发 Gradle属性文件 gradle.properties 本地/远程分离 灵活,团队适用 加密凭证 encrypted-properties AES加密存储 最安全,企业级 CI/CD秘密 GitHub Secrets Jenkins Credentials 自动化构建 核心原则 凭证不写死、不提交、不打印 运行时注入,最小权限原则

实战建议

讲了这么多,到底怎么选?我个人的经验是这样的:

场景 推荐方案 理由
个人开发 环境变量 简单直接,不用额外配置
小团队 Gradle属性文件 + .local 灵活,每个人可以有自己的配置
企业项目 加密凭证 + CI/CD秘密 安全合规,审计可追溯
开源项目 环境变量 + CI/CD秘密 不暴露任何敏感信息给贡献者
我的习惯: 开发环境用环境变量,CI/CD用平台秘密管理。如果项目涉及金融或医疗数据,我会上加密凭证。说白了,安全等级越高,方案越重,但心里越踏实。

避坑指南

最后分享几个我踩过的坑:

  • 别用println打印凭证 — 我曾经调试时顺手打印了密码,结果CI日志里全看到了。嗯,那叫一个尴尬。
  • 注意.gitignore的生效范围 — 如果你把gradle.properties.local加到了全局忽略里,但项目里还有别的敏感文件,记得一并处理。
  • 加密密码本身也要安全 — 加密凭证的密码如果也写死在脚本里,那加密就失去意义了。建议从环境变量读取。
  • 定期轮换凭证 — 别一个密码用三年。我一般每季度换一次,CI/CD里也设置过期提醒。

好了,关于安全与凭证,今天就聊到这儿。记住一句话:信任代码,但别信任代码里的秘密