1. 概述
HashiCorp的Vault是一种用于存储和保护机密的工具。总的来说,Vault解决的是如何管理机密的软件开发安全问题。要了解更多信息,请在此处查看我们的文章。
Spring Vault为HashiCorp的Vault提供Spring抽象。
在本教程中,我们将通过一个示例来说明如何在Vault中存储和检索机密。
2. Maven依赖
首先,让我们看一下开始使用Spring Vault所需的依赖项:
<dependencies>
<dependency>
<groupId>org.springframework.vault</groupId>
<artifactId>spring-vault-core</artifactId>
<version>2.3.2</version>
</dependency>
</dependencies>
可以在Maven Central上找到最新版本的spring-vault-core。
3. 配置Vault
现在让我们完成配置Vault所需的步骤。
3.1 创建VaultTemplate
为了保护我们的机密,我们必须实例化一个VaultTemplate,为此我们需要VaultEndpoint和TokenAuthentication实例:
VaultTemplate vaultTemplate = new VaultTemplate(new VaultEndpoint(), new TokenAuthentication("00000000-0000-0000-0000-000000000000"));
3.2 创建VaultEndpoint
有几种方法可以实例化VaultEndpoint。让我们来看看其中的一些。
第一个是使用默认构造函数简单地实例化它,这将创建一个指向http://localhost:8200的默认端点:
VaultEndpoint endpoint = new VaultEndpoint();
另一种方法是通过指定Vault的主机和端口来创建VaultEndpoint:
VaultEndpoint endpoint = VaultEndpoint.create("host", port);
最后,我们还可以从Vault URL创建它:
VaultEndpoint endpoint = VaultEndpoint.from(new URI("vault uri"));
这里有几点需要注意-Vault将配置根令牌00000000-0000-0000-0000-000000000000以运行此应用程序。
在我们的示例中,我们使用了TokenAuthentication,但也支持其他身份验证方法。
4. 使用Spring配置Vault Bean
使用Spring,我们可以通过多种方式配置Vault。一种是通过扩展AbstractVaultConfiguration,另一种是使用EnvironmentVaultConfiguration,它利用了Spring的环境属性。
我们现在将讨论这两种方式。
4.1 使用AbstractVaultConfiguration
让我们创建一个扩展AbstractVaultConfiguration的类来以配置Spring Vault:
@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 Vault通过扩展抽象类AbstractVaultConfiguration来配置Vault bean。
我们只需要提供配置VaultEndpoint和ClientAuthentication的实现。
4.2 使用EnvironmentVaultConfiguration
我们还可以使用EnvironmentVaultConfiguration配置Spring Vault:
@Configuration
@PropertySource(value = { "vault-config.properties" })
@Import(value = EnvironmentVaultConfiguration.class)
public class VaultEnvironmentConfig {
}
EnvironmentVaultConfiguration使用Spring的PropertySource来配置Vault beans。我们只需要为属性文件提供一些可接收的条目。
有关所有预定义属性的更多信息,请参见官方文档。
要配置Vault,我们至少需要这几个属性:
vault.uri=https://localhost:8200
vault.token=00000000-0000-0000-0000-000000000000
5. 保护机密
我们将创建一个映射到用户名和密码的简单Credentials类:
public class Credentials {
private String username;
private String password;
// standard constructors, getters, setters
}
现在,让我们看看如何使用VaultTemplate保护我们的Credentials对象:
Credentials credentials = new Credentials("username", "password");
vaultTemplate.write("secret/myapp", credentials);
完成这些行后,我们的机密就被存储了。
接下来,我们将了解如何访问它们。
6. 访问机密
我们可以使用VaultTemplate中的read()方法访问安全机密,该方法返回VaultResponseSupport作为响应:
VaultResponseSupport<Credentials> response = vaultTemplate
.read("secret/myapp", Credentials.class);
String username = response.getData().getUsername();
String password = response.getData().getPassword();
我们的机密值现在准备好了。
7. Vault Repository
Vault Repository是Spring Vault 2.0附带的一个方便的功能。它在Vault之上应用了Spring Data的Repository概念。
让我们深入了解如何在实践中使用这个新功能。
7.1 @Secret和@Id注解
Spring提供了这两个注解来标记我们想要持久化到Vault中的对象。
所以首先,我们需要标注我们的域类Credentials:
@Secret(backend = "credentials", value = "myapp")
public class Credentials {
@Id
private String username;
// Same code
}
@Secret注解的value属性用于区分域类型。backend属性表示机密后端挂载。
另一方面,@Id只是简单地划分了我们对象的标识符。
7.2 Vault Repository
现在,让我们定义一个使用我们的域对象Credentials的Repository接口:
public interface CredentialsRepository extends CrudRepository<Credentials, String> {
}
正如我们所看到的,我们的Repository扩展了CrudRepository,它提供了基本的CRUD和查询方法。
接下来,让我们将CredentialsRepository注入CredentialsService并实现一些CRUD方法:
public class CredentialsService {
@Autowired
private CredentialsRepository credentialsRepository;
public Credentials saveCredentials(Credentials credentials) {
return credentialsRepository.save(credentials);
}
public Optional<Credentials> findById(String username) {
return credentialsRepository.findById(username);
}
}
现在我们已经添加了所有缺失的拼图,让我们使用测试用例确认一切正常。
首先,让我们从save()方法的测试用例开始:
@Test
public void givenCredentials_whenSave_thenReturnCredentials() {
// Given
Credentials credentials = new Credentials("login", "password");
Mockito.when(credentialsRepository.save(credentials))
.thenReturn(credentials);
// When
Credentials savedCredentials = credentialsService.saveCredentials(credentials);
// Then
assertNotNull(savedCredentials);
assertEquals(savedCredentials.getUsername(), credentials.getUsername());
assertEquals(savedCredentials.getPassword(), credentials.getPassword());
}
最后,让我们用一个测试用例来确认一下findById()方法:
@Test
public void givenId_whenFindById_thenReturnCredentials() {
// Given
Credentials credentials = new Credentials("login", "p@ssw@rd");
Mockito.when(credentialsRepository.findById("login"))
.thenReturn(Optional.of(credentials));
// When
Optional<Credentials> returnedCredentials = credentialsService.findById("login");
// Then
assertNotNull(returnedCredentials);
assertNotNull(returnedCredentials.get());
assertEquals(returnedCredentials.get().getUsername(), credentials.getUsername());
assertEquals(returnedCredentials.get().getPassword(), credentials.getPassword());
}
8. 总结
在本文中,我们通过一个示例展示了Spring Vault在典型场景中的工作原理,了解了Spring Vault的基础知识。
与往常一样,本教程的完整源代码可在GitHub上获得。