Secret 与变量
AuraBoot 的配置是分层的,每一层覆盖下层。理解层级顺序至关重要:紧急补丁注入到了错误的层,要么完全不生效,要么泄漏进受 git 跟踪的文件。
三层模型
| 层 | 来源 | 优先级 | 典型用途 |
|---|---|---|---|
| 1. 程序参数 | --spring.data.redis.host=... | 最高 | 一次性 override,生产 launcher |
| 2. 环境变量 | DATABASE_PASSWORD、SPRING_DATA_REDIS_HOST | 中 | 容器部署、CI |
| 3. 外部 vault | 通过 spring.config.import 接入 Hashicorp Vault / 云 KMS | 三层中最低(仍高于 application.yml) | 长期 secret、轮换 |
打包后的 application.yml 只包含不敏感的默认值;它绝不应该携带真实密码或 API key。
敏感字段
下列字段绝不能提交,必须来自第 2 层或第 3 层:
| 字段 | 说明 |
|---|---|
spring.datasource.password | PostgreSQL 主库凭据 |
spring.data.redis.host / password | 企业版启动时强依赖 Redis |
security.jwt.secret | 签发平台 JWT;轮换会让所有会话失效 |
aura.storage.minio.access-key / secret-key | 对象存储 |
aura.mq.kafka.bootstrap-servers 凭据 | bootstrap host 本身打日志没问题;SASL secret 不行 |
Connector 凭据(cr_csp_connector_pid resolver) | 第三方 API key、OAuth token |
| Webhook 签名 secret | 见 Webhooks |
| LLM 提供商 key | OpenAI / Claude / 本地模型网关 token |
何时使用
- 引导一个全新环境:以环境变量为主,便于迁移。
- 生产且需要频繁轮换:绑定外部 vault,绝不硬编码。
- 本地开发:用一个 gitignore 的
.env文件加一个把它翻译成程序参数的 launcher。
工作原理
Spring Boot 按声明的优先级顺序解析配置。AuraBoot 在此基础上扩展了一个 connector 凭据 resolver,会按 cr_csp_connector_pid 查 bearer / basic / api-key 负载,并把它们注入出站请求,而不会把明文落到 DSL 里。
# application.yml(受提交跟踪,且不含 secret)
spring:
config:
import:
- "optional:vault://secret/auraboot/${SPRING_PROFILES_ACTIVE}"
datasource:
url: "${DATABASE_URL:jdbc:postgresql://localhost:5432/aura_boot?charSet=UTF8}"
username: "${DATABASE_USERNAME:auraboot}"
password: "${DATABASE_PASSWORD:}" # 生产必填,无默认值
data:
redis:
host: "${REDIS_HOST:localhost}" # 启用 Redis 时由 SPRING_DATA_REDIS_HOST 注入
port: "${REDIS_PORT:6379}"
security:
jwt:
secret: "${JWT_SECRET}" # 必填一次性 override(例如事故响应时),用程序参数传入,避免泄漏到每一个子 shell 的进程环境里:
java -jar platform.jar \
--spring.profiles.active=prod \
--spring.data.redis.host=10.0.3.7 \
--spring.datasource.hikari.maximum-pool-size=50轮换
# 1. 生成新密钥;把当前密钥降级为「上一把」,保证旧 token 仍可校验
NEW_SECRET="$(openssl rand -base64 64)"
# JWT_PREVIOUS_SECRET=$JWT_SECRET, JWT_PREVIOUS_KID=$JWT_KID
# JWT_SECRET=$NEW_SECRET, JWT_KID=key-$(date +%Y%m%d)
# 2. 滚动重启每个节点,让新配置生效(平台未启用 Spring Cloud /actuator/refresh)
# 新签发的 token 用新密钥,旧 token 仍由 previous-secret 校验
# 3. 验证新 JWT 可签发:用新 token 调一个受保护端点
curl -sS "$AURA_API_URL/api/auth/me" \
-H "Authorization: Bearer $NEW_TOKEN"
# 4. 过一个 token 过期周期(默认 86400s)后,移除 JWT_PREVIOUS_* 环境变量平台用 security.jwt.previous-secret / previous-kid 支持双密钥并存:滚动重启期间旧 token 仍可校验,无需让所有活跃会话立即失效。一个过期周期后下线旧密钥即可。安排在维护窗口里执行,或者借助具备会话亲和性的负载均衡器逐节点滚动。
验证
- 在仓库上
git grep找不到任何已提交的 secret(pg_password、jwt.secret:、明文 bearer token)。 /actuator/configprops(只有 admin 可访问)对敏感字段清单中的每一项都展示为掩码。- 服务端 INFO 级日志不回显 secret;结构化日志过滤器会掩码
password、secret、token、apiKey。 - 轮换后的 secret 在每个节点滚动重启后生效;旧凭据被拒。
- CI 流水线从 CI vault 集成中取 secret,绝不从明文的仓库变量里取。