该版本仍在开发中,尚未被视为稳定版本。如需获取最新的稳定版本,请使用Spring Vault 4.0.1spring-doc.cadn.net.cn

Vault 客户端访问

Spring Vault 框架提供的价值或许可以通过下表中概述的操作序列最好地展示出来。 该表显示了在选择特定客户端选项时 Spring 所负责的功能:spring-doc.cadn.net.cn

客户端 HTTP 请求 身份验证 Vault 操作 错误处理

REST 客户端spring-doc.cadn.net.cn

对 URI、头部和消息转换具有完全控制权的低级 HTTP 请求访问。您需要手动构造请求并处理响应。VaultEndpoint 可以通过 UriBuilderFactory 进行配置,从而启用相对路径的使用。spring-doc.cadn.net.cn

没有内置的身份验证支持;您必须自己管理Tokens和标头。spring-doc.cadn.net.cn

没有直接的 Vault 域操作;您需要手动构建对 Vault 端点的请求。spring-doc.cadn.net.cn

4xx/5xx 状态转换为 HttpServerErrorException 各自的 HttpClientErrorException, onStatus 允许您注册自定义处理器spring-doc.cadn.net.cn

VaultClientspring-doc.cadn.net.cn

基于 RestClient 构建的特定于 Vault 的流式 HTTP 客户端。提供常用 HTTP 动词和请求构造的方法,同时支持 VaultEndpoint,以通过相对路径解耦对功能的访问与实际的 Vault 服务器配置。spring-doc.cadn.net.cn

设置Tokens (token(…)) 和命名空间 (namespace(…)) 的便捷方法;更易于管理身份验证头,并且可以与 ClientAuthentication 实现一起使用以登录到 Vault。spring-doc.cadn.net.cn

基于路径的请求方法;允许通过ResponseSpec.body()ResponseSpec.toEntity()方法将Vault响应作为VaultResponse使用;并直接支持响应包装。spring-doc.cadn.net.cn

4xx/5xx 状态转换为 VaultClientResponseException, onStatus 允许您注册自定义处理器spring-doc.cadn.net.cn

VaultTemplatespring-doc.cadn.net.cn

Vault操作的高级抽象。可以通过客户端回调发出HTTP请求。spring-doc.cadn.net.cn

对简单的 ClientAuthenticationSessionManager 实现提供了支持,这些实现能够处理Tokens续订和身份验证。spring-doc.cadn.net.cn

支持多种机密后端(KV、Cubbyhole、PKI、Transit 等)的 Vault 域操作(读取、写入、删除、列出)的 Java API。spring-doc.cadn.net.cn

基于 VaultClient 构建,将 HTTP 错误转换为 Spring Vault 异常(例如,VaultException)。spring-doc.cadn.net.cn

客户端示例

RestClient
RestClient client = RestClient.create();

VaultResponse response = client.get()
	.uri("https://vault.example.com/v1/secret/my-secret")
	.header("X-Vault-Token", "…")
	.header("X-Vault-Namespace", "…")
	.retrieve()
	.body(VaultResponse.class);
val client = RestClient.create()

val response = client.get()
	.uri("https://vault.example.com/v1/secret/my-secret")
	.header("X-Vault-Token", "…")
	.header("X-Vault-Namespace", "…")
	.retrieve()
	.body<VaultResponse>()
VaultClient
VaultClient client = VaultClient.create(VaultEndpoint.create("vault.example.com", 8200));

VaultResponse response = client.get()
	.path("secret/my-secret")
	.token(VaultToken.of(…))
	.namespace(…)
	.retrieve()
	.requiredBody();
val client = VaultClient.create(VaultEndpoint.create("vault.example.com", 8200))

val response = client.get()
	.path("secret/my-secret")
	.token(VaultToken.of(…))
	.namespace(…)
	.retrieve()
	.requiredBody()
VaultTemplate
VaultTemplate vaultOperations = new VaultTemplate(vaultClient, sessionManager);
VaultResponse response = vaultOperations.read("secret/my-secret");
// or
Object value = vaultOperations.readRequired("secret/my-secret").getRequiredData().get("value");
val vaultOperations = VaultTemplate(vaultClient, sessionManager)
val response = vaultOperations.read("secret/my-secret")
// or
val value = vaultOperations.readRequired("secret/my-secret").getRequiredData()["value"]

推荐

如果需要最大的灵活性以及对 HTTP 客户端的完全控制(连接池、JVM 级别的自定义 TLS 密钥库、用于诊断的线路日志),请从正确配置的 RestClient 或请求工厂开始。 这是必须适应多种环境的框架级代码或库的正确选择。spring-doc.cadn.net.cn

对于大多数使用 Vault 的应用程序代码来说,VaultClient 实现了良好的平衡:它提供了一个可读性强、流畅的 API,同时具备类似于 RestClient 的外观和操作体验。其安全的默认设置(如端点范围限定和Tokens助手)避免了常见的陷阱。spring-doc.cadn.net.cn

选择 VaultTemplate 当你更喜欢在更高的抽象层次上使用 Vault 时。 它减少了样板代码,并与 Spring 的转换和异常转换模式集成。 当你的应用程序执行密钥的 CRUD 操作时使用它, 你希望获得简洁、表达意图的代码,这些代码为 Vault 提供了 Java API。spring-doc.cadn.net.cn

VaultClientReactiveVaultClient

Spring Vault 提供了以下几种调用 Vault 端点的选择:spring-doc.cadn.net.cn

VaultClient 是一个同步的 HTTP 客户端,提供了用于执行请求的流畅 API。 它的响应式变体 ReactiveVaultClient 镜像了 VaultClient 的设计以实现非阻塞访问。 在后续内容中,文档展示了 VaultClient。除了响应式特性外,ReactiveVaultClient 的工作方式相同。 它作为 HTTP 库的抽象层,负责将 HTTP 请求和响应内容与基于 RestClient 的高级 Java 对象进行转换,对于其响应式变体则是基于 WebClientspring-doc.cadn.net.cn

创建一个VaultClient

VaultClient 提供了静态的 create 快捷方法。 它还公开了一个带有更多选项的 builder()spring-doc.cadn.net.cn

一旦创建,VaultClient 可安全地在多个线程中使用。spring-doc.cadn.net.cn

您可以注册 VaultClientCustomizer 或其响应式变体 ReactiveVaultClientCustomizer,当使用 Spring Vault 的配置基础设施来自定义 Spring Vault 创建的 VaultClientReactiveVaultClient 实例时。spring-doc.cadn.net.cn

以下示例展示了如何创建或构建 VaultClientspring-doc.cadn.net.cn

VaultClient defaultClient = VaultClient.create();

VaultClient customClient = VaultClient.builder()
	.endpoint(VaultEndpoint.create("vault.acme.com", 8200))
	.requestFactory(new HttpComponentsClientHttpRequestFactory())
	.defaultHeader("My-Header", "Foo")
	.configureRestClient(builder -> … )
	.build();
val defaultClient = VaultClient.create()

val customClient = VaultClient.builder()
	.endpoint(VaultEndpoint.create("vault.acme.com", 8200))
	.requestFactory(HttpComponentsClientHttpRequestFactory())
	.defaultHeader("My-Header", "Foo")
	.configureRestClient{ … }
	.build()

使用VaultClient

要执行 Vault 请求,首先指定要使用的 HTTP 方法。 使用诸如 get()post()delete() 和其他便捷方法,或者 method(HttpMethod)spring-doc.cadn.net.cn

请求路径

接下来,使用 path 方法指定请求路径。路径通常指定为 String,并包含可选的 URI 模板变量。以下展示了如何执行请求:spring-doc.cadn.net.cn

int id = 42;
vaultClient.get()
	.path("secret/{id}", id)
	// ...
val id = 42
vaultClient.get()
	.path("secret/{id}", id)
	// ...

字符串URL默认是编码的,但可以通过使用自定义的uriBuilderFactory来构建客户端进行更改。 URL也可以通过函数提供,或者作为java.net.URI提供,这两种方式都不会进行编码。使用URI可以访问外部服务器。spring-doc.cadn.net.cn

请求头和请求体

如果需要,可以通过添加请求头来操纵 Vault 请求,例如使用 header(String, String)token(VaultToken) 等等。spring-doc.cadn.net.cn

请求体本身可以通过body(Object)来设置,其内部使用HTTP消息转换。 或者,可以使用ParameterizedTypeReference来设置请求体,从而允许你使用泛型。spring-doc.cadn.net.cn

正在检索响应

一旦请求设置完成,可以通过在retrieve()之后链式调用方法来发送请求。 例如,可以使用retrieve().body(Class)retrieve().body(ParameterizedTypeReference)来访问响应体,特别是对于像列表这样的参数化类型。便捷方法retrieve().body()会使用Vault的默认响应类型VaultResponse返回响应体。 body方法可以将响应内容转换为各种类型——例如,字节可以转换为String,JSON可以使用Jackson转换为对象。spring-doc.cadn.net.cn

响应也可以转换为 ResponseEntity,通过 retrieve().toEntity(Class) 可以同时访问响应头和响应体。spring-doc.cadn.net.cn

单独调用 retrieve() 是一个空操作(no-op),并返回一个 ResponseSpec。 应用程序必须在 ResponseSpec 上调用一个终止操作(terminal operation)才能产生任何副作用。 如果你的使用场景对消费响应内容不感兴趣,可以使用 retrieve().toBodilessEntity()

此示例展示了如何使用 VaultClient 执行一个简单的 GET 请求。spring-doc.cadn.net.cn

VaultResponse result = vaultClient.get() (1)
	.path("secret/my-secret")            (2)
	.retrieve()                          (3)
	.requiredBody();                     (4)

System.out.println(result);              (5)
1 设置一个 GET 请求
2 指定Vault服务器上的路径
3 获取响应
4 将响应转换为 VaultResponse
5 打印结果
val result = vaultClient.get()   (1)
	.path("secret/my-secret")   (2)
	.retrieve()                 (3)
	.requiredBody()             (4)

println(result)                 (5)
1 设置一个 GET 请求
2 指定Vault服务器上的路径
3 获取响应
4 将响应转换为 VaultResponse
5 打印结果

通过 ResponseEntity 提供对响应状态码和响应头的访问:spring-doc.cadn.net.cn

ResponseEntity<VaultResponse> result = vaultClient.get() (1)
	.path("secret/my-secret") (1)
	.retrieve()
	.toEntity(); (2)

System.out.println("Response status: " + result.getStatusCode()); (3)
System.out.println("Response headers: " + result.getHeaders()); (3)
System.out.println("Contents: " + result.getBody()); (3)
1 为指定的 URL 设置一个 GET 请求
2 将响应转换为 ResponseEntity
3 打印结果
val result = vaultClient.get() (1)
	.path("secret/my-secret") (1)
	.retrieve()
	.toEntity<String>() (2)

println("Response status: " + result.statusCode) (3)
println("Response headers: " + result.headers) (3)
println("Contents: " + result.body) (3)
1 为指定的 URL 设置一个 GET 请求
2 将响应转换为 ResponseEntity
3 打印结果

错误处理

默认情况下,当接收到状态码为 4xx 或 5xx 的响应时,VaultClient 会抛出 VaultClientResponseException 的某个子类异常。 可以使用 onStatus 方法来覆盖此行为。spring-doc.cadn.net.cn

String result = vaultClient.get()
	.path("secret/this-path-does-not-exist") (1)
	.retrieve()
	.onStatus(HttpStatusCode::is4xxClientError, (request, response) -> { (2)
		throw new MyCustomRuntimeException(response.getStatusCode(), response.getHeaders()); (3)
	})
	.body(String.class);
1 创建一个向返回 404 状态码的 URL 发起的 GET 请求
2 为所有 4xx 状态码设置一个状态处理器
3 抛出一个自定义异常
val result = vaultClient.get()
	.path("secret/this-path-does-not-exist") (1)
	.retrieve()
	.onStatus(HttpStatusCode::is4xxClientError) { _, response -> (2)
		throw MyCustomRuntimeException(response.getStatusCode(), response.getHeaders()) } (3)
	.body<String>()
1 创建一个向返回 404 状态码的 URL 发起的 GET 请求
2 为所有 4xx 状态码设置一个状态处理器
3 抛出一个自定义异常