1. 概述
Spring Cloud Config 是 Spring 提供的客户端/服务器架构方案,用于在多个应用和环境之间存储和分发分布式配置。
理想情况下,这个配置存储应该通过 Git 版本控制进行管理,并且可以在应用运行时修改。虽然它与 Spring 应用完美契合(支持所有配置文件格式以及 Environment、PropertySource 或 @Value 等特性),但它也能在任何运行任何编程语言的环境中使用。
本文将重点介绍如何:
- 设置基于 Git 的配置服务器
- 在简单的 REST 应用服务器中使用它
- 构建包含加密属性值的安全环境
2. 项目设置与依赖
首先创建两个新的 Maven 项目。服务器项目需要以下依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
客户端项目则只需要:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
3. 配置服务器实现
应用核心是一个配置类,具体来说是带有 @SpringBootApplication 注解的主类,通过 @EnableConfigServer 自动完成所有必要设置:
@SpringBootApplication
@EnableConfigServer
public class ConfigServer {
public static void main(String[] arguments) {
SpringApplication.run(ConfigServer.class, arguments);
}
}
现在需要配置服务器监听端口和提供版本控制配置内容的 Git URL。后者支持 http、ssh 或本地文件系统协议。
⚠️ 踩坑提醒:如果计划使用多个指向同一仓库的配置服务器实例,建议配置服务器将仓库克隆到本地临时文件夹。但要注意:启用双因素认证的私有仓库很难处理!遇到这种情况,直接克隆到本地文件系统再操作会更省心。
我们还需要在 application.properties 中设置 Basic Authentication 的用户名和密码,避免每次重启应用都生成随机密码:
server.port=8888
spring.cloud.config.server.git.uri=ssh://localhost/config-repo
spring.cloud.config.server.git.clone-on-start=true
spring.security.user.name=root
spring.security.user.password=s3cr3t
4. 使用 Git 仓库存储配置
完成服务器配置后,需要在指定 URL 初始化 Git 仓库,创建属性文件并填充值。
配置文件命名规则类似 Spring 的 application.properties,但用客户端的应用名(如 spring.application.name 的值)替换 "application",后跟短横线和激活的 profile。例如:
$> git init
$> echo 'user.role=Developer' > config-client-development.properties
$> echo 'user.role=User' > config-client-production.properties
$> git add .
$> git commit -m 'Initial config-client properties'
❌ 故障排查:遇到 ssh 认证问题时,检查 SSH 服务器上的 ~/.ssh/known_hosts 和 ~/.ssh/authorized_keys 文件。
5. 查询配置
现在可以启动服务器了。服务器提供的基于 Git 的配置 API 可通过以下路径访问:
/{application}/{profile}[/{label}]
/{application}-{profile}.yml
/{label}/{application}-{profile}.yml
/{application}-{profile}.properties
/{label}/{application}-{profile}.properties
其中:
- *{label}*:Git 分支
- *{application}*:客户端应用名
- *{profile}*:客户端激活的 profile
例如,获取 master 分支下开发环境配置:
$> curl http://root:s3cr3t@localhost:8888/config-client/development/master
6. 客户端实现
接下来实现客户端。这是一个简单的客户端应用,包含一个带 GET 方法的 REST 控制器。
✅ 关键配置:Spring Boot 2.4 引入了通过 spring.config.import 加载配置的新方式,这是连接 Config Server 的默认方法:
@SpringBootApplication
@RestController
public class ConfigClient {
@Value("${user.role}")
private String role;
public static void main(String[] args) {
SpringApplication.run(ConfigClient.class, args);
}
@GetMapping(
value = "/whoami/{username}",
produces = MediaType.TEXT_PLAIN_VALUE)
public String whoami(@PathVariable("username") String username) {
return String.format("Hello!
You're %s and you'll become a(n) %s...\n", username, role);
}
}
在 application.properties 中配置应用名、激活 profile 和连接信息:
spring.application.name=config-client
spring.profiles.active=development
spring.config.import=optional:configserver:http://root:s3cr3t@localhost:8888
这会连接到 http://localhost:8888 的 Config Server,并使用 HTTP Basic 认证。也可通过 spring.cloud.config.username
和 spring.cloud.config.password
单独设置。
⚠️ 注意:如果希望服务在无法连接 Config Server 时启动失败,移除 optional: 前缀即可。
测试配置是否正确加载:
$> curl http://localhost:8080/whoami/Mr_Pink
若返回以下内容,说明配置服务器和客户端工作正常:
Hello! You're Mr_Pink and you'll become a(n) Developer...
7. 加密与解密
前提:使用 Spring 加密解密功能时,需在 JVM 中安装 Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files。可从 Oracle 下载,按说明安装。部分 Linux 发行版可通过包管理器安装。
配置服务器支持属性值的加密解密,因此我们可以用公共仓库存储敏感数据(如用户名密码)。加密值以 {cipher} 开头,可通过调用 /encrypt 接口生成(需配置对称密钥或密钥对)。
解密接口也通过 /decrypt 提供。两个接口都支持包含应用名和 profile 占位符的路径:*/*/{name}/{profile}*,这对按客户端控制加密很有用。但使用前需先配置加密密钥。
7.1 CSRF 处理
默认情况下,Spring Security 为所有请求启用 CSRF 保护。为使用 /encrypt 和 /decrypt 接口,需禁用它们的 CSRF 防护:
@Configuration
public class SecurityConfiguration {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf(csrf -> csrf.ignoringRequestMatchers(
"/encrypt/**", "/decrypt/**"
))
//...
}
}
7.2 密钥管理
配置服务器默认支持对称和非对称加密:
- 对称加密:在 application.properties 中设置 encrypt.key 为自定义密钥,或通过环境变量 ENCRYPT_KEY 传递。
- 非对称加密:设置 encrypt.key 为 PEM 编码字符串,或配置 keystore。
为构建高安全环境,我们选择 keystore 方案。先用 Java keytool 生成包含 RSA 密钥对的 keystore:
$> keytool -genkeypair -alias config-server-key \
-keyalg RSA -keysize 4096 -sigalg SHA512withRSA \
-dname 'CN=Config Server,OU=Spring Cloud,O=Baeldung' \
-keypass my-k34-s3cr3t -keystore config-server.jks \
-storepass my-s70r3-s3cr3t
将 keystore 添加到服务器配置并重启:
encrypt.keyStore.location=classpath:/config-server.jks
encrypt.keyStore.password=my-s70r3-s3cr3t
encrypt.keyStore.alias=config-server-key
encrypt.keyStore.secret=my-k34-s3cr3t
调用加密接口生成密文,添加到配置文件:
$> export PASSWORD=$(curl -X POST --data-urlencode d3v3L \
http://root:s3cr3t@localhost:8888/encrypt)
$> echo "user.password={cipher}$PASSWORD" >> config-client-development.properties
$> git commit -am 'Added encrypted password'
$> curl -X POST http://root:s3cr3t@localhost:8888/refresh
修改 ConfigClient 测试解密:
@SpringBootApplication
@RestController
public class ConfigClient {
...
@Value("${user.password}")
private String password;
...
public String whoami(@PathVariable("username") String username) {
return String.format("Hello!
You're %s and you'll become a(n) %s, " +
"but only if your password is '%s'!\n",
username, role, password);
}
}
验证解密是否成功:
$> curl http://localhost:8080/whoami/Mr_Pink
Hello! You're Mr_Pink and you'll become a(n) Developer, \
but only if your password is 'd3v3L'!
7.3 使用多密钥
若需为不同应用使用不同密钥,可在 {cipher} 前缀和 BASE64 编码值之间添加 {name:value} 形式的前缀。
服务器原生支持 {secret:my-crypto-secret} 或 {key:my-key-alias} 等前缀。后者需在 application.properties 中配置 keystore,服务器会查找匹配的密钥别名。例如:
user.password={cipher}{secret:my-499-s3cr3t}AgAMirj1DkQC0WjRv...
user.password={cipher}{key:config-client-key}AgAMirj1DkQC0WjRv...
无 keystore 时,需实现 TextEncryptorLocator 类型的 @Bean 来处理密钥查找。
7.4 提供加密属性
若要禁用服务端加密,在本地处理属性解密,可在服务器配置中添加:
spring.cloud.config.server.encrypt.enabled=false
同时删除所有 encrypt. 属性以禁用 REST 接口。
8. 总结
现在我们已经能够创建配置服务器,从 Git 仓库向客户端应用提供配置文件集。还可以实现更多功能:
- 提供 YAML 或 Properties 格式配置(而非 JSON),并解析占位符。这在非 Spring 环境中尤其有用。
- 提供纯文本配置文件(可选解析占位符),例如提供环境相关的日志配置。
- 将配置服务器嵌入应用中,使其从 Git 仓库自我配置,而非作为独立服务运行。此时需根据场景调整属性或移除 @EnableConfigServer。
- 将配置服务器注册到 Spring Netflix Eureka 服务发现,并在客户端启用自动服务器发现。当服务器位置不固定或可能变动时这很重要。
本文源码可在 Github 获取。