Vault Repositories
使用 VaultTemplate
和映射到 Java 类的响应允许进行基本的读写删除等数据操作。Vault 存储库在 Vault 之上应用了 Spring Data 的存储库概念。Vault 存储库公开基本 CRUD 功能,并支持使用约束标识符属性、分页和排序的谓词进行查询推导。Vault 存储库使用键/值秘密引擎功能来持久保存和查询数据。从版本 2.4 开始,Spring Vault 还可以使用键/值版本 2 秘密引擎,实际秘密引擎版本在运行时发现。
版本化键/值机密引擎内的删除使用 |
在 Spring Data Commons reference documentation 中阅读有关 Spring Data 存储库的更多信息。参考文档将为你介绍 Spring Data 存储库。 |
Usage
要访问存储在 Vault 中的域实体,您可以利用存储库支持,它可以大幅简化实现这些实体。
@Secret
class Credentials {
@Id String id;
String password;
String socialSecurityNumber;
Address address;
}
我们这里有一个非常简单的域对象。请注意,它有一个使用 org.springframework.data.annotation.Id
进行注释名为 id
的属性,并且其类型上有 @Secret
注释。这两个负责创建实际密钥,用于将对象作为 JSON 持久保存到 Vault 中。
使用 |
下一步是声明一个使用域对象的存储库接口。
Credentials
entitiesinterface CredentialsRepository extends CrudRepository<Credentials, String> {
}
由于我们的存储库扩展了 CrudRepository
,它提供基本的 CRUD 和查询方法。Vault 存储库需要 Spring Data 组件。请务必在类路径中包含 spring-data-commons
和 spring-data-keyvalue
工件。
实现此目的最简单的方法是设置依赖项管理并向 pom.xml
添加工件:
然后将以下内容添加到 pom.xml
依赖项部分。
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-bom</artifactId>
<version>{springDataVersion}</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>{version}</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 的属性存储在使用密钥模式 keyspace/id 的 Vault Hash 中,在本例中为 credentials/heisenberg ,位于 key-value 机密 secrets 引擎中。 |
2 | 使用提供的 ID 在 keyspace/id 存储的对象进行检索。 |
3 | 统计 @Secret 在 Credentials 上定义的键空间 credentials 中可用实体的总数。 |
4 | 从 Vault 移除给定对象的密钥。 |
Object to Vault JSON Mapping
Vault 存储库使用 JSON 作为交换格式将对象存储在 Vault 中。由 VaultConverter
完成 JSON 与实体之间的对象映射。转换器读写 SecretDocument
,其中包含 VaultResponse
的正文。String
和 Object
的 VaultResponse`s are read from Vault and the body is deserialized by Jackson into a `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 | 复杂类型的属性映射为嵌套对象。 |
|
Type | Sample | Mapped Value |
---|---|---|
Simple Type (eg. String) |
String firstname = "Walter"; |
"firstname": "Walter" |
Complex Type (eg. Address) |
Address adress = new Address("308 Negra Arroyo Lane"); |
"address": { "street": "308 Negra Arroyo Lane" } |
List of Simple Type |
List<String> nicknames = asList("walt", "heisenberg"); |
"nicknames": ["walt", "heisenberg"] |
Map of Simple Type |
Map<String, Integer> atts = asMap("age", 51) |
"atts" : {"age" : 51} |
List of Complex Type |
List<Address> addresses = asList(new Address("308… |
"address": [{ "street": "308 Negra Arroyo Lane" }, …] |
您可以通过在 VaultCustomConversions
中注册 Converter
来定制映射行为。这些转换器可以处理从/到 LocalDate
和 SecretDocument
等类型的转换,其中第一个适用于转换简单属性,而最后一个适用于将复杂类型转换为其 JSON 表示。第二个选项可完全控制结果 SecretDocument
。将对象写入 Vault
将删除内容并重新创建整个条目,因此未映射的数据将丢失。
Queries and Query Methods
查询方法允许从方法名称自动推导出简单查询。Vault 没有查询引擎,但需要直接访问 HTTP 上下文路径。Vault 查询方法将 Vault 的 API 可能性转换为查询。查询方法执行在上下文路径下列出子级,对 ID 应用过滤,必要时使用偏移量/限制限制 ID 流,并在获取结果后应用排序。
interface CredentialsRepository extends CrudRepository<Credentials, String> {
List<Credentials> findByIdStartsWith(String prefix);
}
Vault 存储库的查询方法仅支持对 |
以下是 Vault 支持的关键字概述。
Keyword | Sample |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Sorting and Paging
查询方法通过在内存中选择从 Vault 上下文路径检索的子列表(偏移/限制)ID 来支持排序和分页。与查询方法谓词不同,排序不受特定字段的限制。在 ID 过滤后应用未分页排序,并将所有生成的秘密从 Vault 中提取。通过这种方式,查询方法只能提取作为结果的一部分返回的结果。
使用分页和排序要求在过滤 ID 之前提取秘密,这会影响性能。即使 Vault 返回的 ID 的自然顺序发生变化,排序和分页也保证返回相同的结果。因此,首先从 Vault 中提取所有 ID,然后应用排序,最后进行过滤和偏移/限制。
interface CredentialsRepository extends PagingAndSortingRepository<Credentials, String> {
List<Credentials> findTop10ByIdStartsWithOrderBySocialSecurityNumberDesc(String prefix);
List<Credentials> findByIdStarts(String prefix, Pageable pageRequest);
}
Optimistic Locking
Vault 的 key/value 秘密引擎版本 2 可以维护版本化的秘密。Spring Vault 通过域模型中使用 @Version
注解的版本属性支持版本控制。使用乐观锁可确保仅将更新应用于与版本匹配的秘密。因此,通过 cas
属性将版本属性的实际值添加到更新请求中。如果在此时另一个操作改变了秘密,则会抛出 OptimisticLockingFailureException,并且秘密不会被更新。
版本属性必须是数字属性,例如 int
或 long
,并在更新秘密时映射到 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 获取一个密码。 |
2 | 根据其 ID sample-credentials 获取密码的第二个实例。 |
3 | 更新密码,并让 Vault 增加版本。 |
4 | 更新使用上一个版本的第二个实例。该操作将失败,并出现 OptimisticLockingFailureException ,因为与此同时 Vault 中的版本已增加。 |
在删除版本化机密时,按 ID 删除将删除最新的机密。按实体删除将删除指定版本处的机密。 |
Accessing versioned secrets
Key/Value 版本 2 秘钥引擎维护可以通过在您的 Vault 存储库接口声明中实现 RevisionRepository
来访问的秘钥版本。修订存储库定义了查找方法,以获得特定标识符的修订。标识符必须是 String
。
RevisionRepository
interface RevisionCredentialsRepository extends CrudRepository<Credentials, String>,
RevisionRepository<Credentials, String, Integer> 1
{
}
1 | 第一个类型参数 (Credentials ) 表示实体类型,第二个 (String ) 表示 id 属性的类型,最后一个 (Integer ) 是版本号的类型。Vault 仅支持 String 标识符和 Integer 版本号。 |
Usage
现在,您可以使用 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));