1. 概述
HashiCorp 的 Vault 是一款用於存儲和保護密鑰的工具。Vault 總體上解決了軟件開發安全問題,即如何管理密鑰。要了解更多信息,請查看我們的文章 Spring Vault 提供 Spring 抽象層,用於訪問 HashiCorp 的 Vault。
在本教程中,我們將演示如何從 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 類的實例,該實例需要 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();另一種方法是創建一個 VaultEndpoint,通過指定 Vault 的主機和端口:
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 bean。 我們只需要提供包含可接受條目的屬性文件。
有關所有預定義的屬性的更多信息,請參閲 官方文檔。
要配置 Vault,我們需要至少提供幾個屬性:
vault.uri=https://localhost:8200
vault.token=00000000-0000-0000-0000-0000000000005. 安全密鑰
我們將創建一個簡單的 憑據 類,該類將映射到用户名和密碼:
public class Credentials {
private String username;
private String password;
// standard constructors, getters, setters
}現在,讓我們看看如何使用Credentials對象以及VaultTemplate來對其進行安全保護。
Credentials credentials = new Credentials("username", "password");
VaultKeyValueOperations vaultKeyValueOperations = vaultTemplate.opsForKeyValue("credentials/myapp",
VaultKeyValueOperationsSupport.KeyValueBackend.KV_2)
vaultKeyValueOperations.put(credentials.getUsername(), credentials);需要注意的是,我們使用了 VaultKeyValueOperations 實例,因為它支持既有版本控制又沒有版本控制的鍵值存儲後端。
接下來,我們將看到如何訪問它們。
6. 訪問密鑰
我們可以使用 VaultKeyValueOperations 中的 get() 方法訪問受保護的密鑰,該方法返回 VaultResponseSupport 作為響應:
VaultResponseSupport response = vaultKeyValueOperations.get(username, Credentials.class);
String username = response.getData().getUsername();
String password = response.getData().getPassword();我們的核心價值觀已就緒。
7. Vault 存儲庫
Vault 存儲庫是 Spring Vault 2.0 中一個方便的功能。它在 Vault 上應用了 Spring Data 的存儲庫概念。
讓我們深入瞭解如何使用此新功能來處理非版本控制的鍵值存儲。
7.1. @Secret 和 @Id 註解
Spring 提供了這兩個註解,用於標記我們希望在 Vault 中持久化的對象。
因此,首先我們需要裝飾我們的領域類型 Credentials:
@Secret(backend = "credentials", value = "myapp")
public class Credentials {
@Id
private String username;
// Same code
]value 屬性在 @Secret 註解中用於區分領域類型。backend 屬性表示秘密後端掛載。
另一方面,@Id 則簡單地標明瞭我們對象的標識符。
7.2. 密鑰庫倉庫
現在,讓我們定義一個使用我們的領域對象 Credentials 的倉庫接口:
public interface CredentialsRepository extends CrudRepository<Credentials, String> {
}如我們所見,我們的倉庫擴展了 CrudRepository,它提供了基本的 CRUD 和查詢方法。
接下來,我們將 CredentialsRepository 注入到 CredentialsService 中,並實現一些 CRUD 方法:
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);
}
}現在我們已經添加了所有缺失的拼圖碎片,讓我們使用測試用例確認一切都按預期工作。
首先,我們從 save() 方法的測試用例開始。
@Test
public void givenCredentials_whenSave_thenReturnCredentials() throws InterruptedException {
Assume.assumeTrue("v1".equals(API_VERSION));
credentialsService = new CredentialsService(vaultTemplate, credentialsRepository);
// Given
Credentials credentials = new Credentials("login", "password");
// When
Credentials savedCredentials = credentialsService.saveCredentials(credentials);
// Then
assertNotNull(savedCredentials);
assertEquals(credentials.getUsername(), savedCredentials.getUsername());
assertEquals(credentials.getPassword(), savedCredentials.getPassword());
}最後,讓我們通過一個測試用例來驗證 findById() 方法:
@Test
public void givenId_whenFindById_thenReturnCredentials() {
// Given
Assume.assumeTrue("v1".equals(API_VERSION));
Credentials expectedCredentials = new Credentials("login", "p@ssw@rd");
credentialsService.saveCredentials(expectedCredentials);
// When
Optional retrievedCredentials = credentialsService.findById(expectedCredentials.getUsername());
// Then
assertNotNull(retrievedCredentials);
assertNotNull(retrievedCredentials.get());
assertEquals(expectedCredentials.get().getUsername(), retrievedCredentials.getUsername());
assertEquals(expectedCredentials.getPassword(), retrievedCredentials.get().getPassword());
}我們必須注意到,我們僅使用 CredentialsRepository 僅用於未版本化的 (“v1”) 鍵值存儲後端。
8. 結論
在本文中,我們學習了 Spring Vault 的基本原理,並通過一個示例展示了 Vault 在典型場景下的工作方式。