Vault Repositories
使用 VaultTemplate
和映射到 Java 类的响应允许进行基本的读写删除等数据操作。Vault 存储库在 Vault 之上应用了 Spring Data 的存储库概念。Vault 存储库公开基本 CRUD 功能,并支持使用约束标识符属性、分页和排序的谓词进行查询推导。Vault 存储库使用键/值秘密引擎功能来持久保存和查询数据。从版本 2.4 开始,Spring Vault 还可以使用键/值版本 2 秘密引擎,实际秘密引擎版本在运行时发现。
Working with VaultTemplate
and responses mapped to Java classes allows basic data operations like read, write and delete.
Vault repositories apply Spring Data’s repository concept on top of Vault.
A Vault repository exposes basic CRUD functionality and supports query derivation with predicates constraining the identifier property, paging and sorting.
Vault repositories use the key/value secrets engine functionality to persist and query data.
As of version 2.4, Spring Vault can use additionally key/value version 2 secrets engine, the actual secrets engine version is discovered during runtime.
版本化键/值机密引擎内的删除使用 |
Deletes within versioned key/value secrets engine use the |
在 Spring Data Commons reference documentation 中阅读有关 Spring Data 存储库的更多信息。参考文档将为你介绍 Spring Data 存储库。 |
Read more about Spring Data Repositories in the Spring Data Commons reference documentation. The reference documentation will give you an introduction to Spring Data repositories. |
Usage
要访问存储在 Vault 中的域实体,您可以利用存储库支持,它可以大幅简化实现这些实体。
To access domain entities stored in Vault you can leverage repository support that eases implementing those quite significantly.
@Secret
class Credentials {
@Id String id;
String password;
String socialSecurityNumber;
Address address;
}
我们这里有一个非常简单的域对象。请注意,它有一个使用 org.springframework.data.annotation.Id
进行注释名为 id
的属性,并且其类型上有 @Secret
注释。这两个负责创建实际密钥,用于将对象作为 JSON 持久保存到 Vault 中。
We have a pretty simple domain object here.
Note that it has a property named id
annotated with
org.springframework.data.annotation.Id
and a @Secret
annotation on its type.
Those two are responsible for creating the actual key used to persist the object as JSON inside Vault.
使用 |
Properties annotated with |
下一步是声明一个使用域对象的存储库接口。
The next step is to declare a repository interface that uses the domain object.
Credentials
entitiesinterface CredentialsRepository extends CrudRepository<Credentials, String> {
}
由于我们的存储库扩展了 CrudRepository
,它提供基本的 CRUD 和查询方法。Vault 存储库需要 Spring Data 组件。请务必在类路径中包含 spring-data-commons
和 spring-data-keyvalue
工件。
As our repository extends CrudRepository
it provides basic CRUD and query methods.
Vault repositories require Spring Data components.
Make sure to include spring-data-commons
and spring-data-keyvalue
artifacts in your class path.
实现此目的最简单的方法是设置依赖项管理并向 pom.xml
添加工件:
The easiest way to achieve this, is by setting up dependency management and adding the artifacts to your pom.xml
:
然后将以下内容添加到 pom.xml
依赖项部分。
Then add the following to pom.xml
dependencies section.
<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 配置。
The thing we need in between to glue things together is the according Spring configuration.
@Configuration
@EnableVaultRepositories
class ApplicationConfig {
@Bean
VaultTemplate vaultTemplate() {
return new VaultTemplate(…);
}
}
给定上述设置,我们可以继续将 CredentialsRepository
注入我们的组件中。
Given the setup above we can go on and inject CredentialsRepository
into our components.
@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 | Stores properties of Credentials inside Vault Hash with a key pattern keyspace/id , in this case credentials/heisenberg , in the key-value secret secrets engine. |
2 | Uses the provided id to retrieve the object stored at keyspace/id . |
3 | Counts the total number of entities available within the keyspace credentials defined by @Secret on Credentials . |
4 | Removes the key for the given object from 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
,并将这些内容转换为实体,反之亦然。
Vault repositories store objects in Vault using JSON as interchange format.
Object mapping between JSON and the entity is done by VaultConverter
.
The converter reads and writes SecretDocument
that contains the body from a VaultResponse
. VaultResponse`s are read from Vault and the body is deserialized by Jackson into a `Map
of String
and Object
.
The default VaultConverter
implementation reads the Map
with nested values, List
and Map
objects and converts these to entities and vice versa.
鉴于前面章节中的 Credentials
类型,默认映射如下:
Given the Credentials
type from the previous sections the default mapping is as follows:
{
"_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 | The _class attribute is included on root level as well as on any nested interface or abstract types. |
2 | Simple property values are mapped by path. |
3 | Properties of complex types are mapped as nested objects. |
|
The |
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
将删除内容并重新创建整个条目,因此未映射的数据将丢失。
You can customize the mapping behavior by registering a Converter
in VaultCustomConversions
.
Those converters can take care of converting from/to a type such as LocalDate
as well as SecretDocument
whereas the first one is suitable for converting simple properties and the last one complex types to their JSON representation.
The second option offers full control over the resulting SecretDocument
.
Writing objects to Vault
will delete the content and re-create the whole entry, so not mapped data will be lost.
Queries and Query Methods
查询方法允许从方法名称自动推导出简单查询。Vault 没有查询引擎,但需要直接访问 HTTP 上下文路径。Vault 查询方法将 Vault 的 API 可能性转换为查询。查询方法执行在上下文路径下列出子级,对 ID 应用过滤,必要时使用偏移量/限制限制 ID 流,并在获取结果后应用排序。
Query methods allow automatic derivation of simple queries from the method name. Vault has no query engine but requires direct access of HTTP context paths. Vault query methods translate Vault’s API possibilities to queries. A query method execution lists children under a context path, applies filtering to the Id, optionally limits the Id stream with offset/limit and applies sorting after fetching the results.
interface CredentialsRepository extends CrudRepository<Credentials, String> {
List<Credentials> findByIdStartsWith(String prefix);
}
Vault 存储库的查询方法仅支持对 |
Query methods for Vault repositories support only queries with predicates on the |
以下是 Vault 支持的关键字概述。
Here’s an overview of the keywords supported for Vault.
Keyword | Sample |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Sorting and Paging
查询方法通过在内存中选择从 Vault 上下文路径检索的子列表(偏移/限制)ID 来支持排序和分页。与查询方法谓词不同,排序不受特定字段的限制。在 ID 过滤后应用未分页排序,并将所有生成的秘密从 Vault 中提取。通过这种方式,查询方法只能提取作为结果的一部分返回的结果。
Query methods support sorting and paging by selecting in memory a sublist (offset/limit) Id’s retrieved from a Vault context path. Sorting has is not limited to a particular field, unlike query method predicates. Unpaged sorting is applied after Id filtering and all resulting secrets are fetched from Vault. This way a query method fetches only results that are also returned as part of the result.
使用分页和排序要求在过滤 ID 之前提取秘密,这会影响性能。即使 Vault 返回的 ID 的自然顺序发生变化,排序和分页也保证返回相同的结果。因此,首先从 Vault 中提取所有 ID,然后应用排序,最后进行过滤和偏移/限制。
Using paging and sorting requires secret fetching before filtering the Id’s which impacts performance. Sorting and paging guarantees to return the same result even if the natural order of Id returned by Vault changes. Therefore, all Id’s are fetched from Vault first, then sorting is applied and afterwards filtering and offset/limiting.
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,并且秘密不会被更新。
Vaults key/value secrets engine version 2 can maintain versioned secrets.
Spring Vault supports versioning through a version property in the domain model that are annotated with @Version
.
Using optimistic locking makes sure updates are only applied to secrets with a matching version.
Therefore, the actual value of the version property is added to the update request through the cas
property.
If another operation altered the secret in the meantime, then an OptimisticLockingFailureException is thrown and the secret isn’t updated.
版本属性必须是数字属性,例如 int
或 long
,并在更新秘密时映射到 cas
属性。
Version properties must be numeric properties such as int
or long
and map to the cas
property when updating secrets.
@Secret
class VersionedCredentials {
@Id String id;
@Version int version;
String password;
String socialSecurityNumber;
Address address;
}
以下示例展示了这些功能:
The following example shows these features:
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 | Obtain a secret by its Id sample-credentials . |
2 | Obtain a second instance of the secret by its Id sample-credentials . |
3 | Update the secret and let Vault increment the version. |
4 | Update the second instance that uses the previous version.
The operation fails with an OptimisticLockingFailureException as the version was incremented in Vault in the meantime. |
在删除版本化机密时,按 ID 删除将删除最新的机密。按实体删除将删除指定版本处的机密。 |
When deleting versioned secrets, delete by Id deletes the most recent secret. Delete by entity deletes the secret at the provided version. |
Accessing versioned secrets
Key/Value 版本 2 秘钥引擎维护可以通过在您的 Vault 存储库接口声明中实现 RevisionRepository
来访问的秘钥版本。修订存储库定义了查找方法,以获得特定标识符的修订。标识符必须是 String
。
Key/Value version 2 secrets engine maintains versions of secrets that can be accessed by implementing RevisionRepository
in your Vault repository interface declaration.
Revision repositories define lookup methods to obtain revisions for a particular identifier.
Identifiers must be String
.
RevisionRepository
interface RevisionCredentialsRepository extends CrudRepository<Credentials, String>,
RevisionRepository<Credentials, String, Integer> 1
{
}
1 | The first type parameter (Credentials ) denotes the entity type, the second (String ) denotes the type of the id property, and the last one (Integer ) is the type of the revision number. Vault supports only String identifiers and Integer revision numbers. |
Usage
现在,您可以使用 RevisionRepository
中的方法查询实体的修订,如下所示:
You can now use the methods from RevisionRepository
to query the revisions of the entity, as the following example shows:
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));