跳到主要内容
版本:4.1.0

Vault Client 访问

Doubao 中英对照 Vault Client Access

Spring Vault 框架提供的价值或许最好通过下表概述的一系列操作来展示。该表显示了在选择特定客户端选项时,Spring 负责哪些功能:

客户端HTTP 请求认证Vault 操作错误处理
RestClient底层 HTTP 请求访问,可完全控制 URI、请求头和消息转换。你需要手动构造请求并处理响应。VaultEndpoint 可以通过 UriBuilderFactory 配置,从而支持相对路径的使用。没有内置的认证支持;你必须自己管理令牌和请求头。没有直接的 Vault 领域操作;你需要手动为 Vault 端点构造请求。4xx/5xx 状态被转换为 HttpServerErrorException 或 HttpClientErrorException,onStatus 允许你注册自定义处理器
VaultClient基于 RestClient 构建的 Vault 专用流式 HTTP 客户端。提供常见 HTTP 方法和请求构造的方法,以及 VaultEndpoint 支持,通过相对路径将对功能的访问与实际 Vault 服务器配置解耦。用于设置令牌(token(…))和命名空间(namespace(…))的便捷方法;更易于管理认证请求头,并可与 ClientAuthentication 实现一起使用以登录 Vault。基于路径的请求方法;允许通过 ResponseSpec.body() 和 ResponseSpec.toEntity() 方法将 Vault 响应消费为 VaultResponse;直接支持响应包裹。4xx/5xx 状态被转换为 VaultClientResponseException,onStatus 允许你注册自定义处理器
VaultTemplateVault 操作的高级抽象。可以通过客户端回调发起 HTTP 请求。支持处理令牌续期和认证的简单 ClientAuthentication 和 SessionManager 实现。用于多种 Vault 领域操作(读取、写入、删除、列出)的 Java API,支持各种机密后端(KV、Cubbyhole、PKI、Transit 等)。构建在 VaultClient 之上,将 HTTP 错误转换为 Spring Vault 异常(例如 VaultException)。

客户端示例​

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);
VaultClient client = VaultClient.create(VaultEndpoint.create("vault.example.com", 8200));

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

建议​

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

对于大多数使用 Vault 的应用程序代码,VaultClient 达到了正确的平衡:它提供了可读的流式 API,以及类似 RestClient 外观的简单请求/响应处理。其安全默认值(例如端点限定和令牌辅助方法)避免了常见的陷阱。

当你更倾向于在更高级别的抽象层上使用 Vault 时,请选择 VaultTemplate。它减少了样板代码,并与 Spring 的转换和异常转换模式集成。当你的应用程序执行机密 CRUD 操作并且你想要简洁、意图明确的代码时使用它,它为 Vault 提供了 Java API。

VaultClient 和 ReactiveVaultClient​

Spring Vault 为调用 Vault 端点提供了以下选择:

  • VaultClient — 基于 Spring Framework 的 RestClient 的同步客户端,具有流式 API
  • ReactiveVaultClient — 基于 Spring Framework 的 WebClient 的非阻塞响应式客户端,具有流式 API

VaultClient 是一个同步 HTTP 客户端,提供执行请求的流式 API。它的响应式变体 ReactiveVaultClient 为非阻塞访问镜像了 VaultClient 的设计。从今往后,文档展示的是 VaultClient。除了响应式方面,ReactiveVaultClient 的工作方式相同。它作为 HTTP 库之上的抽象,处理 HTTP 请求和响应内容与更高级别 Java 对象之间的转换,构建在 RestClient 之上(其响应式变体则使用 WebClient)。

创建 VaultClient​

VaultClient 具有静态的 create 快捷方法。它还暴露了一个带有更多选项的 builder():

  • 选择要使用的 HTTP 库,请参阅 客户端请求工厂
  • 设置 VaultEndpoint 或 VaultEndpointProvider 以启用相对路径使用,并将请求限制在配置的 Vault 服务器
  • 设置默认请求头
  • 自定义底层 RestClient(对于 ReactiveVaultClient 则是 WebClient)

一旦创建,VaultClient 可以在多个线程中安全使用。

使用 Spring Vault 的配置基础架构时,你可以注册 VaultClientCustomizer 或其响应式变体 ReactiveVaultClientCustomizer,以自定义 Spring Vault 创建的 VaultClient 或 ReactiveVaultClient 实例。

以下示例展示了如何创建或构建一个 VaultClient:

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();

使用 VaultClient​

要执行 Vault 请求,首先指定要使用的 HTTP 方法。使用 get()、post()、delete() 等便捷方法,或 method(HttpMethod)。

请求路径​

接下来,使用 path 方法指定请求路径。路径通常指定为 String,带有可选的 URI 模板变量。以下展示了如何执行请求:

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

字符串 URL 默认进行编码,但可以通过构建带有自定义 uriBuilderFactory 的客户端来更改。URL 也可以通过函数或 java.net.URI 提供,两者都不会被编码。使用 URI 可以访问外部服务器。

请求头和请求体​

如有必要,可以通过使用 header(String, String) 或 token(VaultToken) 等添加请求头来操作 Vault 请求。

请求体本身可以通过 body(Object) 设置,内部使用 HTTP 消息转换。或者,请求体可以使用 ParameterizedTypeReference 设置,允许你使用泛型。

检索响应​

请求设置完成后,可以通过在 retrieve() 之后链式调用方法发送。例如,可以使用 retrieve().body(Class) 或 retrieve().body(ParameterizedTypeReference) 访问响应体(用于列表等参数化类型)。便捷方法 retrieve().body() 使用 Vault 的默认响应类型 VaultResponse 返回响应体。body 方法将响应内容转换为各种类型 — 例如,字节可以转换为 String,JSON 可以使用 Jackson 转换为对象。

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

备注

单独调用 retrieve() 是一个无操作,并返回一个 ResponseSpec。应用程序必须在 ResponseSpec 上调用终端操作才能产生任何副作用。如果消费响应对你的用例没有意义,你可以使用 retrieve().toBodilessEntity()。

此示例展示了如何使用 VaultClient 执行简单的 GET 请求。

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

System.out.println(result); 5
  • 设置一个 GET 请求

  • 在 Vault 服务器上指定路径

  • 检索响应

  • 将响应转换为 VaultResponse

  • 打印结果

通过 ResponseEntity 提供对响应状态码和请求头的访问:

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
  • 为指定的 URL 设置一个 GET 请求

  • 将响应转换为 ResponseEntity

  • 打印结果

错误处理​

默认情况下,当检索到具有 4xx 或 5xx 状态码的响应时,VaultClient 会抛出 VaultClientResponseException 的子类。此行为可以使用 onStatus 覆盖。

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);
  • 为返回 404 状态码的 URL 创建一个 GET 请求

  • 为所有 4xx 状态码设置状态处理器

  • 抛出一个自定义异常