广告位 · 页头(后台-模块管理-新建模块后替换此区域)

如何用 Spring Cloud Config 实现配置集中管理与敏感信息加密?

在微服务数量超过个位数之后,配置管理会迅速变成一件让人头疼的事。数据库连接串、Redis 地址、消息队列账号等分散在不同仓库中,改一处配置往往要重新打包发布。Spring Cloud Config 通过一个独立的 Config Server 进程统一管理配置源,客户端按应用名和环境拉取对应的配置项,从而让修改、回滚和审计都集中起来。

如何用 Spring Cloud Config 实现配置集中管理与敏感信息加密?

一、Config Server 的工作模型与 Git 仓库接入

Spring Cloud Config 的核心是一个 Spring Boot 应用,它对外暴露 REST 接口,客户端通过 HTTP 请求获取配置。服务端本身不保存太多业务数据,而是把 Git 仓库、本地文件系统或者 HashiCorp Vault 当成配置源。Git 作为后端是最常见的选择,因为它天然具备版本历史和变更审计能力。服务端启动后会克隆或拉取指定仓库到本地缓存,当客户端请求某个应用配置时,服务端根据应用名、profile 和 label 三个维度拼出对应的文件路径。

配置仓库的目录结构非常灵活,可以按应用名建目录,也可以把所有 yml 文件平铺在根目录。假设一个订单服务叫 order-service,它拥有 dev 和 prod 两个环境,仓库里可以放置 application.yml、order-service.yml、order-service-dev.yml、order-service-prod.yml 等文件。服务端会按照从通用到特定的顺序合并配置,例如 order-service-dev.yml 中的值会覆盖 application.yml 中同名键。要启用 Git 后端,只需要在 Config Server 的 application.yml 中配置 git.uri 即可。

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-config-server</artifactId>
</dependency>
server:
  port: 8888
spring:
  application:
    name: config-server
  cloud:
    config:
      server:
        git:
          uri: https://gitee.com/your-team/config-repo.git
          search-paths: '{application}'
          default-label: main

这里的 search-paths 设置成应用名占位符后,服务端会到仓库下对应的应用目录里查找文件,适合按应用拆分目录的团队。如果不想分目录,也可以直接去掉 search-paths。default-label 指的是默认分支,旧版默认是 master,现在很多仓库已经切到 main。服务端还支持配置多个仓库组合、基于模式匹配的仓库路由,以及配置仓库本身的加密访问,这些在文档中都有说明。

二、客户端接入与局部刷新

客户端要接入 Config Server,需要在项目中加入 spring-cloud-starter-config 和 actuator 依赖。与传统 Spring Boot 不同,配置中心的地址信息通常放在 bootstrap.yml 中,因为 bootstrap 上下文会在主应用上下文启动前加载,确保后续的 Bean 初始化能用到远程配置。bootstrap.yml 里最少需要指定 spring.application.name 和 spring.cloud.config.uri,前者决定请求哪个应用配置,后者是 Config Server 地址。

spring:
  application:
    name: order-service
  cloud:
    config:
      uri: http://127.0.0.1:8888
      fail-fast: true
      profile: dev
management:
  endpoints:
    web:
      exposure:
        include: refresh

fail-fast 建议开启,这样当 Config Server 不可用时客户端会直接启动失败,而不是带着空配置运行。客户端拿到配置后,默认会注入到 Environment 中,可以通过 @Value 或 @ConfigurationProperties 使用。需要注意的是,远程配置不会覆盖本地 application.yml 中已有的同名属性,除非使用 spring.cloud.config.override-none 等参数调整优先级。对于大多数场景,建议本地只保留 bootstrap 和少量必要默认值,业务配置全部放到配置中心。

如果配置在 Git 仓库中发生了变化,客户端不会自动感知。最简单的做法是在需要动态更新的 Bean 上加上 @RefreshScope 注解,然后向客户端实例发送 POST 请求到 /actuator/refresh。这个注解会让 Bean 在下次访问时重新创建,从而重新绑定 @Value 注入的值。比如下面的 Controller 就能在配置更新后返回新的超时阈值,而无需重启服务。

@RestController
@RefreshScope
public class ConfigController {
    @Value("${order.pay.timeout:30}")
    private int payTimeout;
    @GetMapping("/pay-timeout")
    public int getPayTimeout() {
        return payTimeout;
    }
}

这种方式只对当前实例生效,如果服务部署了多个副本,就需要逐个调用 refresh 端点,或者借助 Spring Cloud Bus 把刷新事件广播出去。此外,refresh 只会重新绑定被 @RefreshScope 标记的 Bean,普通单例 Bean 不会更新,所以动态配置的使用范围要提前规划好。敏感信息通过配置中心下发时,还要考虑解密动作放在哪一端,下面单独讨论。

三、敏感信息加密:对称加密与密文存储

密码、密钥、第三方 Token 这些信息如果以明文形式存入 Git,等于把所有风险都集中到了一个仓库里。Spring Cloud Config 从早期版本就提供了加密能力,核心是服务端的 /encrypt 和 /decrypt 两个端点。开启对称加密最简单的方式是在 Config Server 的启动参数或环境变量中设置 encrypt.key,例如 ENCRYPT_KEY=mySecretKey。然后向 /encrypt 端点 POST 明文,就能得到一串 Base64 编码的密文。

curl -X POST http://127.0.0.1:8888/encrypt -d 'dbPassword123'

拿到密文后,在配置文件里不能直接写明文,而要写成 {cipher} 加密文的形式。YAML 中需要把整个值用单引号包起来,避免花括号被误解。客户端拉取配置时,Config Server 默认会把密文解密后再下发,所以客户端看到的仍然是明文。这种模式要求解密发生在服务端,客户端不需要知道 encrypt.key。如果想改成客户端解密,可以把解密逻辑放到客户端本地,但那样每个客户端都要持有密钥,管理成本更高。

spring:
  datasource:
    password: '{cipher}AQC8dGhpcyBpcyBhIHNhbXBsZSBjaXBoZXJ0ZXh0...'

对称加密虽然配置简单,但密钥一旦泄露,所有密文都能被解开。对于安全要求更高的项目,Spring Cloud Config 也支持基于 KeyStore 的非对称加密。配置好 key-store 的位置、密码和别名后,服务端会用公钥加密,客户端或服务端用私钥解密。密钥文件本身不建议放进 Git 仓库,可以通过挂载卷或环境变量注入。更进一步的方案是集成 Vault 或云厂商的 KMS,把加密和解密完全交给外部系统。

encrypt:
  key-store:
    location: classpath:/server.jks
    password: changeit
    alias: configkey
    secret: changeit

实际使用时要特别注意,encrypt.key 不能写成硬编码提交到配置仓库中,否则等于没有加密。推荐通过环境变量 ENCRYPT_KEY 注入到 Config Server 容器,这样同一个镜像在不同环境可以配合不同密钥。另外,加密端点本身应该限制访问,不能谁都能调用 /encrypt 去批量加密或解密。

四、生产落地中的权限、回滚与密钥轮换

Config Server 一旦成为配置中心,就会变成整个微服务体系的敏感入口。生产环境至少要给它加上认证,让客户端携带 Token 或使用 Spring Security 做访问控制。如果公司已有统一的 API 网关,也可以在网关上只放行客户端网段,阻止外部直接访问。对于配置文件里的敏感数据,还要结合 Git 仓库的权限管理,比如只允许 CI 账号写入,开发人员通过 PR 修改。

Git 后端带来的一个明显好处是版本回滚。当某个配置变更导致线上故障时,可以直接在 Git 中 revert 到上一个提交,然后触发客户端刷新,配置就会恢复。这个过程不需要重新打包镜像,比传统的发版快得多。不过要注意,如果客户端本地缓存了配置,需要确认 refresh 动作是否真正拉取了新的版本。可以在 /actuator/env 端点查看当前生效的配置值,确认变更已经到达。

密钥轮换是另一个容易忽略的环节。对称加密的 encrypt.key 建议定期更换,更换后所有旧密文必须重新加密,否则客户端会解密失败。可以在维护窗口内批量调用 /encrypt 生成新密文,再更新 Git 中的配置。对于非对称加密,可以通过更换 KeyStore 别名或轮换证书来实现。无论哪种方式,都需要提前演练解密失败时的回退流程,避免把数据库密码锁死在无法解开的密文里。

最后,监控和审计也不可缺少。Config Server 的访问日志可以记录哪个应用在什么时间拉取了哪个版本的配置,结合 Git 的提交记录,基本能还原一次配置变更的完整链路。如果团队规模不大,先用 Config Server 加 Git 的方案快速落地,等后续需要更细粒度的权限和动态密钥时,再迁移到 Vault 或云平台配置管理服务也不迟。

广告位 · 文章底部(后台-模块管理替换此区域)