知識庫 / Spring RSS 訂閱

Spring Vault

Security,Spring
HongKong
4
01:41 PM · Dec 06 ,2025

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 類的實例,該實例需要 VaultEndpointTokenAuthentication 實例。

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。 我們只需要提供 VaultEndpointClientAuthentication 的實現即可。

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-000000000000

5. 安全密鑰

我們將創建一個簡單的 憑據 類,該類將映射到用户名和密碼:

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 在典型場景下的工作方式。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.