Vault Repositories
使用 VaultTemplate
並將響應對映到 Java 類,可以進行讀、寫和刪除等基本資料操作。Vault Repositories 在 Vault 之上應用了 Spring Data 的 Repository 概念。Vault Repository 暴露了基本的 CRUD 功能,並支援帶有約束識別符號屬性的查詢派生、分頁和排序。Vault Repositories 使用鍵/值 Secret Engine 功能來持久化和查詢資料。從 2.4 版本開始,Spring Vault 還可以使用鍵/值版本 2 Secret Engine,實際的 Secret Engine 版本在執行時發現。
在版本化的鍵/值 Secret Engine 中,刪除操作使用 DELETE 命令。Secrets 不會透過 CrudRepository.delete(…) 被銷燬。 |
Vault Repositories 透過 Vault 的 sys/internal/ui/mounts/… 端點確定掛載路徑。請確保您的策略允許訪問該路徑,否則將無法使用 Repository 抽象。 |
有關 Spring Data Repositories 的更多資訊,請參閱 Spring Data Commons 參考文件。該參考文件將向您介紹 Spring Data Repositories。 |
用法
為了訪問儲存在 Vault 中的域實體,您可以利用 Repository 支援,這極大地簡化了實現過程。
@Secret
class Credentials {
@Id String id;
String password;
String socialSecurityNumber;
Address address;
}
這裡我們有一個非常簡單的域物件。請注意,它有一個名為 id
的屬性,帶有 org.springframework.data.annotation.Id
註解,並且在其型別上有一個 @Secret
註解。這兩個註解負責建立用於將物件作為 JSON 持久化到 Vault 中的實際鍵。
帶有 @Id 註解的屬性以及名稱為 id 的屬性都被視為識別符號屬性。帶有註解的屬性優先於其他屬性。 |
下一步是宣告一個使用該域物件的 Repository 介面。
Credentials
實體的基本 Repository 介面interface CredentialsRepository extends CrudRepository<Credentials, String> {
}
由於我們的 Repository 擴充套件了 CrudRepository
,它提供了基本的 CRUD 和查詢方法。Vault Repositories 需要 Spring Data 元件。請確保在您的類路徑中包含 spring-data-commons
和 spring-data-keyvalue
artifact。
最簡單的方法是設定依賴管理,並將 artifact 新增到您的 pom.xml
中。
然後將以下內容新增到 pom.xml
的 dependencies 部分。
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-bom</artifactId>
<version>2023.1.9</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- other dependency elements omitted -->
<dependency>
<groupId>org.springframework.vault</groupId>
<artifactId>spring-vault-core</artifactId>
<version>3.1.3</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-keyvalue</artifactId>
<!-- Version inherited from the BOM -->
</dependency>
</dependencies>
我們中間需要一個東西來連線這些事物,那就是相應的 Spring 配置。
@Configuration
@EnableVaultRepositories
class ApplicationConfig {
@Bean
VaultTemplate vaultTemplate() {
return new VaultTemplate(…);
}
}
有了上面的設定,我們就可以繼續將 CredentialsRepository
注入到我們的元件中。
@Autowired CredentialsRepository repo;
void basicCrudOperations() {
Credentials creds = new Credentials("heisenberg", "327215", "AAA-GG-SSSS");
rand.setAddress(new Address("308 Negra Arroyo Lane", "Albuquerque", "New Mexico", "87104"));
repo.save(creds); (1)
repo.findOne(creds.getId()); (2)
repo.count(); (3)
repo.delete(creds); (4)
}
1 | 將 Credentials 的屬性儲存在 Vault Hash 中,鍵模式為 keyspace/id ,在本例中為 credentials/heisenberg ,儲存在鍵-值 secret secrets engine 中。 |
2 | 使用提供的 ID 來檢索儲存在 keyspace/id 的物件。 |
3 | 計算由 Credentials 上的 @Secret 定義的 credentials 鍵空間中可用實體的總數。 |
4 | 從 Vault 中移除給定物件的鍵。 |
物件到 Vault JSON 對映
Vault Repositories 使用 JSON 作為交換格式將物件儲存在 Vault 中。JSON 與實體之間的物件對映由 VaultConverter
完成。轉換器讀取和寫入包含來自 VaultResponse
的主體的 SecretDocument
。VaultResponse
從 Vault 讀取,其主體由 Jackson 反序列化為一個 String
和 Object
的 Map
。預設的 VaultConverter
實現讀取帶有巢狀值、List
和 Map
物件的 Map
,並將它們轉換為實體,反之亦然。
考慮到前幾節中的 Credentials
型別,預設對映如下:
{
"_class": "org.example.Credentials", (1)
"password": "327215", (2)
"socialSecurityNumber": "AAA-GG-SSSS",
"address": { (3)
"street": "308 Negra Arroyo Lane",
"city": "Albuquerque",
"state": "New Mexico",
"zip": "87104"
}
}
1 | _class 屬性包含在根級別以及任何巢狀介面或抽象型別上。 |
2 | 簡單屬性值透過路徑對映。 |
3 | 複雜型別的屬性對映為巢狀物件。 |
@Id 屬性必須對映到 String 。 |
型別 | 示例 | 對映值 |
---|---|---|
簡單型別 |
String firstname = "Walter"; |
"firstname": "Walter" |
複雜型別 |
Address adress = new Address("308 Negra Arroyo Lane"); |
"address": { "street": "308 Negra Arroyo Lane" } |
List |
List<String> nicknames = asList("walt", "heisenberg"); |
"nicknames": ["walt", "heisenberg"] |
Map |
Map<String, Integer> atts = asMap("age", 51) |
"atts" : {"age" : 51} |
List |
List<Address> addresses = asList(new Address("308… |
"address": [{ "street": "308 Negra Arroyo Lane" }, …] |
您可以透過在 VaultCustomConversions
中註冊 Converter
來定製對映行為。這些轉換器可以負責從/到 LocalDate
等型別以及 SecretDocument
的轉換,其中前者適合轉換簡單屬性,而後者適合將複雜型別轉換為其 JSON 表示。第二種選項提供了對結果 SecretDocument
的完全控制。將物件寫入 Vault
將刪除原有內容並重新建立整個條目,因此未對映的資料將會丟失。
查詢和查詢方法
查詢方法允許從方法名稱自動派生簡單查詢。Vault 沒有查詢引擎,但需要直接訪問 HTTP 上下文路徑。Vault 查詢方法將 Vault 的 API 功能轉換為查詢。查詢方法執行時會列出上下文路徑下的子項,對 Id 應用過濾,可選擇使用偏移量/限制來限制 Id 流,並在獲取結果後應用排序。
interface CredentialsRepository extends CrudRepository<Credentials, String> {
List<Credentials> findByIdStartsWith(String prefix);
}
Vault Repositories 的查詢方法僅支援對 @Id 屬性進行謂詞的查詢。 |
以下是 Vault 支援的關鍵字概覽。
關鍵字 | 示例 |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
排序和分頁
查詢方法透過在記憶體中選擇一個子列表(偏移量/限制)來支援排序和分頁,該子列表包含從 Vault 上下文路徑檢索到的 Id。排序不像查詢方法謂詞那樣僅限於特定欄位。未分頁的排序在 Id 過濾後應用,並且所有結果 Secrets 都從 Vault 獲取。這樣,查詢方法只會獲取作為結果一部分返回的結果。
使用分頁和排序需要在過濾 Id 之前獲取 Secret,這會影響效能。排序和分頁保證即使 Vault 返回的 Id 自然順序發生變化,也能返回相同的結果。因此,首先從 Vault 獲取所有 Id,然後應用排序,之後進行過濾和偏移量/限制。
interface CredentialsRepository extends PagingAndSortingRepository<Credentials, String> {
List<Credentials> findTop10ByIdStartsWithOrderBySocialSecurityNumberDesc(String prefix);
List<Credentials> findByIdStarts(String prefix, Pageable pageRequest);
}
樂觀鎖
Vault 的鍵/值 Secret Engine 版本 2 可以維護版本化的 Secret。Spring Vault 透過域模型中帶有 @Version
註解的版本屬性來支援版本控制。使用樂觀鎖確保更新僅應用於版本匹配的 Secret。因此,版本屬性的實際值透過 cas
屬性新增到更新請求中。如果在此期間另一個操作修改了 Secret,則會丟擲 OptimisticLockingFailureException
並且 Secret 不會被更新。
版本屬性必須是數字屬性,例如 int
或 long
,並在更新 Secret 時對映到 cas
屬性。
@Secret
class VersionedCredentials {
@Id String id;
@Version int version;
String password;
String socialSecurityNumber;
Address address;
}
以下示例展示了這些特性。
VersionedCredentialsRepository repo = …;
VersionedCredentials credentials = repo.findById("sample-credentials").get(); (1)
VersionedCredentials concurrent = repo.findById("sample-credentials").get(); (2)
credentials.setPassword("something-else");
repos.save(credentials); (3)
concurrent.setPassword("concurrent change");
repos.save(concurrent); // throws OptimisticLockingFailureException (4)
1 | 透過其 Id sample-credentials 獲取一個 Secret。 |
2 | 透過其 Id sample-credentials 獲取該 Secret 的第二個例項。 |
3 | 更新 Secret 並讓 Vault 遞增版本。 |
4 | 更新使用前一個版本的第二個例項。由於在此期間 Vault 中的版本已遞增,該操作將失敗並丟擲 OptimisticLockingFailureException 。 |
刪除版本化的 Secret 時,按 Id 刪除會刪除最新的 Secret。按實體刪除會刪除指定版本處的 Secret。 |
訪問版本化的 Secret
鍵/值版本 2 Secret Engine 維護 Secret 的版本,可以透過在您的 Vault Repository 介面宣告中實現 RevisionRepository
來訪問這些版本。Revision Repositories 定義了查詢特定識別符號版本的方法。識別符號必須是 String
型別。
RevisionRepository
interface RevisionCredentialsRepository extends CrudRepository<Credentials, String>,
RevisionRepository<Credentials, String, Integer> (1)
{
}
1 | 第一個型別引數(Credentials )表示實體型別,第二個型別引數(String )表示 Id 屬性的型別,最後一個型別引數(Integer )是修訂號的型別。Vault 僅支援 String 識別符號和 Integer 修訂號。 |
用法
現在您可以使用 RevisionRepository
中的方法來查詢實體的修訂版本,如下例所示:
RevisionRepository
RevisionCredentialsRepository repo = …;
Revisions<Integer, Credentials> revisions = repo.findRevisions("my-secret-id");
Page<Revision<Integer, Credentials>> firstPageOfRevisions = repo.findRevisions("my-secret-id", Pageable.ofSize(4));