1. 概述
HashiCorp Vault 是一个用于安全存储和管理敏感信息(如密码、API 密钥等)的工具。它主要解决的是在软件开发过程中如何妥善处理敏感数据的问题。如果你对 Vault 还不太了解,可以参考我们之前的文章:Vault 基础入门。
而 Spring Vault 则是 Spring 生态中针对 HashiCorp Vault 提供的一套抽象封装,简化了在 Spring 应用中与 Vault 的交互方式。
本文将通过一个实际例子,带你快速掌握如何使用 Spring Vault 来存储和读取敏感信息。
2. Maven 依赖配置
首先,我们需要引入 Spring Vault 的核心依赖:
<dependencies>
<dependency>
<groupId>org.springframework.vault</groupId>
<artifactId>spring-vault-core</artifactId>
<version>3.1.1</version>
</dependency>
</dependencies>
最新的 spring-vault-core
版本可以在 Maven Central 上找到。
3. 配置 Vault 客户端
要操作 Vault,我们需要先配置好客户端。
3.1. 创建 VaultTemplate
我们可以通过 VaultTemplate
来与 Vault 进行交互。创建它需要两个核心组件:VaultEndpoint
和 TokenAuthentication
:
VaultTemplate vaultTemplate = new VaultTemplate(
new VaultEndpoint(),
new TokenAuthentication("00000000-0000-0000-0000-000000000000")
);
3.2. 配置 VaultEndpoint
有几种方式可以创建 VaultEndpoint
实例:
✅ 默认方式(适用于本地开发):
VaultEndpoint endpoint = new VaultEndpoint();
这会默认连接到 http://localhost:8200
。
✅ 指定主机和端口:
VaultEndpoint endpoint = VaultEndpoint.create("host", port);
✅ 使用完整 URI:
VaultEndpoint endpoint = VaultEndpoint.from(new URI("vault uri"));
⚠️ 注意:这里我们使用的是一个默认的 root token 00000000-0000-0000-0000-000000000000
,仅用于演示,生产环境请勿使用。
除了 TokenAuthentication
,Spring Vault 还支持其他多种认证方式,详见官方文档:认证方式。
4. 使用 Spring 配置 Vault Bean
在 Spring 中,有两种主要方式来配置 Vault Bean:
- 继承
AbstractVaultConfiguration
- 使用
EnvironmentVaultConfiguration
并配合属性文件
我们分别来看一下。
4.1. 使用 AbstractVaultConfiguration
创建一个配置类继承 AbstractVaultConfiguration
:
@Configuration
public class VaultConfig extends AbstractVaultConfiguration {
@Override
public ClientAuthentication clientAuthentication() {
return new TokenAuthentication("00000000-0000-0000-0000-000000000000");
}
@Override
public VaultEndpoint vaultEndpoint() {
return VaultEndpoint.create("host", 8020);
}
}
这种方式和前面手动创建的方式类似,只是封装成了 Spring 配置类,便于管理。
4.2. 使用 EnvironmentVaultConfiguration
另一种方式是使用 EnvironmentVaultConfiguration
,通过属性文件来配置:
@Configuration
@PropertySource(value = { "vault-config.properties" })
@Import(value = EnvironmentVaultConfiguration.class)
public class VaultEnvironmentConfig {
}
然后在 vault-config.properties
文件中配置如下内容:
vault.uri=https://localhost:8200
vault.token=00000000-0000-0000-0000-000000000000
更多信息可参考官方文档:EnvironmentVaultConfiguration 配置
5. 存储敏感信息
我们先定义一个简单的实体类 Credentials
:
public class Credentials {
private String username;
private String password;
// 标准构造函数、getter、setter 省略
}
接着使用 VaultTemplate
来保存它:
Credentials credentials = new Credentials("username", "password");
VaultKeyValueOperations vaultKeyValueOperations = vaultTemplate.opsForKeyValue(
"credentials/myapp",
VaultKeyValueOperationsSupport.KeyValueBackend.KV_2
);
vaultKeyValueOperations.put(credentials.getUsername(), credentials);
这里我们使用了 VaultKeyValueOperations
,它支持 KV v1 和 v2 两种格式。
6. 读取敏感信息
通过 get()
方法可以读取之前保存的信息:
VaultResponseSupport<Credentials> response = vaultKeyValueOperations.get(username, Credentials.class);
String username = response.getData().getUsername();
String password = response.getData().getPassword();
这样我们就成功取回了敏感信息 ✅
7. Vault Repository
Spring Vault 2.0 引入了一个非常实用的功能:Vault Repository,它借鉴了 Spring Data Repository 的设计思想,使得操作 Vault 更加面向对象。
我们来看看如何使用这个功能(仅支持 KV v1)。
7.1. @Secret 与 @Id 注解
使用这两个注解可以标记我们要持久化的对象:
@Secret(backend = "credentials", value = "myapp")
public class Credentials {
@Id
private String username;
// 其他字段和方法省略
}
@Secret
:标识存储路径,backend
是挂载点,value
是路径后缀。@Id
:标识主键字段。
7.2. 定义 Repository 接口
定义一个继承 CrudRepository
的接口:
public interface CredentialsRepository extends CrudRepository<Credentials, String> {
}
然后在 Service 中注入并使用:
public class CredentialsService {
private final VaultTemplate vaultTemplate;
private CredentialsRepository credentialsRepository;
private final VaultKeyValueOperations vaultKeyValueOperations;
@Autowired
public CredentialsService(VaultTemplate vaultTemplate, CredentialsRepository credentialsRepository) {
this.vaultTemplate = vaultTemplate;
this.credentialsRepository = credentialsRepository;
this.vaultKeyValueOperations = vaultTemplate.opsForKeyValue(
"credentials/myapp",
VaultKeyValueOperationsSupport.KeyValueBackend.KV_2
);
}
public Credentials saveCredentials(Credentials credentials) {
return credentialsRepository.save(credentials);
}
public Optional<Credentials> findById(String username) {
return credentialsRepository.findById(username);
}
}
7.3. 测试代码
✅ 保存测试:
@Test
public void givenCredentials_whenSave_thenReturnCredentials() throws InterruptedException {
Assume.assumeTrue("v1".equals(API_VERSION));
credentialsService = new CredentialsService(vaultTemplate, credentialsRepository);
Credentials credentials = new Credentials("login", "password");
Credentials savedCredentials = credentialsService.saveCredentials(credentials);
assertNotNull(savedCredentials);
assertEquals(credentials.getUsername(), savedCredentials.getUsername());
assertEquals(credentials.getPassword(), savedCredentials.getPassword());
}
✅ 查询测试:
@Test
public void givenId_whenFindById_thenReturnCredentials() {
Assume.assumeTrue("v1".equals(API_VERSION));
Credentials expectedCredentials = new Credentials("login", "p@ssw@rd");
credentialsService.saveCredentials(expectedCredentials);
Optional<Credentials> retrievedCredentials = credentialsService.findById(expectedCredentials.getUsername());
assertNotNull(retrievedCredentials);
assertNotNull(retrievedCredentials.get());
assertEquals(expectedCredentials.getUsername(), retrievedCredentials.get().getUsername());
assertEquals(expectedCredentials.getPassword(), retrievedCredentials.get().getPassword());
}
⚠️ 再次提醒:Vault Repository 仅支持未版本化的 KV v1 后端。
8. 总结
在这篇文章中,我们介绍了 Spring Vault 的基本使用方式,包括配置客户端、保存和读取敏感信息,以及使用 Vault Repository 来简化操作。
源码示例已上传到 GitHub:Spring Vault 示例代码
📌 踩坑提醒:
- 使用 KV v2 时,某些 API 行为与 v1 不一致,注意区分。
- Token 权限不足时会报错,请确保配置正确的策略和权限。
- 生产环境务必避免使用默认 root token,建议使用 AppRole 或 Kubernetes 认证方式。